diff --git a/src/cpu/Makefile.am b/src/cpu/Makefile.am index 2274f9a4..261369fb 100644 --- a/src/cpu/Makefile.am +++ b/src/cpu/Makefile.am @@ -1,6 +1,7 @@ -SUBDIRS = core_full core_normal +SUBDIRS = core_full core_normal core_dyn_x86 AM_CPPFLAGS = -I$(top_srcdir)/include noinst_LIBRARIES = libcpu.a libcpu_a_SOURCES = callback.cpp cpu.cpp flags.cpp modrm.cpp modrm.h core_full.cpp instructions.h \ - paging.cpp lazyflags.h core_normal.cpp \ No newline at end of file + paging.cpp lazyflags.h core_normal.cpp \ + core_dyn_x86.cpp \ No newline at end of file diff --git a/src/cpu/core_dyn_x86.cpp b/src/cpu/core_dyn_x86.cpp new file mode 100644 index 00000000..2d551f0a --- /dev/null +++ b/src/cpu/core_dyn_x86.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2002-2003 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" + +#if (C_DYNAMIC_X86) + +#include +#include +#include +#include + +#include "callback.h" +#include "regs.h" +#include "mem.h" +#include "cpu.h" +#include "debug.h" +#include "paging.h" +#include "inout.h" + +#define CACHE_TOTAL (1024*1024*2) +#define CACHE_MAXSIZE (4096) +#define CACHE_BLOCKS (50*1024) +#define CACHE_ALIGN (16) +#define DYN_HASH_SHIFT (4) +#define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT) +#define DYN_LINKS (16) + +#if 1 +#define DYN_LOG LOG_MSG +#else +#define DYN_LOG +#endif + +enum { + G_EAX,G_ECX,G_EDX,G_EBX, + G_ESP,G_EBP,G_ESI,G_EDI, + G_ES,G_CS,G_SS,G_DS,G_FS,G_GS, + G_FLAGS,G_SMASK,G_EIP, + G_EA,G_STACK,G_CYCLES, + G_TMPB,G_TMPW,G_SHIFT, + G_EXIT, + G_MAX, +}; + +enum SingleOps { + SOP_INC,SOP_DEC, + SOP_NOT,SOP_NEG, +}; + +enum DualOps { + DOP_ADD,DOP_ADC, + DOP_SUB,DOP_SBB, + DOP_CMP,DOP_XOR, + DOP_AND,DOP_OR, + DOP_MOV, + DOP_TEST, + DOP_IMUL, + DOP_XCHG, +}; + +enum ShiftOps { + SHIFT_ROL,SHIFT_ROR, + SHIFT_RCL,SHIFT_RCR, + SHIFT_SHL,SHIFT_SHR, + SHIFT_SAR, +}; + +enum BranchTypes { + BR_O,BR_NO,BR_B,BR_NB, + BR_Z,BR_NZ,BR_BE,BR_NBE, + BR_S,BR_NS,BR_P,BR_NP, + BR_L,BR_NL,BR_LE,BR_NLE +}; + +enum BlockType { + BT_Free=0, + BT_Normal, + BT_SingleLink, + BT_DualLink, + BT_CheckFlags, +}; + +enum BlockReturn { + BR_Normal=0, + BR_Cycles, + BR_Link1,BR_Link2, + BR_Opcode, + BR_CallBack, +}; + +#define DYNFLG_HAS16 0x1 //Would like 8-bit host reg support +#define DYNFLG_HAS8 0x2 //Would like 16-bit host reg support +#define DYNFLG_LOAD 0x4 //Load value when accessed +#define DYNFLG_SAVE 0x8 //Needs to be saved back at the end of block +#define DYNFLG_CHANGED 0x10 //Load value only once because of save +#define DYNFLG_LOADONCE 0x20 //Load value only once because of save + +class GenReg; +class CodePageHandler; + +struct DynReg { + Bitu flags; + GenReg * genreg; + void * data; +}; + +enum DynAccess { + DA_d,DA_w, + DA_bh,DA_bl +}; + +enum ByteCombo { + BC_ll,BC_lh, + BC_hl,BC_hh, +}; + +static DynReg DynRegs[G_MAX]; +#define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ] + +static struct { + Bitu ea,tmpb,tmpd,stack,shift; +} extra_regs; + +static void IllegalOption(void) { + E_Exit("Illegal option"); +} + +#include "core_dyn_x86\cache.h" + +static struct { + Bitu callback; + CacheBlock * lastblock; +} core_dyn; + + +#include "core_dyn_x86\risc_x86.h" + +struct DynState { + DynReg regs[G_MAX]; +}; + +static void dyn_releaseregs(void) { + for (Bitu i=0;iregs[i].flags=DynRegs[i].flags; + state->regs[i].genreg=DynRegs[i].genreg; + } +} + +static void dyn_loadstate(DynState * state) { + for (Bitu i=0;iregs[i]); + } +} + + +static void dyn_synchstate(DynState * state) { + for (Bitu i=0;iregs[i]); + } +} +#include "core_dyn_x86\decoder.h" + +Bits CPU_Core_Dyn_X86_Run(void) { + /* Determine the linear address of CS:EIP */ +restart_core: + PhysPt ip_point=SegPhys(cs)+reg_eip; + Bitu ip_page=ip_point>>12; + mem_readb(ip_point); //Init whatever page we are in + PageHandler * handler=paging.tlb.handler[ip_page]; + CodePageHandler * chandler=0; + #if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; + #endif + if (handler->flags & PFLAG_HASCODE) { + /* Find correct Dynamic Block to run */ + chandler=(CodePageHandler *)handler; +findblock:; + CacheBlock * block=chandler->FindCacheBlock(ip_point&4095); + if (!block) { + block=CreateCacheBlock(ip_point,cpu.code.big,128); + DYN_LOG("Created block size %x type %d",block->cache.size,block->type); + chandler->AddCacheBlock(block); + if (block->page.end>=4096) { + DYN_LOG("block crosses page boundary"); + } + } +run_block: + BlockReturn ret=gen_runcode(block->cache.start); + switch (ret) { + case BR_Normal: + /* Maybe check if we staying in the same page? */ +#if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif + goto restart_core; + case BR_Cycles: +#if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif + return CBRET_NONE; + case BR_CallBack: + return core_dyn.callback; + case BR_Opcode: + CPU_CycleLeft+=CPU_Cycles; + CPU_Cycles=1; + return CPU_Core_Normal_Run(); + case BR_Link1: + case BR_Link2: + { + Bitu temp_ip=SegPhys(cs)+reg_eip; + Bitu temp_page=temp_ip >> 12; + CodePageHandler * temp_handler=(CodePageHandler *)paging.tlb.handler[temp_page]; + if (temp_handler->flags & PFLAG_HASCODE) { + block=temp_handler->FindCacheBlock(temp_ip & 4095); + if (!block) goto restart_core; + cache_linkblocks(core_dyn.lastblock,block,ret==BR_Link2); + goto run_block; + } + } + goto restart_core; + } + } else { + if (handler->flags & PFLAG_NOCODE) { + LOG_MSG("can't run code in this page"); + return CPU_Core_Normal_Run(); + } + Bitu phys_page=ip_page; + if (!PAGING_MakePhysPage(phys_page)) { + LOG_MSG("Can't find physpage"); + return CPU_Core_Normal_Run(); + } + chandler=new CodePageHandler(handler); + MEM_SetPageHandler(phys_page,1,chandler); //Setup the handler + PAGING_UnlinkPages(ip_page,1); + goto findblock; + } + return 0; +} + + +void CPU_Core_Dyn_X86_Init(void) { + Bits i; + /* Setup the global registers and their flags */ + for (i=0;iflags|PFLAG_HASCODE; + flags&=~PFLAG_WRITEABLE; + memset(&hash_map,0,sizeof(hash_map)); + memset(&write_map,0,sizeof(write_map)); + } + void InvalidateRange(Bits start,Bits end) { + Bits maps=start>>DYN_HASH_SHIFT; + Bits map=maps; + Bits count=write_map[maps]; + while (map>=0 && count>0) { + CacheBlock * block=hash_map[map]; + CacheBlock * * where=&hash_map[map]; + while (block) { + CacheBlock * nextblock=block->hash.next; + if (start<=block->page.end && end>=block->page.start) { + for (Bitu i=block->page.first;i<=block->page.last;i++) write_map[i]--; + block->code_page=0; //Else resetblock will do double work + count--; + cache_resetblock(block); + *where=nextblock; + } else { + where=&block->hash.next; + } + block=nextblock; + } + map--; + } + } + void writeb(PhysPt addr,Bitu val){ + if (val!=host_readb(hostmem+(addr&4095))) { + InvalidateRange(addr&4095,addr&4095); + host_writeb(hostmem+(addr&4095),val); + } + } + void writew(PhysPt addr,Bitu val){ + if (val!=host_readw(hostmem+(addr&4095))) { + InvalidateRange(addr&4095,(addr&4095)+1); + host_writew(hostmem+(addr&4095),val); + } + } + void writed(PhysPt addr,Bitu val){ + if (val!=host_readd(hostmem+(addr&4095))) { + InvalidateRange(addr&4095,(addr&4095)+3); + host_writed(hostmem+(addr&4095),val); + } + } + void AddCacheBlock(CacheBlock * block) { + Bit16u first,last; + if (block->page.start<0) first=0; + else first=block->page.start>>DYN_HASH_SHIFT; + block->hash.next=hash_map[first]; + hash_map[first]=block; + if (block->page.end>=4096) last=DYN_PAGE_HASH-1; + else last=block->page.end>>DYN_HASH_SHIFT; + block->page.first=first; + block->page.last=last; + for (;first<=last;first++) { + write_map[first]++; + } + block->code_page=this; + } + void DelCacheBlock(CacheBlock * block) { + CacheBlock * * where=&hash_map[block->page.first]; + while (*where) { + if (*where==block) { + *where=block->hash.next; + break; + } + where=&((*where)->hash.next); + } + for (Bitu i=block->page.first;i<=block->page.last;i++) { + write_map[i]--; + } + } + CacheBlock * FindCacheBlock(Bitu start) { + CacheBlock * block=hash_map[start>>DYN_HASH_SHIFT]; + while (block) { + if (block->page.start==start) return block; + block=block->hash.next; + } + return 0; + } + HostPt GetHostPt(Bitu phys_page) { + hostmem=old_pagehandler->GetHostPt(phys_page); + return hostmem; + } +private: + PageHandler * old_pagehandler; + CacheBlock * hash_map[DYN_PAGE_HASH]; + Bit8u write_map[DYN_PAGE_HASH]; + HostPt hostmem; +}; + + +static INLINE void cache_addunsedblock(CacheBlock * block) { + block->list_next=cache.block.free; + cache.block.free=block; +} + +static CacheBlock * cache_getblock(void) { + CacheBlock * ret=cache.block.free; + if (!ret) E_Exit("Ran out of CacheBlocks" ); + cache.block.free=ret->list_next; + return ret; +} + +static INLINE void cache_clearlinkfrom(CacheBlock * block,CacheBlock * from) { + for (Bitu i=0;ilink.from[i]==from) block->link.from[i]=0; + } +} + +static INLINE void cache_clearlinkto(CacheBlock * block,CacheBlock * to) { + if (block->link.to[0]==to) block->link.to[0]=&cache.linkblocks[0]; + if (block->link.to[1]==to) block->link.to[1]=&cache.linkblocks[1]; +} + +static void cache_linkblocks(CacheBlock * from,CacheBlock * to,Bitu link) { + from->link.to[link]=to; + CacheBlock * clear=to->link.from[to->link.index]; + if (clear) { + DYN_LOG("backlink buffer full"); + cache_clearlinkto(to->link.from[to->link.index],to); + } + to->link.from[to->link.index]=from; + to->link.index++; + if (to->link.index>=DYN_LINKS) to->link.index=0; +} + +static void cache_resetblock(CacheBlock * block) { + Bits i; + DYN_LOG("Resetted block"); + block->type=BT_Free; + /* Clear all links to this block from other blocks */ + for (i=0;ilink.from[i]) cache_clearlinkto(block->link.from[i],block); + block->link.from[i]=0; + } + /* Clear all links from this block to other blocks */ + if (block->link.to[0]!=&cache.linkblocks[0]) { + cache_clearlinkfrom(block->link.to[0],block); + block->link.to[0]=&cache.linkblocks[0]; + } + if (block->link.to[1]!=&cache.linkblocks[1]) { + cache_clearlinkfrom(block->link.to[1],block); + block->link.to[1]=&cache.linkblocks[1]; + } + block->link.index=0; + if (block->code_page) block->code_page->DelCacheBlock(block); +} + +static CacheBlock * cache_openblock(void) { + CacheBlock * block=cache.block.active; + /* check for enough space in this block */ + Bitu size=block->cache.size; + CacheBlock * nextblock=block->list_next; + while (sizecache.size; + CacheBlock * tempblock=nextblock->list_next; + if (nextblock->type!=BT_Free) cache_resetblock(nextblock); + cache_addunsedblock(nextblock); + nextblock=tempblock; + } +skipresize: + block->cache.size=size; + block->list_next=nextblock; + cache.pos=block->cache.start; + return block; +} + +static void cache_closeblock(BlockType type) { + CacheBlock * block=cache.block.active; + /* Setup some structures in the block determined by type */ + block->type=type; + switch (type) { + case BT_Normal: + break; + case BT_SingleLink: + block->link.to[0]=&cache.linkblocks[0]; + break; + case BT_DualLink: + block->link.to[0]=&cache.linkblocks[0]; + block->link.to[1]=&cache.linkblocks[1]; + break; + } + /* Close the block with correct alignments */ + Bitu written=cache.pos-block->cache.start; + if (written>block->cache.size) { + if (!block->list_next) { + if (written>block->cache.size+CACHE_MAXSIZE) E_Exit("CacheBlock overrun"); + } else E_Exit("CacheBlock overrun"); + } else { + Bitu new_size; + Bitu left=block->cache.size-written; + /* Smaller than cache align then don't bother to resize */ + if (left>CACHE_ALIGN) { + new_size=((written-1)|(CACHE_ALIGN-1))+1; + } else new_size=block->cache.size; + CacheBlock * newblock=cache_getblock(); + newblock->cache.start=block->cache.start+new_size; + newblock->cache.size=block->cache.size-new_size; + newblock->list_next=block->list_next; + newblock->type=BT_Free; + block->cache.size=new_size; + block->list_next=newblock; + } + /* Advance the active block pointer */ + if (!block->list_next) { + DYN_LOG("Cache full restarting"); + cache.block.active=cache.block.first; + } else { + cache.block.active=block->list_next; + } +} + +static INLINE void cache_addb(Bit8u val) { + *cache.pos++=val; +} + +static INLINE void cache_addw(Bit16u val) { + *(Bit16u*)cache.pos=val; + cache.pos+=2; +} + +static INLINE void cache_addd(Bit32u val) { + *(Bit32u*)cache.pos=val; + cache.pos+=4; +} + + +static void gen_return(BlockReturn retcode); + +static void cache_init(void) { + Bits i; + memset(&cache_blocks,0,sizeof(cache_blocks)); + cache.block.free=&cache_blocks[0]; + for (i=0;icache.start=&cache_code[0]; + block->cache.size=CACHE_TOTAL; + block->list_next=0; //Last block in the list + cache.pos=&cache_code_link_blocks[0][0]; + cache.linkblocks[0].cache.start=cache.pos; + gen_return(BR_Link1); + cache.pos=&cache_code_link_blocks[1][0]; + cache.linkblocks[1].cache.start=cache.pos; + gen_return(BR_Link2); +} \ No newline at end of file diff --git a/src/cpu/core_dyn_x86/decoder.h b/src/cpu/core_dyn_x86/decoder.h new file mode 100644 index 00000000..8326d252 --- /dev/null +++ b/src/cpu/core_dyn_x86/decoder.h @@ -0,0 +1,1252 @@ +static struct DynDecode { + PhysPt code; + PhysPt code_start; + PhysPt op_start; + bool big_op; + bool big_addr; + bool rep; + Bitu cycles; + CacheBlock * block; + struct { + Bitu val; + Bitu mod; + Bitu rm; + Bitu reg; + } modrm; + DynReg * segprefix; +} decode; + +#define FASTCALL __fastcall + +#include "helpers.h" + +static Bit8u FASTCALL decode_fetchb(void) { + return mem_readb(decode.code++); +} +static Bit16u FASTCALL decode_fetchw(void) { + decode.code+=2; + return mem_readw(decode.code-2); +} +static Bit32u FASTCALL decode_fetchd(void) { + decode.code+=4; + return mem_readd(decode.code-4); +} + +static void dyn_read_byte(DynReg * addr,DynReg * dst,Bitu high) { + if (high) gen_call_function((void *)&mem_readb,"%Dd%Rh",addr,dst); + else gen_call_function((void *)&mem_readb,"%Dd%Rl",addr,dst); +} +static void dyn_write_byte(DynReg * addr,DynReg * val,Bitu high) { + if (high) gen_call_function((void *)&mem_writeb,"%Dd%Dh",addr,val); + else gen_call_function((void *)&mem_writeb,"%Dd%Dl",addr,val); +} + +static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword) { + if (dword) gen_call_function((void *)&mem_readd,"%Dd%Rd",addr,dst); + else gen_call_function((void *)&mem_readw,"%Dd%Rw",addr,dst); +} + +static void dyn_write_word(DynReg * addr,DynReg * val,bool dword) { + if (dword) gen_call_function((void *)&mem_writed,"%Dd%Dd",addr,val); + else gen_call_function((void *)&mem_writew,"%Dd%Dw",addr,val); +} + +static void dyn_reduce_cycles(void) { + if (!decode.cycles) decode.cycles++; + gen_lea(DREG(CYCLES),DREG(CYCLES),0,0,-(Bits)decode.cycles); + gen_releasereg(DREG(CYCLES)); +} + +static void dyn_push(DynReg * dynreg) { + gen_storeflags(); + if (decode.big_op) { + gen_dop_word_imm(DOP_SUB,true,DREG(ESP),4); + } else { + gen_dop_word_imm(DOP_SUB,true,DREG(ESP),2); + } + gen_dop_word(DOP_MOV,true,DREG(STACK),DREG(ESP)); + gen_dop_word(DOP_AND,true,DREG(STACK),DREG(SMASK)); + gen_dop_word(DOP_ADD,true,DREG(STACK),DREG(SS)); + if (decode.big_op) { + gen_call_function((void *)&mem_writed,"%Drd%Dd",DREG(STACK),dynreg); + } else { + //Can just push the whole 32-bit word as operand + gen_call_function((void *)&mem_writew,"%Drd%Dd",DREG(STACK),dynreg); + } + gen_releasereg(DREG(STACK)); + gen_restoreflags(); +} + +static void dyn_pop(DynReg * dynreg) { + gen_storeflags(); + gen_dop_word(DOP_MOV,true,DREG(STACK),DREG(ESP)); + gen_dop_word(DOP_AND,true,DREG(STACK),DREG(SMASK)); + gen_dop_word(DOP_ADD,true,DREG(STACK),DREG(SS)); + if (decode.big_op) { + gen_call_function((void *)&mem_readd,"%Rd%Drd",dynreg,DREG(STACK)); + } else { + gen_call_function((void *)&mem_readw,"%Rw%Drd",dynreg,DREG(STACK)); + } + if (dynreg!=DREG(ESP)) { + if (decode.big_op) { + gen_dop_word_imm(DOP_ADD,true,DREG(ESP),4); + } else { + gen_dop_word_imm(DOP_ADD,true,DREG(ESP),2); + } + } + gen_releasereg(DREG(STACK)); + gen_restoreflags(); +} + +static void FASTCALL dyn_get_modrm(void) { + decode.modrm.val=decode_fetchb(); + decode.modrm.mod=(decode.modrm.val >> 6) & 3; + decode.modrm.reg=(decode.modrm.val >> 3) & 7; + decode.modrm.rm=(decode.modrm.val & 7); +} + +static void FASTCALL dyn_fill_ea(bool addseg=true) { + DynReg * segbase; + if (!decode.big_addr) { + Bits imm; + switch (decode.modrm.mod) { + case 0:imm=0;break; + case 1:imm=(Bit8s)decode_fetchb();break; + case 2:imm=(Bit16s)decode_fetchw();break; + } + switch (decode.modrm.rm) { + case 0:/* BX+SI */ + gen_lea(DREG(EA),DREG(EBX),DREG(ESI),0,imm); + segbase=DREG(DS); + break; + case 1:/* BX+DI */ + gen_lea(DREG(EA),DREG(EBX),DREG(EDI),0,imm); + segbase=DREG(DS); + break; + case 2:/* BP+SI */ + gen_lea(DREG(EA),DREG(EBP),DREG(ESI),0,imm); + segbase=DREG(SS); + break; + case 3:/* BP+DI */ + gen_lea(DREG(EA),DREG(EBP),DREG(EDI),0,imm); + segbase=DREG(SS); + break; + case 4:/* SI */ + gen_lea(DREG(EA),DREG(ESI),0,0,imm); + segbase=DREG(DS); + break; + case 5:/* DI */ + gen_lea(DREG(EA),DREG(EDI),0,0,imm); + segbase=DREG(DS); + break; + case 6:/* imm/BP */ + if (!decode.modrm.mod) { + imm=(Bit16s)decode_fetchw(); + gen_dop_word_imm(DOP_MOV,true,DREG(EA),imm); + segbase=DREG(DS); + } else { + gen_lea(DREG(EA),DREG(EBP),0,0,imm); + segbase=DREG(SS); + } + break; + case 7: /* BX */ + gen_lea(DREG(EA),DREG(EBX),0,0,imm); + segbase=DREG(DS); + break; + } + gen_extend_word(false,DREG(EA),DREG(EA)); + } else { + Bits imm=0; + DynReg * base=0;DynReg * scaled=0;Bitu scale=0; + switch (decode.modrm.rm) { + case 0:base=DREG(EAX);segbase=DREG(DS);break; + case 1:base=DREG(ECX);segbase=DREG(DS);break; + case 2:base=DREG(EDX);segbase=DREG(DS);break; + case 3:base=DREG(EBX);segbase=DREG(DS);break; + case 4: /* SIB */ + { + Bitu sib=decode_fetchb(); + switch (sib & 7) { + case 0:base=DREG(EAX);segbase=DREG(DS);break; + case 1:base=DREG(ECX);segbase=DREG(DS);break; + case 2:base=DREG(EDX);segbase=DREG(DS);break; + case 3:base=DREG(EBX);segbase=DREG(DS);break; + case 4:base=DREG(ESP);segbase=DREG(SS);break; + case 5: + if (decode.modrm.mod) { + base=DREG(EBP);segbase=DREG(SS); + } else { + imm=(Bit32s)decode_fetchd();segbase=DREG(DS); + } + break; + case 6:base=DREG(ESI);segbase=DREG(DS);break; + case 7:base=DREG(EDI);segbase=DREG(DS);break; + } + static DynReg * scaledtable[8]={ + DREG(EAX),DREG(ECX),DREG(EDX),DREG(EBX), + 0,DREG(EBP),DREG(ESI),DREG(EDI), + }; + scaled=scaledtable[(sib >> 3) &7]; + scale=(sib >> 6); + } + break; /* SIB Break */ + case 5: + if (decode.modrm.mod) { + base=DREG(EBP);segbase=DREG(SS); + } else { + imm=(Bit32s)decode_fetchd();segbase=DREG(DS); + } + break; + case 6:base=DREG(ESI);segbase=DREG(DS);break; + case 7:base=DREG(EDI);segbase=DREG(DS);break; + } + switch (decode.modrm.mod) { + case 1:imm=(Bit8s)decode_fetchb();break; + case 2:imm=(Bit32s)decode_fetchd();break; + } + gen_lea(DREG(EA),base,scaled,scale,imm); + } + if (addseg) { + gen_lea(DREG(EA),DREG(EA),decode.segprefix ? decode.segprefix : segbase,0,0); + } +} + +static void dyn_dop_ebgb(DualOps op) { + dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg&3]; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_byte(DREG(EA),DREG(TMPB),false); + gen_dop_byte(op,DREG(TMPB),0,rm_reg,decode.modrm.reg&4); + dyn_write_byte(DREG(EA),DREG(TMPB),false); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB)); + } else { + gen_dop_byte(op,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,rm_reg,decode.modrm.reg&4); + } +} + + +static void dyn_dop_gbeb(DualOps op) { + dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg&3]; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_byte(DREG(EA),DREG(TMPB),false); + gen_dop_byte(op,rm_reg,decode.modrm.reg&4,DREG(TMPB),0); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB)); + } else { + gen_dop_byte(op,rm_reg,decode.modrm.reg&4,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); + } +} + +static void dyn_mov_ebib(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + //TODO Maybe not use a temp register here and call mem_writeb directly? + dyn_fill_ea(); + gen_dop_byte_imm(DOP_MOV,DREG(TMPB),0,decode_fetchb()); + dyn_write_byte(DREG(EA),DREG(TMPB),false); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB)); + } else { + gen_dop_byte_imm(DOP_MOV,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,decode_fetchb()); + } +} + +static void dyn_mov_ebgb(void) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];Bitu rm_regi=decode.modrm.reg&4; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_write_byte(DREG(EA),rm_reg,rm_regi); + gen_releasereg(DREG(EA)); + } else { + gen_dop_byte(DOP_MOV,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,rm_reg,rm_regi); + } +} + +static void dyn_mov_gbeb(void) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];Bitu rm_regi=decode.modrm.reg&4; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_byte(DREG(EA),rm_reg,rm_regi); + gen_releasereg(DREG(EA)); + } else { + gen_dop_byte(DOP_MOV,rm_reg,rm_regi,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); + } +} + +static void dyn_dop_evgv(DualOps op) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg]; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_dop_word(op,decode.big_op,DREG(TMPW),rm_reg); + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW)); + } else { + gen_dop_word(op,decode.big_op,&DynRegs[decode.modrm.rm],rm_reg); + } +} + +static void dyn_dop_gvev(DualOps op) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg]; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_dop_word(op,decode.big_op,rm_reg,DREG(TMPW)); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW)); + } else { + gen_dop_word(op,decode.big_op,rm_reg,&DynRegs[decode.modrm.rm]); + } +} + +static void dyn_mov_evgv(void) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg]; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_write_word(DREG(EA),rm_reg,decode.big_op); + gen_releasereg(DREG(EA)); + } else { + gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],rm_reg); + } +} + +static void dyn_mov_gvev(void) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg]; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),rm_reg,decode.big_op); + gen_releasereg(DREG(EA)); + } else { + gen_dop_word(DOP_MOV,decode.big_op,rm_reg,&DynRegs[decode.modrm.rm]); + } +} +static void dyn_mov_eviv(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + gen_dop_word_imm(DOP_MOV,decode.big_op,DREG(TMPW),decode.big_op ? decode_fetchd() : decode_fetchw()); + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW)); + } else { + gen_dop_word_imm(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],decode.big_op ? decode_fetchd() : decode_fetchw()); + } +} + +static void dyn_dshift_ev_gv(bool left,bool immediate) { + dyn_get_modrm(); + DynReg * rm_reg=&DynRegs[decode.modrm.reg]; + DynReg * ea_reg; + if (decode.modrm.mod<3) { + dyn_fill_ea();ea_reg=DREG(TMPW); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + } else ea_reg=&DynRegs[decode.modrm.rm]; + if (immediate) gen_dshift_imm(decode.big_op,left,ea_reg,rm_reg,decode_fetchb()); + else gen_dshift_cl(decode.big_op,left,ea_reg,rm_reg,DREG(ECX)); + if (decode.modrm.mod<3) { + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW)); + } +} + + +static DualOps grp1_table[8]={DOP_ADD,DOP_OR,DOP_ADC,DOP_SBB,DOP_AND,DOP_SUB,DOP_XOR,DOP_CMP}; +static void dyn_grp1_eb_ib(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_byte(DREG(EA),DREG(TMPB),false); + gen_dop_byte_imm(grp1_table[decode.modrm.reg],DREG(TMPB),0,decode_fetchb()); + if (grp1_table[decode.modrm.reg]!=DOP_CMP) dyn_write_byte(DREG(EA),DREG(TMPB),false); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB)); + } else { + gen_dop_byte_imm(grp1_table[decode.modrm.reg],&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,decode_fetchb()); + } +} + +static void dyn_grp1_ev_ivx(bool withbyte) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + Bits imm=withbyte ? (Bit8s)decode_fetchb() : (decode.big_op ? decode_fetchd(): decode_fetchw()); + gen_dop_word_imm(grp1_table[decode.modrm.reg],decode.big_op,DREG(TMPW),imm); + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW)); + } else { + Bits imm=withbyte ? (Bit8s)decode_fetchb() : (decode.big_op ? decode_fetchd(): decode_fetchw()); + gen_dop_word_imm(grp1_table[decode.modrm.reg],decode.big_op,&DynRegs[decode.modrm.rm],imm); + } +} + + +static ShiftOps grp2_table[8]={ + SHIFT_ROL,SHIFT_ROR,SHIFT_RCL,SHIFT_RCR, + SHIFT_SHL,SHIFT_SHR,SHIFT_SHL,SHIFT_SAR +}; + +enum grp2_types { + grp2_1,grp2_imm,grp2_cl, +}; + +static void dyn_grp2_eb(grp2_types type) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_byte(DREG(EA),DREG(TMPB),false); + DynReg * shift; + switch (type) { + case grp2_cl:shift=DREG(ECX);break; + case grp2_1:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,1);break; + case grp2_imm:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,decode_fetchb());break; + } + gen_shift_byte(grp2_table[decode.modrm.reg],shift,DREG(TMPB),0); + dyn_write_byte(DREG(EA),DREG(TMPB),false); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB));gen_releasereg(DREG(SHIFT)); + } else { + DynReg * shift; + switch (type) { + case grp2_cl:shift=DREG(ECX);break; + case grp2_1:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,1);break; + case grp2_imm:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,decode_fetchb());break; + } + gen_shift_byte(grp2_table[decode.modrm.reg],shift,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); + gen_releasereg(DREG(SHIFT)); + } +} + +static void dyn_grp2_ev(grp2_types type) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + DynReg * shift; + switch (type) { + case grp2_cl:shift=DREG(ECX);break; + case grp2_1:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,1);break; + case grp2_imm:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,decode_fetchb());break; + } + gen_shift_word(grp2_table[decode.modrm.reg],shift,decode.big_op,DREG(TMPW)); + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW));gen_releasereg(DREG(SHIFT)); + } else { + DynReg * shift; + switch (type) { + case grp2_cl:shift=DREG(ECX);break; + case grp2_1:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,1);break; + case grp2_imm:shift=DREG(SHIFT);gen_dop_byte_imm(DOP_MOV,DREG(SHIFT),0,decode_fetchb());break; + } + gen_shift_word(grp2_table[decode.modrm.reg],shift,decode.big_op,&DynRegs[decode.modrm.rm]); + gen_releasereg(DREG(SHIFT)); + } +} + +static void dyn_grp3_eb(void) { + DynState state;Bit8u * branch; + dyn_get_modrm();DynReg * src;Bit8u src_i; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_byte(DREG(EA),DREG(TMPB),false); + src=DREG(TMPB);src_i=0; + } else { + src=&DynRegs[decode.modrm.rm&3]; + src_i=decode.modrm.rm&4; + } + switch (decode.modrm.reg) { + case 0x0: /* test eb,ib */ + gen_dop_byte_imm(DOP_TEST,src,src_i,decode_fetchb()); + goto skipsave; + case 0x2: /* NOT Eb */ + gen_sop_byte(SOP_NOT,src,src_i); + break; + case 0x3: /* NEG Eb */ + gen_sop_byte(SOP_NEG,src,src_i); + break; + case 0x4: /* mul Eb */ + gen_mul_byte(false,DREG(EAX),src,src_i); + goto skipsave; + case 0x5: /* imul Eb */ + gen_mul_byte(true,DREG(EAX),src,src_i); + goto skipsave; + case 0x6: /* div Eb */ + case 0x7: /* idiv Eb */ + /* EAX could be used, so precache it */ + if (decode.modrm.mod==3) + gen_dop_byte(DOP_MOV,DREG(TMPB),0,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); + gen_storeflags();gen_releasereg(DREG(EAX)); + gen_call_function((decode.modrm.reg==6) ? (void *)&dyn_helper_divb : (void *)&dyn_helper_idivb, + "%Rd%Drl",DREG(TMPB),DREG(TMPB)); + gen_dop_word(DOP_OR,true,DREG(TMPB),DREG(TMPB)); + branch=gen_create_branch(BR_Z); + dyn_savestate(&state); + dyn_reduce_cycles(); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.op_start-decode.code_start); + dyn_save_flags(true); + dyn_releaseregs(); + gen_call_function((void *)&CPU_Exception,"%Id%Id",0,0); + dyn_load_flags(); + gen_return(BR_Normal); + dyn_loadstate(&state); + gen_fill_branch(branch); + gen_restoreflags(); + goto skipsave; + } + /* Save the result if memory op */ + if (decode.modrm.mod<3) dyn_write_byte(DREG(EA),DREG(TMPB),false); +skipsave: + gen_releasereg(DREG(TMPB));gen_releasereg(DREG(EA)); +} + +static void dyn_grp3_ev(void) { + DynState state;Bit8u * branch; + dyn_get_modrm();DynReg * src; + if (decode.modrm.mod<3) { + dyn_fill_ea();src=DREG(TMPW); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + } else src=&DynRegs[decode.modrm.rm]; + switch (decode.modrm.reg) { + case 0x0: /* test ev,iv */ + gen_dop_word_imm(DOP_TEST,decode.big_op,src,decode.big_op ? decode_fetchd() : decode_fetchw()); + goto skipsave; + case 0x2: /* NOT Ev */ + gen_sop_word(SOP_NOT,decode.big_op,src); + break; + case 0x3: /* NEG Eb */ + gen_sop_word(SOP_NEG,decode.big_op,src); + break; + case 0x4: /* mul Eb */ + gen_mul_word(false,DREG(EAX),DREG(EDX),decode.big_op,src); + goto skipsave; + case 0x5: /* imul Eb */ + gen_mul_word(true,DREG(EAX),DREG(EDX),decode.big_op,src); + goto skipsave; + case 0x6: /* div Eb */ + case 0x7: /* idiv Eb */ + /* EAX could be used, so precache it */ + if (decode.modrm.mod==3) + gen_dop_word(DOP_MOV,decode.big_op,DREG(TMPW),&DynRegs[decode.modrm.rm]); + gen_storeflags();gen_releasereg(DREG(EAX));gen_releasereg(DREG(EDX)); + void * func=(decode.modrm.reg==6) ? + (decode.big_op ? (void *)&dyn_helper_divd : (void *)&dyn_helper_divw) : + (decode.big_op ? (void *)&dyn_helper_idivd : (void *)&dyn_helper_idivw); + gen_call_function(func,"%Rd%Drd",DREG(TMPW),DREG(TMPW)); + gen_dop_word(DOP_OR,true,DREG(TMPW),DREG(TMPW)); + branch=gen_create_branch(BR_Z); + dyn_savestate(&state); + dyn_reduce_cycles(); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.op_start-decode.code_start); + dyn_save_flags(true); + dyn_releaseregs(); + gen_call_function((void *)&CPU_Exception,"%Id%Id",0,0); + dyn_load_flags(); + gen_return(BR_Normal); + dyn_loadstate(&state); + gen_fill_branch(branch); + gen_restoreflags(); + goto skipsave; + } + /* Save the result if memory op */ + if (decode.modrm.mod<3) dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); +skipsave: + gen_releasereg(DREG(TMPW));gen_releasereg(DREG(EA)); +} + + +static void dyn_mov_ev_seg(void) { + dyn_get_modrm(); + gen_load_host(&Segs.val[(SegNames) decode.modrm.reg],DREG(TMPW),2); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA)); + } else { + gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],DREG(TMPW)); + } + gen_releasereg(DREG(TMPW)); +} + +static void dyn_mov_seg_ev(void) { + dyn_get_modrm(); + SegNames seg=(SegNames)decode.modrm.reg; + if (seg==cs) IllegalOption(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),DREG(EA),decode.big_op); + gen_call_function((void *)&CPU_SetSegGeneral,"%Id%Drw",seg,DREG(EA)); + } else { + gen_call_function((void *)&CPU_SetSegGeneral,"%Id%Dw",seg,&DynRegs[decode.modrm.rm]); + } + gen_releasereg(&DynRegs[G_ES+seg]); +} + + +static void dyn_push_seg(SegNames seg) { + gen_load_host(&Segs.val[seg],DREG(TMPW),2); + dyn_push(DREG(TMPW)); + gen_releasereg(DREG(TMPW)); +} + +static void dyn_pop_seg(SegNames seg) { + gen_storeflags(); + dyn_pop(DREG(TMPW)); + gen_call_function((void*)&CPU_SetSegGeneral,"%Id%Drw",seg,DREG(TMPW)); + gen_releasereg(&DynRegs[G_ES+seg]); + gen_restoreflags(); +} + +static void dyn_pop_ev(void) { + gen_storeflags(); + dyn_pop(DREG(TMPW)); + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA)); + } else { + gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],DREG(TMPW)); + } + gen_releasereg(DREG(TMPW)); + gen_restoreflags(); +} + +static void dyn_leave(void) { + gen_storeflags(); + gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(SMASK)); + gen_sop_word(SOP_NOT,true,DREG(TMPW)); + gen_dop_word(DOP_AND,true,DREG(ESP),DREG(TMPW)); + gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(EBP)); + gen_dop_word(DOP_AND,true,DREG(TMPW),DREG(SMASK)); + gen_dop_word(DOP_OR,true,DREG(ESP),DREG(TMPW)); + dyn_pop(DREG(EBP)); + gen_releasereg(DREG(TMPW)); + gen_restoreflags(); +} + +static void dyn_segprefix(SegNames seg) { + if (decode.segprefix) IllegalOption(); + decode.segprefix=&DynRegs[G_ES+seg]; +} + +static void dyn_closeblock(BlockType type) { + //Shouldn't create empty block normally but let's do it like this + if (decode.code>decode.code_start) decode.code--; + Bitu start_page=decode.code_start >> 12; + Bitu end_page=decode.code>>12; + decode.block->page.start=(Bit16s)decode.code_start & 4095; + decode.block->page.end=(Bit16s)((end_page-start_page)*4096+(decode.code&4095)); + cache_closeblock(type); +} + +static void dyn_normal_exit(BlockReturn code) { + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_reduce_cycles(); + dyn_releaseregs(); + gen_return(code); + dyn_closeblock(BT_Normal); +} + +static void dyn_exit_link(bool dword,Bits eip_change) { + gen_lea(DREG(EIP),DREG(EIP),0,0,(decode.code-decode.code_start)+eip_change); + if (!dword) gen_extend_word(false,DREG(EIP),DREG(EIP)); + dyn_reduce_cycles(); + dyn_releaseregs(); +// gen_return(BR_Normal); + gen_jmp_ptr(&decode.block->link.to[0],offsetof(CacheBlock,cache.start)); + dyn_closeblock(BT_SingleLink); +} + +static void dyn_branched_exit(BranchTypes btype,Bit32s eip_add) { + dyn_reduce_cycles(); + dyn_releaseregs(); + Bitu eip_base=decode.code-decode.code_start; + Bit8u * data=gen_create_branch(btype); + /* Branch not taken */ + gen_lea(DREG(EIP),DREG(EIP),0,0,eip_base); + gen_releasereg(DREG(EIP)); +// gen_return(BR_Normal); + gen_jmp_ptr(&decode.block->link.to[0],offsetof(CacheBlock,cache.start)); + gen_fill_branch(data); + /* Branch taken */ + gen_lea(DREG(EIP),DREG(EIP),0,0,eip_base+eip_add); + gen_releasereg(DREG(EIP)); +// gen_return(BR_Normal); + gen_jmp_ptr(&decode.block->link.to[1],offsetof(CacheBlock,cache.start)); + dyn_closeblock(BT_DualLink); +} + +enum LoopTypes { + LOOP_NONE,LOOP_NE,LOOP_E, +}; + +static void dyn_loop(LoopTypes type) { + Bits eip_add=(Bit8s)decode_fetchb(); + Bitu eip_base=decode.code-decode.code_start; + gen_storeflags(); + dyn_reduce_cycles(); + Bit8u * branch1; + Bit8u * branch2=0; + gen_sop_word(SOP_DEC,decode.big_addr,DREG(ECX)); + dyn_releaseregs(); + branch1=gen_create_branch(BR_Z); + gen_restoreflags(true); + switch (type) { + case LOOP_NONE: + break; + case LOOP_E: + branch2=gen_create_branch(BR_NZ); + break; + case LOOP_NE: + branch2=gen_create_branch(BR_Z); + break; + } + gen_lea(DREG(EIP),DREG(EIP),0,0,eip_base+eip_add); + gen_releasereg(DREG(EIP)); + gen_jmp_ptr(&decode.block->link.to[0],offsetof(CacheBlock,cache.start)); + gen_fill_branch(branch1); + if (branch2) gen_fill_branch(branch2); + /* Branch taken */ + gen_restoreflags(); + gen_lea(DREG(EIP),DREG(EIP),0,0,eip_base); + gen_releasereg(DREG(EIP)); + gen_jmp_ptr(&decode.block->link.to[1],offsetof(CacheBlock,cache.start)); + dyn_closeblock(BT_DualLink); +} + +static void dyn_ret_near(Bitu bytes) { + dyn_reduce_cycles(); +//TODO maybe AND eip 0xffff, but shouldn't be needed + gen_storeflags(); + dyn_pop(DREG(EIP)); + if (bytes) gen_dop_word_imm(DOP_ADD,true,DREG(ESP),bytes); + dyn_releaseregs(); + gen_restoreflags(); + gen_return(BR_Normal); + dyn_closeblock(BT_Normal); +} + +static void dyn_ret_far(Bitu bytes) { + dyn_reduce_cycles(); +//TODO maybe AND eip 0xffff, but shouldn't be needed + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_save_flags(); + dyn_releaseregs(); + gen_call_function((void*)&CPU_RET,"%Id%Id%Id",decode.big_op,bytes,decode.code-decode.op_start); + dyn_load_flags(); + dyn_releaseregs();; + gen_return(BR_Normal); + dyn_closeblock(BT_Normal); +} + +static void dyn_call_near_imm(void) { + Bits imm; + if (decode.big_op) imm=(Bit32s)decode_fetchd(); + else imm=(Bit16s)decode_fetchw(); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_push(DREG(EIP)); + gen_lea(DREG(EIP),DREG(EIP),0,0,imm); + if (!decode.big_op) gen_extend_word(false,DREG(EIP),DREG(EIP)); + dyn_reduce_cycles(); + dyn_releaseregs(); + gen_return(BR_Normal); +// gen_jmp_ptr(&decode.block->link.to[0],offsetof(CacheBlock,cache.start)); + dyn_closeblock(BT_SingleLink); +} + +static void dyn_call_far_imm(void) { + Bitu sel,off; + off=decode.big_op ? decode_fetchd() : decode_fetchw(); + sel=decode_fetchw(); + dyn_reduce_cycles(); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_save_flags(); + dyn_releaseregs(); + gen_call_function((void*)&CPU_CALL,"%Id%Id%Id%Id",decode.big_op,sel,off,decode.code-decode.op_start); + dyn_load_flags(); + dyn_releaseregs(); + gen_return(BR_Normal); + dyn_closeblock(BT_Normal); +} + +static void dyn_jmp_far_imm(void) { + Bitu sel,off; + off=decode.big_op ? decode_fetchd() : decode_fetchw(); + sel=decode_fetchw(); + dyn_reduce_cycles(); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_save_flags(); + dyn_releaseregs(); + gen_call_function((void*)&CPU_JMP,"%Id%Id%Id%Id",decode.big_op,sel,off,decode.code-decode.op_start); + dyn_load_flags(); + dyn_releaseregs(); + gen_return(BR_Normal); + dyn_closeblock(BT_Normal); +} + +static void dyn_iret(void) { + dyn_save_flags(); + dyn_reduce_cycles(); + gen_dop_word_imm(DOP_ADD,true,DREG(EIP),decode.code-decode.code_start); + dyn_releaseregs(); + gen_call_function((void*)&CPU_IRET,"%Id%Id",decode.big_op,decode.code-decode.op_start); + dyn_load_flags(); + dyn_releaseregs(); + gen_return(BR_Normal); + dyn_closeblock(BT_CheckFlags); +} + +static void dyn_interrupt(Bitu num) { + dyn_save_flags(); + dyn_reduce_cycles(); + gen_dop_word_imm(DOP_ADD,true,DREG(EIP),decode.code-decode.code_start); + dyn_releaseregs(); + gen_call_function((void*)&CPU_Interrupt,"%Id%Id%Id",num,CPU_INT_SOFTWARE,decode.code-decode.op_start); + dyn_load_flags(); + dyn_releaseregs(); + gen_return(BR_Normal); + dyn_closeblock(BT_Normal); +} + +static CacheBlock * CreateCacheBlock(PhysPt start,bool big,Bitu max_opcodes) { + Bits i; + decode.code_start=start; + decode.code=start; + Bitu cycles=0; + decode.block=cache_openblock(); + gen_save_host_direct(&core_dyn.lastblock,(Bit32u)decode.block); + for (i=0;i=G_EAX;i--) { + dyn_pop((i!=G_ESP) ? &DynRegs[i] : DREG(TMPW)); + } + gen_restoreflags();gen_releasereg(DREG(TMPW)); + break; + //segprefix FS,GS + case 0x64:dyn_segprefix(fs);goto restart_prefix; + case 0x65:dyn_segprefix(gs);goto restart_prefix; + //Push immediates + //Operand size + case 0x66:decode.big_op=!big;goto restart_prefix; + //Address size + case 0x67:decode.big_addr=!big;goto restart_prefix; + case 0x68: /* PUSH Iv */ + gen_dop_word_imm(DOP_MOV,decode.big_op,DREG(TMPW),decode.big_op ? decode_fetchd() : decode_fetchw()); + dyn_push(DREG(TMPW)); + gen_releasereg(DREG(TMPW)); + break; + case 0x6a: /* PUSH Ibx */ + gen_dop_word_imm(DOP_MOV,true,DREG(TMPW),(Bit8s)decode_fetchb()); + dyn_push(DREG(TMPW)); + gen_releasereg(DREG(TMPW)); + break; + /* 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()); + return decode.block; + /* Group 1 */ + + case 0x80:dyn_grp1_eb_ib();break; + case 0x81:dyn_grp1_ev_ivx(false);break; + case 0x82:dyn_grp1_eb_ib();break; + case 0x83:dyn_grp1_ev_ivx(true);break; + /* TEST Gb,Eb Gv,Ev */ //Can use G,E since results don't get saved + case 0x84:dyn_dop_gbeb(DOP_TEST);break; + case 0x85:dyn_dop_gvev(DOP_TEST);break; + /* XCHG Eb,Gb Ev,Gv */ + case 0x86:dyn_dop_ebgb(DOP_XCHG);break; + case 0x87:dyn_dop_evgv(DOP_XCHG);break; + /* MOV e,g and g,e */ + case 0x88:dyn_mov_ebgb();break; + case 0x89:dyn_mov_evgv();break; + case 0x8a:dyn_mov_gbeb();break; + case 0x8b:dyn_mov_gvev();break; + /* MOV ev,seg */ + case 0x8c:dyn_mov_ev_seg();break; + /* LEA Gv */ + case 0x8d: + dyn_get_modrm();dyn_fill_ea(false); + gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.reg],DREG(EA)); + gen_releasereg(DREG(EA)); + break; + /* Mov seg,ev */ +// case 0x8e:dyn_mov_seg_ev();break; + /* POP Ev */ + case 0x8f:dyn_pop_ev();break; + //NOP + case 0x90: + break; + //XCHG ax,reg + case 0x91:case 0x92:case 0x93:case 0x94:case 0x95:case 0x96:case 0x97: + gen_dop_word(DOP_XCHG,decode.big_op,DREG(EAX),&DynRegs[opcode&07]); + break; + /* CBW/CWDE */ + case 0x98: + if (decode.big_op) gen_extend_word(true,DREG(EAX),DREG(EAX)); + else gen_extend_byte(true,false,DREG(EAX),DREG(EAX),0); + break; + /* CALL FAR Ip */ + case 0x9a:dyn_call_far_imm();return decode.block; + /* MOV AL,direct addresses */ + case 0xa0: + gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0, + decode.big_addr ? decode_fetchd() : decode_fetchw()); + dyn_read_byte(DREG(EA),DREG(EAX),false); + gen_releasereg(DREG(EA)); + break; + /* MOV AX,direct addresses */ + case 0xa1: + gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0, + decode.big_addr ? decode_fetchd() : decode_fetchw()); + dyn_read_word(DREG(EA),DREG(EAX),decode.big_op); + gen_releasereg(DREG(EA)); + break; + /* MOV direct address,AL */ + case 0xa2: + gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0, + decode.big_addr ? decode_fetchd() : decode_fetchw()); + dyn_write_byte(DREG(EA),DREG(EAX),false); + gen_releasereg(DREG(EA)); + break; + /* MOV direct addresses,AX */ + case 0xa3: + gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0, + decode.big_addr ? decode_fetchd() : decode_fetchw()); + dyn_write_word(DREG(EA),DREG(EAX),decode.big_op); + gen_releasereg(DREG(EA)); + break; + /* TEST AL,AX Imm */ + case 0xa8:gen_dop_byte_imm(DOP_TEST,DREG(EAX),0,decode_fetchb());break; + case 0xa9:gen_dop_word_imm(DOP_TEST,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break; + + //Mov Byte reg,Imm byte + case 0xb0:case 0xb1:case 0xb2:case 0xb3:case 0xb4:case 0xb5:case 0xb6:case 0xb7: + gen_dop_byte_imm(DOP_MOV,&DynRegs[opcode&3],opcode&4,decode_fetchb()); + break; + //Mov word reg imm byte,word, + case 0xb8:case 0xb9:case 0xba:case 0xbb:case 0xbc:case 0xbd:case 0xbe:case 0xbf: + gen_dop_word_imm(DOP_MOV,decode.big_op,&DynRegs[opcode&7],decode.big_op ? decode_fetchd() : decode_fetchw());break; + break; + //GRP2 Eb/Ev,Ib + case 0xc0:dyn_grp2_eb(grp2_imm);break; + case 0xc1:dyn_grp2_ev(grp2_imm);break; + //RET near Iw / Ret + case 0xc2:dyn_ret_near(decode_fetchw());return decode.block; + case 0xc3:dyn_ret_near(0);return decode.block; + // MOV Eb/Ev,Ib/Iv + case 0xc6:dyn_mov_ebib();break; + case 0xc7:dyn_mov_eviv();break; + // LEAVE + case 0xc9:dyn_leave();break; + //RET far Iw / Ret + case 0xca:dyn_ret_far(decode_fetchw());return decode.block; + case 0xcb:dyn_ret_far(0);return decode.block; + /* Interrupt */ + case 0xcd:dyn_interrupt(decode_fetchb());return decode.block; + /* IRET */ + case 0xcF:dyn_iret();return decode.block; + //GRP2 Eb/Ev,1 + case 0xd0:dyn_grp2_eb(grp2_1);break; + case 0xd1:dyn_grp2_ev(grp2_1);break; + //GRP2 Eb/Ev,CL + case 0xd2:dyn_grp2_eb(grp2_cl);break; + case 0xd3:dyn_grp2_ev(grp2_cl);break; + //IN AL/AX,imm + case 0xe2:dyn_loop(LOOP_NONE);return decode.block; + case 0xe4:gen_call_function((void*)&IO_ReadB,"%Id%Rl",decode_fetchb(),DREG(EAX));break; + case 0xe5: + if (decode.big_op) { + gen_call_function((void*)&IO_ReadD,"%Id%Rd",decode_fetchb(),DREG(EAX)); + } else { + gen_call_function((void*)&IO_ReadW,"%Id%Rw",decode_fetchb(),DREG(EAX)); + } + break; + //OUT imm,AL + case 0xe6:gen_call_function((void*)&IO_WriteB,"%Id%Dl",decode_fetchb(),DREG(EAX));break; + case 0xe7: + if (decode.big_op) { + gen_call_function((void*)&IO_WriteD,"%Id%Dd",decode_fetchb(),DREG(EAX)); + } else { + gen_call_function((void*)&IO_WriteW,"%Id%Dw",decode_fetchb(),DREG(EAX)); + } + break; + case 0xe8: /* CALL Ivx */ + dyn_call_near_imm(); + return decode.block; + case 0xe9: /* Jmp Ivx */ + dyn_exit_link(decode.big_op,decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw()); + return decode.block; + /* CALL FAR Ip */ + case 0xea:dyn_jmp_far_imm();return decode.block; + /* Jmp Ibx */ + case 0xeb:dyn_exit_link(decode.big_op,(Bit8s)decode_fetchb());return decode.block; + /* IN AL/AX,DX*/ + case 0xec:gen_call_function((void*)&IO_ReadB,"%Dw%Rl",DREG(EDX),DREG(EAX));break; + case 0xed: + if (decode.big_op) { + gen_call_function((void*)&IO_ReadD,"%Dw%Rd",DREG(EDX),DREG(EAX)); + } else { + gen_call_function((void*)&IO_ReadW,"%Dw%Rw",DREG(EDX),DREG(EAX)); + } + break; + /* OUT DX,AL/AX */ + case 0xee:gen_call_function((void*)&IO_WriteB,"%Dw%Dl",DREG(EDX),DREG(EAX));break; + case 0xef: + if (decode.big_op) { + gen_call_function((void*)&IO_WriteD,"%Dw%Dd",DREG(EDX),DREG(EAX)); + } else { + gen_call_function((void*)&IO_WriteW,"%Dw%Dw",DREG(EDX),DREG(EAX)); + } + break; + /* Change carry flag */ + case 0xf5: //CMC + case 0xf8: //CLC + case 0xf9: //STC + cache_addb(opcode);break; + /* GRP 3 Eb/EV */ + case 0xf6:dyn_grp3_eb();break; + case 0xf7:dyn_grp3_ev();break; + /* Change interrupt flag */ + case 0xfa: //CLI + gen_storeflags(); + gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FLAG_IF); + gen_restoreflags(); + break; + case 0xfb: //STI + gen_storeflags(); + gen_dop_word_imm(DOP_OR,true,DREG(FLAGS),FLAG_IF); + gen_restoreflags(); + if (max_opcodes<=0) max_opcodes=1; //Allow 1 extra opcode + break; + /* GRP 4 Eb and callback's */ + case 0xfe: + dyn_get_modrm(); + switch (decode.modrm.reg) { + case 0x0://INC Eb + case 0x1://DEC Eb + if (decode.modrm.mod<3) { + dyn_fill_ea();dyn_read_byte(DREG(EA),DREG(TMPB),false); + gen_sop_byte(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,DREG(TMPB),0); + dyn_write_byte(DREG(EA),DREG(TMPB),false); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB)); + } else { + gen_sop_byte(decode.modrm.reg==0 ? SOP_INC : SOP_DEC, + &DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); + } + break; + case 0x7: //CALBACK Iw + gen_save_host_direct(&core_dyn.callback,decode_fetchw()); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_reduce_cycles(); + dyn_releaseregs(); + gen_return(BR_CallBack); + dyn_closeblock(BT_Normal); + return decode.block; + } + break; + case 0xff: + { + dyn_get_modrm();DynReg * src; + if (decode.modrm.mod<3) { + dyn_fill_ea(); + dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op); + src=DREG(TMPW); + } else src=&DynRegs[decode.modrm.rm]; + switch (decode.modrm.reg) { + case 0x0://INC Ev + case 0x1://DEC Ev + gen_sop_word(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,decode.big_op,src); + if (decode.modrm.mod<3){ + dyn_write_word(DREG(EA),DREG(TMPW),decode.big_op); + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPW)); + } + break; + case 0x2: /* CALL Ev */ + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_push(DREG(EIP)); + gen_dop_word(DOP_MOV,decode.big_op,DREG(EIP),src); + goto core_close_block; + case 0x4: /* JMP Ev */ + gen_dop_word(DOP_MOV,decode.big_op,DREG(EIP),src); + goto core_close_block; + case 0x3: /* CALL Ep */ + case 0x5: /* JMP Ep */ + dyn_save_flags(); + gen_lea(DREG(EA),DREG(EA),0,0,decode.big_op ? 4: 2); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.code-decode.code_start); + dyn_read_word(DREG(EA),DREG(EA),false); + for (Bitu i=0;i0xff) return 1; + return 0; +} + +static Bitu dyn_helper_idivb(Bit8s val) { + if (!val) return 1; + Bits quo=(Bit16s)reg_ax / val; + reg_ah=(Bit8s)((Bit16s)reg_ax % val); + reg_al=(Bit8s)quo; + if (quo!=(Bit8s)reg_al) return 1; + return 0; +} + +static Bitu dyn_helper_divw(Bit16u val) { + if (!val) return 1; + Bitu num=(reg_dx<<16)|reg_ax; + Bitu quo=num/val; + reg_dx=(Bit16u)(num % val); + reg_ax=(Bit16u)quo; + if (quo!=reg_ax) return 1; + return 0; +} + +static Bitu dyn_helper_idivw(Bit16s val) { + if (!val) return 1; + Bits num=(reg_dx<<16)|reg_ax; + Bits quo=num/val; + reg_dx=(Bit16s)(num % val); + reg_ax=(Bit16s)quo; + if (quo!=(Bit16s)reg_ax) return 1; + return 0; +} + +static Bitu dyn_helper_divd(Bit32u val) { + if (!val) return 1; + Bit64u num=(((Bit64u)reg_edx)<<32)|reg_eax; + Bit64u quo=num/val; + reg_edx=(Bit32u)(num % val); + reg_eax=(Bit32u)quo; + if (quo!=(Bit64u)reg_eax) return 1; + return 0; +} + +static Bitu dyn_helper_idivd(Bit32s val) { + if (!val) return 1; + Bit64s num=(((Bit64u)reg_edx)<<32)|reg_eax; + Bit64s quo=num/val; + reg_edx=(Bit32s)(num % val); + reg_eax=(Bit32s)(quo); + if (quo!=(Bit64s)((Bit32s)reg_eax)) return 1; + return 0; +} diff --git a/src/cpu/core_dyn_x86/risc_x86.h b/src/cpu/core_dyn_x86/risc_x86.h new file mode 100644 index 00000000..8d149ba5 --- /dev/null +++ b/src/cpu/core_dyn_x86/risc_x86.h @@ -0,0 +1,719 @@ +#define GEN_HAS_IMM 1 + +static void gen_init(void); + +/* End of needed */ + +#define X86_REGS 7 +#define X86_REG_EAX 0x00 +#define X86_REG_ECX 0x01 +#define X86_REG_EDX 0x02 +#define X86_REG_EBX 0x03 +#define X86_REG_EBP 0x04 +#define X86_REG_ESI 0x05 +#define X86_REG_EDI 0x06 + +#define X86_REG_MASK(_REG_) (1 << X86_REG_ ## _REG_) + +static struct { + Bitu last_used; + Bitu stored_flags; + GenReg * regs[X86_REGS]; +} x86gen; + +class GenReg { +public: + GenReg(Bit8u _index,bool _protect) { + index=_index;protect=_protect; + notusable=false;dynreg=0; + } + DynReg * dynreg; + Bitu last_used; //Keeps track of last assigned regs + Bit8u index; + bool notusable; + bool protect; + void Load(DynReg * _dynreg) { + if (!_dynreg) return; + if (dynreg) Clear(); + dynreg=_dynreg; + last_used=x86gen.last_used; + dynreg->flags&=~DYNFLG_CHANGED; + dynreg->genreg=this; + if (dynreg->flags & (DYNFLG_LOAD|DYNFLG_LOADONCE)) { + dynreg->flags&=~DYNFLG_LOADONCE; + cache_addw(0x058b+(index << (8+3))); //Mov reg,[data] + cache_addd((Bit32u)dynreg->data); + } + } + void Save(void) { + if (!dynreg) IllegalOption(); + dynreg->flags&=~DYNFLG_CHANGED; + cache_addw(0x0589+(index << (8+3))); //Mov [data],reg + cache_addd((Bit32u)dynreg->data); + } + void Release(void) { + if (!dynreg) return; + if (dynreg->flags&DYNFLG_CHANGED && dynreg->flags&DYNFLG_SAVE) { + Save(); + } + dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_LOADONCE); + dynreg->genreg=0;dynreg=0; + } + void Clear(void) { + if (!dynreg) return; + if (dynreg->flags&DYNFLG_CHANGED) { + dynreg->flags|=DYNFLG_LOADONCE; + Save(); + } + dynreg->genreg=0;dynreg=0; + } + + +}; + +static Bit32u kut=10; +static BlockReturn gen_runcode(Bit8u * code) { + BlockReturn retval; + #define NEW_MASK $FMASK_TEST + __asm__ volatile ( + + "pushfl \n" + "movl %1,%%ebx \n" + "andl %2,%%ebx \n" + "popl %%ecx \n" + "andl %3,%%ecx \n" + "orl %%ebx,%%ecx \n" + "pushl %%ecx \n" + "popfl \n" + "call %4 \n" + "pushfl \n" + "movl %1,%%ebx \n" + "andl %3,%%ebx \n" + "popl %%ecx \n" + "andl %2,%%ecx \n" + "orl %%ecx,%%ebx \n" + "movl %%ebx,%1 \n" + :"=a" (retval) + :"m" (reg_flags), "n" (FMASK_TEST),"n" (~FMASK_TEST),"m" (code) + :"%ecx","%edx","%ebx","%ebp","%edi","%esi","cc","memory" + ); +#if 0 +/* Prepare the flags */ + pushfd + mov ebx,[reg_flags] + and ebx,FMASK_TEST + pop ecx + and ecx,~FMASK_TEST + or ecx,ebx + push ecx + popfd + call dword ptr [code]; +/* Restore the flags */ + pushfd + mov ebx,[reg_flags] + and ebx,~FMASK_TEST + pop ecx + and ecx,FMASK_TEST + or ebx,ecx + mov [reg_flags],ebx + pop edi + pop esi + pop ebp + pop ebx + mov [retval],eax + } +#endif + return retval; +} + + + + + + +static GenReg * FindDynReg(DynReg * dynreg) { + x86gen.last_used++; + if (dynreg->genreg) { + dynreg->genreg->last_used=x86gen.last_used; + return dynreg->genreg; + } + /* Find best match for selected global reg */ + Bits i; + Bits first_used,first_index; + first_used=-1; + if (dynreg->flags & DYNFLG_HAS8) { + /* Has to be eax,ebx,ecx,edx */ + for (i=first_index=0;i<=X86_REG_EDX;i++) { + GenReg * genreg=x86gen.regs[i]; + if (genreg->notusable) continue; + if (!(genreg->dynreg)) { + genreg->Load(dynreg); + return genreg; + } + if (genreg->last_usedlast_used; + first_index=i; + } + } + /* No free register found use earliest assigned one */ + GenReg * newreg=x86gen.regs[first_index]; + newreg->Load(dynreg); + return newreg; + } else { + for (i=first_index=X86_REGS-1;i>=0;i--) { + GenReg * genreg=x86gen.regs[i]; + if (genreg->notusable) continue; + if (!(genreg->dynreg)) { + genreg->Load(dynreg); + return genreg; + } + if (genreg->last_usedlast_used; + first_index=i; + } + } + /* No free register found use earliest assigned one */ + GenReg * newreg=x86gen.regs[first_index]; + newreg->Load(dynreg); + return newreg; + } +} + +static GenReg * ForceDynReg(GenReg * genreg,DynReg * dynreg) { + genreg->last_used=++x86gen.last_used; + if (dynreg->genreg==genreg) return genreg; + if (genreg->dynreg) genreg->Clear(); + if (dynreg->genreg) dynreg->genreg->Clear(); + genreg->Load(dynreg); + return genreg; +} +static void gen_releasereg(DynReg * dynreg) { + GenReg * genreg=dynreg->genreg; + if (genreg) genreg->Release(); + else dynreg->flags&=~(DYNFLG_LOADONCE|DYNFLG_CHANGED); +} + +static void gen_setupreg(DynReg * dnew,DynReg * dsetup) { + dnew->flags=dsetup->flags; + if (dnew->genreg==dsetup->genreg) return; + /* Not the same genreg must be wrong */ + if (dnew->genreg) { + /* Check if the genreg i'm changing is actually linked to me */ + if (dnew->genreg->dynreg==dnew) dnew->genreg->dynreg=0; + } + dnew->genreg=dsetup->genreg; + if (dnew->genreg) dnew->genreg->dynreg=dnew; +} + +static void gen_synchreg(DynReg * dnew,DynReg * dsynch) { + /* First make sure the registers match */ + if (dnew->genreg!=dsynch->genreg) { + if (dnew->genreg) dnew->genreg->Clear(); + if (dsynch->genreg) { + dsynch->genreg->Load(dnew); + } + } + /* Always use the loadonce flag from either state */ + dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_LOADONCE); + if ((dnew->flags ^ dsynch->flags) & DYNFLG_CHANGED) { + /* Ensure the changed value gets saved */ + if (dnew->flags & DYNFLG_CHANGED) { + dnew->genreg->Save(); + } + } +} + +static void gen_storeflags(void) { + if (!x86gen.stored_flags) { + cache_addb(0x9c); //PUSHFD + } + x86gen.stored_flags++; +} + +static void gen_restoreflags(bool noreduce=false) { + if (noreduce) { + cache_addb(0x9d); + return; + } + if (x86gen.stored_flags) { + x86gen.stored_flags--; + if (!x86gen.stored_flags) + cache_addb(0x9d); //POPFD + } else IllegalOption(); +} + +static void gen_reinit(void) { + x86gen.last_used=0; + x86gen.stored_flags=0; + for (Bitu i=0;idynreg=0; + } +} + +static void gen_dop_byte(DualOps op,DynReg * dr1,Bit8u di1,DynReg * dr2,Bit8u di2) { + GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2); + switch (op) { + case DOP_ADD:cache_addb(0x02);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_OR: cache_addb(0x0a);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_ADC:cache_addb(0x12);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_SBB:cache_addb(0x1a);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_AND:cache_addb(0x22);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_SUB:cache_addb(0x2a);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_XOR:cache_addb(0x32);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_CMP:cache_addb(0x3a);break; + case DOP_MOV:cache_addb(0x8a);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_XCHG:cache_addb(0x86);dr1->flags|=DYNFLG_CHANGED;dr2->flags|=DYNFLG_CHANGED;break; + case DOP_TEST:cache_addb(0x84);break; + default: + IllegalOption(); + } + cache_addb(0xc0+((gr1->index+di1)<<3)+gr2->index+di2); +} + +static void gen_dop_byte_imm(DualOps op,DynReg * dr1,Bit8u di1,Bitu imm) { + GenReg * gr1=FindDynReg(dr1); + switch (op) { + case DOP_ADD: + cache_addw(0xc080+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_OR: + cache_addw(0xc880+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_ADC: + cache_addw(0xd080+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_SBB: + cache_addw(0xd880+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_AND: + cache_addw(0xe080+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_SUB: + cache_addw(0xe880+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_XOR: + cache_addw(0xf080+((gr1->index+di1)<<8)); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_CMP: + cache_addw(0xf880+((gr1->index+di1)<<8)); + break;//Doesn't change + case DOP_MOV: + cache_addb(0xb0+gr1->index+di1); + dr1->flags|=DYNFLG_CHANGED; + break; + case DOP_TEST: + cache_addw(0xc0f6+((gr1->index+di1)<<8)); + break;//Doesn't change + default: + IllegalOption(); + } + cache_addb(imm); +} + +static void gen_sop_byte(SingleOps op,DynReg * dr1,Bit8u di1) { + GenReg * gr1=FindDynReg(dr1); + switch (op) { + case SOP_INC:cache_addw(0xc0FE + ((gr1->index+di1)<<8));break; + case SOP_DEC:cache_addw(0xc8FE + ((gr1->index+di1)<<8));break; + case SOP_NOT:cache_addw(0xd0f6 + ((gr1->index+di1)<<8));break; + case SOP_NEG:cache_addw(0xd8f6 + ((gr1->index+di1)<<8));break; + default: + IllegalOption(); + } + dr1->flags|=DYNFLG_CHANGED; +} + + +static void gen_extend_word(bool sign,DynReg * ddr,DynReg * dsr) { + GenReg * gdr=FindDynReg(ddr);GenReg * gsr=FindDynReg(dsr); + if (sign) cache_addw(0xbf0f); + else cache_addw(0xb70f); + cache_addb(0xc0+(gdr->index<<3)+(gsr->index)); + ddr->flags|=DYNFLG_CHANGED; +} + +static void gen_extend_byte(bool sign,bool dword,DynReg * ddr,DynReg * dsr,Bit8u dsi) { + GenReg * gdr=FindDynReg(ddr);GenReg * gsr=FindDynReg(dsr); + if (!dword) cache_addb(0x66); + if (sign) cache_addw(0xbe0f); + else cache_addw(0xb60f); + cache_addb(0xc0+(gdr->index<<3)+(gsr->index+dsi)); + ddr->flags|=DYNFLG_CHANGED; +} + +static void gen_lea(DynReg * ddr,DynReg * dsr1,DynReg * dsr2,Bitu scale,Bits imm) { + GenReg * gdr=FindDynReg(ddr); + Bitu imm_size; + Bit8u rm_base=(gdr->index << 3); + if (dsr1) { + GenReg * gsr1=FindDynReg(dsr1); + if (!imm && (gsr1->index!=0x5)) { + imm_size=0; rm_base+=0x0; //no imm + } else if ((imm>=-128 && imm<=127)) { + imm_size=1;rm_base+=0x40; //Signed byte imm + } else { + imm_size=4;rm_base+=0x80; //Signed dword imm + } + if (dsr2) { + GenReg * gsr2=FindDynReg(dsr2); + cache_addb(0x8d); //LEA + cache_addb(rm_base+0x4); //The sib indicator + Bit8u sib=(gsr1->index)+(gsr2->index<<3)+(scale<<6); + cache_addb(sib); + } else { + cache_addb(0x8d); //LEA + cache_addb(rm_base+gsr1->index); + } + } else { + if (dsr2) { + GenReg * gsr2=FindDynReg(dsr2); + cache_addb(0x8d); //LEA + cache_addb(rm_base+0x4); //The sib indicator + Bit8u sib=(5+(gsr2->index<<3)+(scale<<6)); + cache_addb(sib); + imm_size=4; + } else { + cache_addb(0x8d); //LEA + cache_addb(rm_base+0x05); //dword imm + imm_size=4; + } + } + switch (imm_size) { + case 0: break; + case 1:cache_addb(imm);break; + case 4:cache_addd(imm);break; + } + ddr->flags|=DYNFLG_CHANGED; +} + +static void gen_dop_word(DualOps op,bool dword,DynReg * dr1,DynReg * dr2) { + GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2); + if (!dword) cache_addb(0x66); + switch (op) { + case DOP_ADD:cache_addb(0x03);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_OR: cache_addb(0x0b);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_ADC:cache_addb(0x13);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_SBB:cache_addb(0x1b);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_AND:cache_addb(0x23);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_SUB:cache_addb(0x2b);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_XOR:cache_addb(0x33);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_CMP:cache_addb(0x3b);break; + case DOP_MOV:cache_addb(0x8b);dr1->flags|=DYNFLG_CHANGED;break; + case DOP_XCHG:cache_addb(0x87);dr1->flags|=DYNFLG_CHANGED;dr2->flags|=DYNFLG_CHANGED;break; + case DOP_TEST:cache_addb(0x85);break; + default: + IllegalOption(); + } + cache_addb(0xc0+(gr1->index<<3)+gr2->index); +} + +static void gen_dop_word_imm(DualOps op,bool dword,DynReg * dr1,Bits imm) { + GenReg * gr1=FindDynReg(dr1); + if (!dword) cache_addb(0x66); + switch (op) { + case DOP_ADD:cache_addw(0xc081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_OR: cache_addw(0xc881+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_ADC:cache_addw(0xd081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_SBB:cache_addw(0xd881+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_AND:cache_addw(0xe081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_SUB:cache_addw(0xe881+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_XOR:cache_addw(0xf081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_CMP:cache_addw(0xf881+(gr1->index<<8));break;//Doesn't change + case DOP_MOV:cache_addb(0xb8+(gr1->index));dr1->flags|=DYNFLG_CHANGED;break; + case DOP_TEST:cache_addw(0xc0f7+(gr1->index<<8));break;//Doesn't change + default: + IllegalOption(); + } + if (dword) cache_addd(imm); + else cache_addw(imm); +} +static void gen_sop_word(SingleOps op,bool dword,DynReg * dr1) { + GenReg * gr1=FindDynReg(dr1); + if (!dword) cache_addb(0x66); + switch (op) { + case SOP_INC:cache_addb(0x40+gr1->index);break; + case SOP_DEC:cache_addb(0x48+gr1->index);break; + case SOP_NOT:cache_addw(0xd0f7+(gr1->index<<8));break; + case SOP_NEG:cache_addw(0xd8f7+(gr1->index<<8));break; + default: + IllegalOption(); + } + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_shift_byte(ShiftOps op,DynReg * drecx,DynReg * dr1,Bit8u di1) { + ForceDynReg(x86gen.regs[X86_REG_ECX],drecx); + GenReg * gr1=FindDynReg(dr1); + switch (op) { + case SHIFT_ROL:cache_addw(0xc0d2+((gr1->index+di1)<<8));break; + case SHIFT_ROR:cache_addw(0xc8d2+((gr1->index+di1)<<8));break; + case SHIFT_RCL:cache_addw(0xd0d2+((gr1->index+di1)<<8));break; + case SHIFT_RCR:cache_addw(0xd8d2+((gr1->index+di1)<<8));break; + case SHIFT_SHL:cache_addw(0xe0d2+((gr1->index+di1)<<8));break; + case SHIFT_SHR:cache_addw(0xe8d2+((gr1->index+di1)<<8));break; + case SHIFT_SAR:cache_addw(0xf8d2+((gr1->index+di1)<<8));break; + default: + IllegalOption(); + } + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_shift_word(ShiftOps op,DynReg * drecx,bool dword,DynReg * dr1) { + ForceDynReg(x86gen.regs[X86_REG_ECX],drecx); + GenReg * gr1=FindDynReg(dr1); + if (!dword) cache_addb(0x66); + switch (op) { + case SHIFT_ROL:cache_addw(0xc0d3+((gr1->index)<<8));break; + case SHIFT_ROR:cache_addw(0xc8d3+((gr1->index)<<8));break; + case SHIFT_RCL:cache_addw(0xd0d3+((gr1->index)<<8));break; + case SHIFT_RCR:cache_addw(0xd8d3+((gr1->index)<<8));break; + case SHIFT_SHL:cache_addw(0xe0d3+((gr1->index)<<8));break; + case SHIFT_SHR:cache_addw(0xe8d3+((gr1->index)<<8));break; + case SHIFT_SAR:cache_addw(0xf8d3+((gr1->index)<<8));break; + default: + IllegalOption(); + } + dr1->flags|=DYNFLG_CHANGED; +} + + +static void gen_mul_byte(bool imul,DynReg * dyn_ax,DynReg * dr1,Bit8u di1) { + ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax); + GenReg * gr1=FindDynReg(dr1); + if (imul) cache_addw(0xe8f6+((gr1->index+di1)<<8)); + else cache_addw(0xe0f6+((gr1->index+di1)<<8)); + dyn_ax->flags|=DYNFLG_CHANGED; +} + +static void gen_mul_word(bool imul,DynReg * dyn_ax,DynReg * dyn_dx,bool dword,DynReg * dr1) { + ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax); + ForceDynReg(x86gen.regs[X86_REG_EDX],dyn_dx); + GenReg * gr1=FindDynReg(dr1); + if (!dword) cache_addb(0x66); + if (imul) cache_addw(0xe8f7+(gr1->index<<8)); + else cache_addw(0xe0f7+(gr1->index<<8)); + dyn_ax->flags|=DYNFLG_CHANGED; + dyn_dx->flags|=DYNFLG_CHANGED; +} + +static void gen_dshift_imm(bool dword,bool left,DynReg * dr1,DynReg * dr2,Bitu imm) { + GenReg * gr1=FindDynReg(dr1); + GenReg * gr2=FindDynReg(dr2); + if (!dword) cache_addb(0x66); + if (left) cache_addw(0xa40f); //SHLD IMM + else cache_addw(0xac0f); //SHRD IMM + cache_addb(0xc0+gr1->index+(gr2->index<<8)); + cache_addb(imm); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_dshift_cl(bool dword,bool left,DynReg * dr1,DynReg * dr2,DynReg * drecx) { + GenReg * gr1=FindDynReg(dr1); + GenReg * gr2=FindDynReg(dr2); + ForceDynReg(x86gen.regs[X86_REG_ECX],drecx); + if (!dword) cache_addb(0x66); + if (left) cache_addw(0xa50f); //SHLD CL + else cache_addw(0xad0f); //SHRD CL + cache_addb(0xc0+gr1->index+(gr2->index<<8)); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_call_function(void * func,char * ops,...) { + Bits paramcount=0; + struct ParamInfo { + char * line; + Bitu value; + } pinfo[32]; + ParamInfo * retparam=0; + /* Clear the EAX Genreg for usage */ + x86gen.regs[X86_REG_EAX]->Clear(); + x86gen.regs[X86_REG_EAX]->notusable=true;; + /* Save the flags */ + gen_storeflags(); + /* Scan for the amount of params */ + if (ops) { + va_list params; + va_start(params,ops); + Bits pindex=0; + while (*ops) { + if (*ops=='%') { + pinfo[pindex].line=ops+1; + pinfo[pindex].value=va_arg(params,Bitu); + pindex++; + } + ops++; + } + paramcount=0; + while (pindex) { + pindex--; + char * scan=pinfo[pindex].line; + switch (*scan++) { + case 'I': /* immediate value */ + paramcount++; + cache_addb(0x68); //Push immediate + cache_addd(pinfo[pindex].value); //Push value + break; + case 'D': /* Dynamic register */ + { + bool release=false; + paramcount++; + DynReg * dynreg=(DynReg *)pinfo[pindex].value; + GenReg * genreg=FindDynReg(dynreg); + scanagain: + switch (*scan++) { + case 'd': + cache_addb(0x50+genreg->index); //Push reg + break; + case 'w': + cache_addw(0xb70f); //MOVZX EAX,reg + cache_addb(0xc0+genreg->index); + cache_addb(0x50); //Push EAX + break; + case 'l': + cache_addw(0xb60f); //MOVZX EAX,reg[0] + cache_addb(0xc0+genreg->index); + cache_addb(0x50); //Push EAX + break; + case 'h': + cache_addw(0xb60f); //MOVZX EAX,reg[1] + cache_addb(0xc4+genreg->index); + cache_addb(0x50); //Push EAX + break; + case 'r': /* release the reg afterwards */ + release=true; + goto scanagain; + default: + IllegalOption(); + } + if (release) gen_releasereg(dynreg); + } + break; + case 'R': /* Dynamic register to get the return value */ + retparam =&pinfo[pindex]; + pinfo[pindex].line=scan; + break; + default: + IllegalOption(); + } + } + } + /* Clear some unprotected registers */ + x86gen.regs[X86_REG_ECX]->Clear(); + x86gen.regs[X86_REG_EDX]->Clear(); + /* Do the actual call to the procedure */ + cache_addb(0xe8); + cache_addd((Bit32u)func - (Bit32u)cache.pos-4); + /* Restore the params of the stack */ + if (paramcount) { + cache_addw(0xc483); //add ESP,imm byte + cache_addb(paramcount*4); + } + /* Save the return value in correct register */ + if (retparam) { + DynReg * dynreg=(DynReg *)retparam->value; + GenReg * genreg=FindDynReg(dynreg); + switch (*retparam->line) { + case 'd': + cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax + break; + case 'w': + cache_addb(0x66); + cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax + break; + case 'l': + cache_addw(0xc08a+(genreg->index <<(8+3))); //mov reg,eax + break; + case 'h': + cache_addw(0xc08a+((genreg->index+4) <<(8+3))); //mov reg,eax + break; + } + dynreg->flags|=DYNFLG_CHANGED; + } + gen_restoreflags(); + /* Restore EAX registers to be used again */ + x86gen.regs[X86_REG_EAX]->notusable=false; +} + +static Bit8u * gen_create_branch(BranchTypes type) { + /* First free all registers */ + cache_addb(0x70+type); + cache_addb(0); + return (cache.pos-1); +} + +static void gen_fill_branch(Bit8u * data) { + *data=(cache.pos-data-1); +} + +static void gen_jmp_ptr(void * ptr,Bits imm=0) { + cache_addb(0xa1); + cache_addd((Bit32u)ptr); + cache_addb(0xff); //JMP EA + if (!imm) { //NO EBP + cache_addb(0x20); + } else if ((imm>=-128 && imm<=127)) { + cache_addb(0x60); + cache_addb(imm); + } else { + cache_addb(0xa0); + cache_addd(imm); + } +} + +static void gen_save_flags(DynReg * dynreg,bool stored) { + GenReg * genreg=FindDynReg(dynreg); + if (!stored) cache_addb(0x9c); //Pushfd + cache_addb(0x58+genreg->index); //POP 32 REG + dynreg->flags|=DYNFLG_CHANGED; +} + +static void gen_load_flags(DynReg * dynreg) { + GenReg * genreg=FindDynReg(dynreg); + cache_addb(0x50+genreg->index); //PUSH 32 + cache_addb(0x9d); //POPFD +} + +static void gen_save_host_direct(void * data,Bits imm) { + cache_addw(0x05c7); //MOV [],dword + cache_addd((Bit32u)data); + cache_addd(imm); +} + +static void gen_load_host(void * data,DynReg * dr1,Bitu size) { + GenReg * gr1=FindDynReg(dr1); + switch (size) { + case 1:cache_addw(0xb60f);break; //movzx byte + case 2:cache_addw(0xb70f);break; //movzx word + case 4:cache_addb(0x8b);break; //mov + default: + IllegalOption(); + } + cache_addb(0x5+(gr1->index<<3)); + cache_addd((Bit32u)data); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_return(BlockReturn retcode) { + cache_addb(0xb8); + cache_addd(retcode); + cache_addb(0xc3); +} + +static void gen_init(void) { + x86gen.regs[X86_REG_EAX]=new GenReg(0,false); + x86gen.regs[X86_REG_ECX]=new GenReg(1,false); + x86gen.regs[X86_REG_EDX]=new GenReg(2,false); + x86gen.regs[X86_REG_EBX]=new GenReg(3,true); + x86gen.regs[X86_REG_EBP]=new GenReg(5,true); + x86gen.regs[X86_REG_ESI]=new GenReg(6,true); + x86gen.regs[X86_REG_EDI]=new GenReg(7,true); +} + + diff --git a/src/cpu/core_dyn_x86/string.h b/src/cpu/core_dyn_x86/string.h new file mode 100644 index 00000000..e69de29b