dosbox-wii/src/cpu/core_dynrec/decoder.h
2021-02-06 16:06:31 +01:00

618 lines
18 KiB
C

/*
* Copyright (C) 2002-2019 The DOSBox Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "decoder_basic.h"
#include "operators.h"
#include "decoder_opcodes.h"
#include "dyn_fpu.h"
/*
The function CreateCacheBlock translates the instruction stream
until either an unhandled instruction is found, the maximum
number of translated instructions is reached or some critical
instruction is encountered.
*/
static CacheBlockDynRec * CreateCacheBlock(CodePageHandlerDynRec * codepage,PhysPt start,Bitu max_opcodes) {
// initialize a load of variables
decode.code_start=start;
decode.code=start;
decode.page.code=codepage;
decode.page.index=start&4095;
decode.page.wmap=codepage->write_map;
decode.page.invmap=codepage->invalidation_map;
decode.page.first=start >> 12;
decode.active_block=decode.block=cache_openblock();
decode.block->page.start=(Bit16u)decode.page.index;
codepage->AddCacheBlock(decode.block);
InitFlagsOptimization();
// every codeblock that is run sets cache.block.running to itself
// so the block linking knows the last executed block
gen_mov_direct_ptr(&cache.block.running,(DRC_PTR_SIZE_IM)decode.block);
// start with the cycles check
gen_mov_word_to_reg(FC_RETOP,&CPU_Cycles,true);
save_info_dynrec[used_save_info_dynrec].branch_pos=gen_create_branch_long_leqzero(FC_RETOP);
save_info_dynrec[used_save_info_dynrec].type=cycle_check;
used_save_info_dynrec++;
decode.cycles=0;
while (max_opcodes--) {
// Init prefixes
decode.big_addr=cpu.code.big;
decode.big_op=cpu.code.big;
decode.seg_prefix=0;
decode.seg_prefix_used=false;
decode.rep=REP_NONE;
decode.cycles++;
decode.op_start=decode.code;
restart_prefix:
Bitu opcode;
if (!decode.page.invmap) opcode=decode_fetchb();
else {
// some entries in the invalidation map, see if the next
// instruction is known to be modified a lot
if (decode.page.index<4096) {
if (GCC_UNLIKELY(decode.page.invmap[decode.page.index]>=4)) goto illegalopcode;
opcode=decode_fetchb();
} else {
// switch to the next page
opcode=decode_fetchb();
if (GCC_UNLIKELY(decode.page.invmap &&
(decode.page.invmap[decode.page.index-1]>=4))) goto illegalopcode;
}
}
switch (opcode) {
// instructions 'op reg8,reg8' and 'op [],reg8'
case 0x00:dyn_dop_ebgb(DOP_ADD);break;
case 0x08:dyn_dop_ebgb(DOP_OR);break;
case 0x10:dyn_dop_ebgb(DOP_ADC);break;
case 0x18:dyn_dop_ebgb(DOP_SBB);break;
case 0x20:dyn_dop_ebgb(DOP_AND);break;
case 0x28:dyn_dop_ebgb(DOP_SUB);break;
case 0x30:dyn_dop_ebgb(DOP_XOR);break;
case 0x38:dyn_dop_ebgb(DOP_CMP);break;
// instructions 'op reg8,reg8' and 'op reg8,[]'
case 0x02:dyn_dop_gbeb(DOP_ADD);break;
case 0x0a:dyn_dop_gbeb(DOP_OR);break;
case 0x12:dyn_dop_gbeb(DOP_ADC);break;
case 0x1a:dyn_dop_gbeb(DOP_SBB);break;
case 0x22:dyn_dop_gbeb(DOP_AND);break;
case 0x2a:dyn_dop_gbeb(DOP_SUB);break;
case 0x32:dyn_dop_gbeb(DOP_XOR);break;
case 0x3a:dyn_dop_gbeb(DOP_CMP);break;
// instructions 'op reg16/32,reg16/32' and 'op [],reg16/32'
case 0x01:dyn_dop_evgv(DOP_ADD);break;
case 0x09:dyn_dop_evgv(DOP_OR);break;
case 0x11:dyn_dop_evgv(DOP_ADC);break;
case 0x19:dyn_dop_evgv(DOP_SBB);break;
case 0x21:dyn_dop_evgv(DOP_AND);break;
case 0x29:dyn_dop_evgv(DOP_SUB);break;
case 0x31:dyn_dop_evgv(DOP_XOR);break;
case 0x39:dyn_dop_evgv(DOP_CMP);break;
// instructions 'op reg16/32,reg16/32' and 'op reg16/32,[]'
case 0x03:dyn_dop_gvev(DOP_ADD);break;
case 0x0b:dyn_dop_gvev(DOP_OR);break;
case 0x13:dyn_dop_gvev(DOP_ADC);break;
case 0x1b:dyn_dop_gvev(DOP_SBB);break;
case 0x23:dyn_dop_gvev(DOP_AND);break;
case 0x2b:dyn_dop_gvev(DOP_SUB);break;
case 0x33:dyn_dop_gvev(DOP_XOR);break;
case 0x3b:dyn_dop_gvev(DOP_CMP);break;
// instructions 'op reg8,imm8'
case 0x04:dyn_dop_byte_imm(DOP_ADD,DRC_REG_EAX,0);break;
case 0x0c:dyn_dop_byte_imm(DOP_OR,DRC_REG_EAX,0);break;
case 0x14:dyn_dop_byte_imm(DOP_ADC,DRC_REG_EAX,0);break;
case 0x1c:dyn_dop_byte_imm(DOP_SBB,DRC_REG_EAX,0);break;
case 0x24:dyn_dop_byte_imm(DOP_AND,DRC_REG_EAX,0);break;
case 0x2c:dyn_dop_byte_imm(DOP_SUB,DRC_REG_EAX,0);break;
case 0x34:dyn_dop_byte_imm(DOP_XOR,DRC_REG_EAX,0);break;
case 0x3c:dyn_dop_byte_imm(DOP_CMP,DRC_REG_EAX,0);break;
// instructions 'op reg16/32,imm16/32'
case 0x05:dyn_dop_word_imm(DOP_ADD,DRC_REG_EAX);break;
case 0x0d:dyn_dop_word_imm_old(DOP_OR,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
case 0x15:dyn_dop_word_imm_old(DOP_ADC,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
case 0x1d:dyn_dop_word_imm_old(DOP_SBB,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
case 0x25:dyn_dop_word_imm(DOP_AND,DRC_REG_EAX);break;
case 0x2d:dyn_dop_word_imm_old(DOP_SUB,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
case 0x35:dyn_dop_word_imm_old(DOP_XOR,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
case 0x3d:dyn_dop_word_imm_old(DOP_CMP,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
// push segment register onto stack
case 0x06:dyn_push_seg(DRC_SEG_ES);break;
case 0x0e:dyn_push_seg(DRC_SEG_CS);break;
case 0x16:dyn_push_seg(DRC_SEG_SS);break;
case 0x1e:dyn_push_seg(DRC_SEG_DS);break;
// pop segment register from stack
case 0x07:dyn_pop_seg(DRC_SEG_ES);break;
case 0x17:dyn_pop_seg(DRC_SEG_SS);break;
case 0x1f:dyn_pop_seg(DRC_SEG_DS);break;
// segment prefixes
case 0x26:dyn_segprefix(DRC_SEG_ES);goto restart_prefix;
case 0x2e:dyn_segprefix(DRC_SEG_CS);goto restart_prefix;
case 0x36:dyn_segprefix(DRC_SEG_SS);goto restart_prefix;
case 0x3e:dyn_segprefix(DRC_SEG_DS);goto restart_prefix;
case 0x64:dyn_segprefix(DRC_SEG_FS);goto restart_prefix;
case 0x65:dyn_segprefix(DRC_SEG_GS);goto restart_prefix;
// case 0x27: DAA missing
// case 0x2f: DAS missing
// case 0x37: AAA missing
// case 0x3f: AAS missing
// dual opcodes
case 0x0f:
{
Bitu dual_code=decode_fetchb();
switch (dual_code) {
case 0x00:
if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
dyn_grp6();
break;
case 0x01:
if (dyn_grp7()) goto finish_block;
break;
/* case 0x02:
if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
dyn_larlsl(true);
break;
case 0x03:
if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
dyn_larlsl(false);
break; */
case 0x20:dyn_mov_from_crx();break;
case 0x22:dyn_mov_to_crx();goto finish_block;
// short conditional jumps
case 0x80:case 0x81:case 0x82:case 0x83:case 0x84:case 0x85:case 0x86:case 0x87:
case 0x88:case 0x89:case 0x8a:case 0x8b:case 0x8c:case 0x8d:case 0x8e:case 0x8f:
dyn_branched_exit((BranchTypes)(dual_code&0xf),
decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw());
goto finish_block;
// conditional byte set instructions
/* case 0x90:case 0x91:case 0x92:case 0x93:case 0x94:case 0x95:case 0x96:case 0x97:
case 0x98:case 0x99:case 0x9a:case 0x9b:case 0x9c:case 0x9d:case 0x9e:case 0x9f:
dyn_set_byte_on_condition((BranchTypes)(dual_code&0xf));
AcquireFlags(FMASK_TEST);
break; */
// push/pop segment registers
case 0xa0:dyn_push_seg(DRC_SEG_FS);break;
case 0xa1:dyn_pop_seg(DRC_SEG_FS);break;
case 0xa8:dyn_push_seg(DRC_SEG_GS);break;
case 0xa9:dyn_pop_seg(DRC_SEG_GS);break;
// double shift instructions
case 0xa4:dyn_dshift_ev_gv(true,true);break;
case 0xa5:dyn_dshift_ev_gv(true,false);break;
case 0xac:dyn_dshift_ev_gv(false,true);break;
case 0xad:dyn_dshift_ev_gv(false,false);break;
case 0xaf:dyn_imul_gvev(0);break;
// lfs
case 0xb4:
dyn_get_modrm();
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
dyn_load_seg_off_ea(DRC_SEG_FS);
break;
// lgs
case 0xb5:
dyn_get_modrm();
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
dyn_load_seg_off_ea(DRC_SEG_GS);
break;
// zero-extending moves
case 0xb6:dyn_movx_ev_gb(false);break;
case 0xb7:dyn_movx_ev_gw(false);break;
// sign-extending moves
case 0xbe:dyn_movx_ev_gb(true);break;
case 0xbf:dyn_movx_ev_gw(true);break;
default:
#if DYN_LOG
// LOG_MSG("Unhandled dual opcode 0F%02X",dual_code);
#endif
goto illegalopcode;
}
break;
}
// 'inc/dec reg16/32'
case 0x40:case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:case 0x47:
dyn_sop_word(SOP_INC,opcode&7);
break;
case 0x48:case 0x49:case 0x4a:case 0x4b:case 0x4c:case 0x4d:case 0x4e:case 0x4f:
dyn_sop_word(SOP_DEC,opcode&7);
break;
// 'push/pop reg16/32'
case 0x50:case 0x51:case 0x52:case 0x53:case 0x54:case 0x55:case 0x56:case 0x57:
dyn_push_reg(opcode&7);
break;
case 0x58:case 0x59:case 0x5a:case 0x5b:case 0x5c:case 0x5d:case 0x5e:case 0x5f:
dyn_pop_reg(opcode&7);
break;
case 0x60:
if (decode.big_op) gen_call_function_raw((void *)&dynrec_pusha_dword);
else gen_call_function_raw((void *)&dynrec_pusha_word);
break;
case 0x61:
if (decode.big_op) gen_call_function_raw((void *)&dynrec_popa_dword);
else gen_call_function_raw((void *)&dynrec_popa_word);
break;
// case 0x62: BOUND missing
// case 0x61: ARPL missing
case 0x66:decode.big_op=!cpu.code.big;goto restart_prefix;
case 0x67:decode.big_addr=!cpu.code.big;goto restart_prefix;
// 'push imm8/16/32'
case 0x68:
dyn_push_word_imm(decode.big_op ? decode_fetchd() : decode_fetchw());
break;
case 0x6a:
dyn_push_byte_imm((Bit8s)decode_fetchb());
break;
// signed multiplication
case 0x69:dyn_imul_gvev(decode.big_op ? 4 : 2);break;
case 0x6b:dyn_imul_gvev(1);break;
// case 0x6c to 0x6f missing (ins/outs)
// short conditional jumps
case 0x70:case 0x71:case 0x72:case 0x73:case 0x74:case 0x75:case 0x76:case 0x77:
case 0x78:case 0x79:case 0x7a:case 0x7b:case 0x7c:case 0x7d:case 0x7e:case 0x7f:
dyn_branched_exit((BranchTypes)(opcode&0xf),(Bit8s)decode_fetchb());
goto finish_block;
// 'op []/reg8,imm8'
case 0x80:
case 0x82:dyn_grp1_eb_ib();break;
// 'op []/reg16/32,imm16/32'
case 0x81:dyn_grp1_ev_iv(false);break;
case 0x83:dyn_grp1_ev_iv(true);break;
// 'test []/reg8/16/32,reg8/16/32'
case 0x84:dyn_dop_gbeb(DOP_TEST);break;
case 0x85:dyn_dop_gvev(DOP_TEST);break;
// 'xchg reg8/16/32,[]/reg8/16/32'
case 0x86:dyn_dop_ebgb_xchg();break;
case 0x87:dyn_dop_evgv_xchg();break;
// 'mov []/reg8/16/32,reg8/16/32'
case 0x88:dyn_dop_ebgb_mov();break;
case 0x89:dyn_dop_evgv_mov();break;
// 'mov reg8/16/32,[]/reg8/16/32'
case 0x8a:dyn_dop_gbeb_mov();break;
case 0x8b:dyn_dop_gvev_mov();break;
// move segment register into memory or a 16bit register
case 0x8c:dyn_mov_ev_seg();break;
// load effective address
case 0x8d:dyn_lea();break;
// move a value from memory or a 16bit register into a segment register
case 0x8e:dyn_mov_seg_ev();break;
// 'pop []'
case 0x8f:dyn_pop_ev();break;
case 0x90: // nop
case 0x9b: // wait
case 0xf0: // lock
break;
case 0x91:case 0x92:case 0x93:case 0x94:case 0x95:case 0x96:case 0x97:
dyn_xchg_ax(opcode&7);
break;
// sign-extend al into ax/sign-extend ax into eax
case 0x98:dyn_cbw();break;
// sign-extend ax into dx:ax/sign-extend eax into edx:eax
case 0x99:dyn_cwd();break;
case 0x9a:dyn_call_far_imm();goto finish_block;
case 0x9c: // pushf
AcquireFlags(FMASK_TEST);
gen_call_function_I((void *)&CPU_PUSHF,decode.big_op);
dyn_check_exception(FC_RETOP);
break;
case 0x9d: // popf
gen_call_function_I((void *)&CPU_POPF,decode.big_op);
dyn_check_exception(FC_RETOP);
InvalidateFlags();
break;
case 0x9e:dyn_sahf();break;
// case 0x9f: LAHF missing
// 'mov al/ax,[]'
case 0xa0:
dyn_mov_byte_al_direct(decode.big_addr ? decode_fetchd() : decode_fetchw());
break;
case 0xa1:
dyn_mov_byte_ax_direct(decode.big_addr ? decode_fetchd() : decode_fetchw());
break;
// 'mov [],al/ax'
case 0xa2:
dyn_mov_byte_direct_al();
break;
case 0xa3:
dyn_mov_byte_direct_ax(decode.big_addr ? decode_fetchd() : decode_fetchw());
break;
// case 0xa6 to 0xaf string operations, some missing
// movsb/w/d
case 0xa4:dyn_string(STR_MOVSB);break;
case 0xa5:dyn_string(decode.big_op ? STR_MOVSD : STR_MOVSW);break;
// stosb/w/d
case 0xaa:dyn_string(STR_STOSB);break;
case 0xab:dyn_string(decode.big_op ? STR_STOSD : STR_STOSW);break;
// lodsb/w/d
case 0xac:dyn_string(STR_LODSB);break;
case 0xad:dyn_string(decode.big_op ? STR_LODSD : STR_LODSW);break;
// 'test reg8/16/32,imm8/16/32'
case 0xa8:dyn_dop_byte_imm(DOP_TEST,DRC_REG_EAX,0);break;
case 0xa9:dyn_dop_word_imm_old(DOP_TEST,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
// 'mov reg8/16/32,imm8/16/32'
case 0xb0:case 0xb1:case 0xb2:case 0xb3:case 0xb4:case 0xb5:case 0xb6:case 0xb7:
dyn_mov_byte_imm(opcode&3,(opcode>>2)&1,decode_fetchb());
break;
case 0xb8:case 0xb9:case 0xba:case 0xbb:case 0xbc:case 0xbd:case 0xbe:case 0xbf:
dyn_mov_word_imm(opcode&7);break;
break;
// 'shiftop []/reg8,imm8/1/cl'
case 0xc0:dyn_grp2_eb(grp2_imm);break;
case 0xd0:dyn_grp2_eb(grp2_1);break;
case 0xd2:dyn_grp2_eb(grp2_cl);break;
// 'shiftop []/reg16/32,imm8/1/cl'
case 0xc1:dyn_grp2_ev(grp2_imm);break;
case 0xd1:dyn_grp2_ev(grp2_1);break;
case 0xd3:dyn_grp2_ev(grp2_cl);break;
// retn [param]
case 0xc2:dyn_ret_near(decode_fetchw());goto finish_block;
case 0xc3:dyn_ret_near(0);goto finish_block;
// les
case 0xc4:
dyn_get_modrm();
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
dyn_load_seg_off_ea(DRC_SEG_ES);
break;
// lds
case 0xc5:
dyn_get_modrm();
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
dyn_load_seg_off_ea(DRC_SEG_DS);break;
// 'mov []/reg8/16/32,imm8/16/32'
case 0xc6:dyn_dop_ebib_mov();break;
case 0xc7:dyn_dop_eviv_mov();break;
case 0xc8:dyn_enter();break;
case 0xc9:dyn_leave();break;
// retf [param]
case 0xca:dyn_ret_far(decode_fetchw());goto finish_block;
case 0xcb:dyn_ret_far(0);goto finish_block;
// int/iret
#if !(C_DEBUG)
case 0xcd:dyn_interrupt(decode_fetchb());goto finish_block;
#endif
case 0xcf:dyn_iret();goto finish_block;
// case 0xd4: AAM missing
// case 0xd5: AAD missing
// case 0xd6: SALC missing
// case 0xd7: XLAT missing
#ifdef CPU_FPU
// floating point instructions
case 0xd8:
dyn_fpu_esc0();
break;
case 0xd9:
dyn_fpu_esc1();
break;
case 0xda:
dyn_fpu_esc2();
break;
case 0xdb:
dyn_fpu_esc3();
break;
case 0xdc:
dyn_fpu_esc4();
break;
case 0xdd:
dyn_fpu_esc5();
break;
case 0xde:
dyn_fpu_esc6();
break;
case 0xdf:
dyn_fpu_esc7();
break;
#endif
// loop instructions
case 0xe0:dyn_loop(LOOP_NE);goto finish_block;
case 0xe1:dyn_loop(LOOP_E);goto finish_block;
case 0xe2:dyn_loop(LOOP_NONE);goto finish_block;
case 0xe3:dyn_loop(LOOP_JCXZ);goto finish_block;
// 'in al/ax/eax,port_imm'
case 0xe4:dyn_read_port_byte_direct(decode_fetchb());break;
case 0xe5:dyn_read_port_word_direct(decode_fetchb());break;
// 'out port_imm,al/ax/eax'
case 0xe6:dyn_write_port_byte_direct(decode_fetchb());break;
case 0xe7:dyn_write_port_word_direct(decode_fetchb());break;
// 'in al/ax/eax,port_dx'
case 0xec:dyn_read_port_byte();break;
case 0xed:dyn_read_port_word();break;
// 'out port_dx,al/ax/eax'
case 0xee:dyn_write_port_byte();break;
case 0xef:dyn_write_port_word();break;
// 'call near imm16/32'
case 0xe8:
dyn_call_near_imm();
goto finish_block;
// 'jmp near imm16/32'
case 0xe9:
dyn_exit_link(decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw());
goto finish_block;
// 'jmp far'
case 0xea:
dyn_jmp_far_imm();
goto finish_block;
// 'jmp short imm8'
case 0xeb:
dyn_exit_link((Bit8s)decode_fetchb());
goto finish_block;
// repeat prefixes
case 0xf2:
decode.rep=REP_NZ;
goto restart_prefix;
case 0xf3:
decode.rep=REP_Z;
goto restart_prefix;
case 0xf5: //CMC
gen_call_function_raw((void*)dynrec_cmc);
break;
case 0xf8: //CLC
gen_call_function_raw((void*)dynrec_clc);
break;
case 0xf9: //STC
gen_call_function_raw((void*)dynrec_stc);
break;
case 0xf6:dyn_grp3_eb();break;
case 0xf7:dyn_grp3_ev();break;
case 0xfa: //CLI
gen_call_function_raw((void *)&CPU_CLI);
dyn_check_exception(FC_RETOP);
break;
case 0xfb: //STI
gen_call_function_raw((void *)&CPU_STI);
dyn_check_exception(FC_RETOP);
if (max_opcodes<=0) max_opcodes=1; //Allow 1 extra opcode
break;
case 0xfc: //CLD
gen_call_function_raw((void*)dynrec_cld);
break;
case 0xfd: //STD
gen_call_function_raw((void*)dynrec_std);
break;
case 0xfe:
if (dyn_grp4_eb()) goto finish_block;
break;
case 0xff:
switch (dyn_grp4_ev()) {
case 0:
break;
case 1:
goto core_close_block;
case 2:
goto illegalopcode;
default:
break;
}
break;
default:
#if DYN_LOG
// LOG_MSG("Dynrec unhandled opcode %X",opcode);
#endif
goto illegalopcode;
}
}
// link to next block because the maximum number of opcodes has been reached
dyn_set_eip_end();
dyn_reduce_cycles();
gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
dyn_closeblock();
goto finish_block;
core_close_block:
dyn_reduce_cycles();
dyn_return(BR_Normal);
dyn_closeblock();
goto finish_block;
illegalopcode:
// some unhandled opcode has been encountered
dyn_set_eip_last();
dyn_reduce_cycles();
dyn_return(BR_Opcode); // tell the core what happened
dyn_closeblock();
goto finish_block;
finish_block:
// setup the correct end-address
decode.page.index--;
decode.active_block->page.end=(Bit16u)decode.page.index;
// LOG_MSG("Created block size %d start %d end %d",decode.block->cache.size,decode.block->page.start,decode.block->page.end);
return decode.block;
}