From f65e8e5f09417c48476deee2bb7ebb54223e634b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Strohh=C3=A4cker?= Date: Sat, 2 Jun 2007 11:47:06 +0000 Subject: [PATCH] add recompiling core that uses highlevel function calls and the existing lazyflags system for better portability; thanks to gulikoza for the x86_64 backend Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@2865 --- configure.in | 1 + include/cpu.h | 2 + src/cpu/Makefile.am | 4 +- src/cpu/core_dynrec.cpp | 314 +++++ src/cpu/core_dynrec/Makefile.am | 2 + src/cpu/core_dynrec/cache.h | 633 +++++++++ src/cpu/core_dynrec/decoder.h | 582 ++++++++ src/cpu/core_dynrec/decoder_basic.h | 1091 +++++++++++++++ src/cpu/core_dynrec/decoder_opcodes.h | 1415 +++++++++++++++++++ src/cpu/core_dynrec/dyn_fpu.h | 679 +++++++++ src/cpu/core_dynrec/operators.h | 1812 +++++++++++++++++++++++++ src/cpu/core_dynrec/risc_x64.h | 631 +++++++++ src/cpu/core_dynrec/risc_x86.h | 427 ++++++ src/cpu/cpu.cpp | 16 +- visualc_net/dosbox.vcproj | 31 + 15 files changed, 7637 insertions(+), 3 deletions(-) create mode 100644 src/cpu/core_dynrec.cpp create mode 100644 src/cpu/core_dynrec/Makefile.am create mode 100644 src/cpu/core_dynrec/cache.h create mode 100644 src/cpu/core_dynrec/decoder.h create mode 100644 src/cpu/core_dynrec/decoder_basic.h create mode 100644 src/cpu/core_dynrec/decoder_opcodes.h create mode 100644 src/cpu/core_dynrec/dyn_fpu.h create mode 100644 src/cpu/core_dynrec/operators.h create mode 100644 src/cpu/core_dynrec/risc_x64.h create mode 100644 src/cpu/core_dynrec/risc_x86.h diff --git a/configure.in b/configure.in index 722ba7bb..39207092 100644 --- a/configure.in +++ b/configure.in @@ -435,6 +435,7 @@ src/cpu/Makefile src/cpu/core_full/Makefile src/cpu/core_normal/Makefile src/cpu/core_dyn_x86/Makefile +src/cpu/core_dynrec/Makefile src/debug/Makefile src/dos/Makefile src/fpu/Makefile diff --git a/include/cpu.h b/include/cpu.h index 430e1821..fd868341 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -58,6 +58,8 @@ Bits CPU_Core_Simple_Run(void); Bits CPU_Core_Full_Run(void); Bits CPU_Core_Dyn_X86_Run(void); Bits CPU_Core_Dyn_X86_Trap_Run(void); +Bits CPU_Core_Dynrec_Run(void); +Bits CPU_Core_Dynrec_Trap_Run(void); //CPU Stuff diff --git a/src/cpu/Makefile.am b/src/cpu/Makefile.am index 62cc988d..0d6c542f 100644 --- a/src/cpu/Makefile.am +++ b/src/cpu/Makefile.am @@ -1,7 +1,7 @@ -SUBDIRS = core_full core_normal core_dyn_x86 +SUBDIRS = core_full core_normal core_dyn_x86 core_dynrec 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 core_simple.cpp \ - core_dyn_x86.cpp \ No newline at end of file + core_dyn_x86.cpp core_dynrec.cpp diff --git a/src/cpu/core_dynrec.cpp b/src/cpu/core_dynrec.cpp new file mode 100644 index 00000000..e73b8b2b --- /dev/null +++ b/src/cpu/core_dynrec.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" + +#if (C_DYNREC) + +#include +#include +#include +#include +#include +#include + +#if (C_HAVE_MPROTECT) +#include + +#include +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif +#endif /* C_HAVE_MPROTECT */ + +#include "callback.h" +#include "regs.h" +#include "mem.h" +#include "cpu.h" +#include "debug.h" +#include "paging.h" +#include "inout.h" + +#define CACHE_MAXSIZE (4096) +#define CACHE_TOTAL (1024*1024*8) +#define CACHE_PAGES (512) +#define CACHE_BLOCKS (128*1024) +#define CACHE_ALIGN (16) +#define DYN_HASH_SHIFT (4) +#define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT) +#define DYN_LINKS (16) + +#if 0 +#define DYN_LOG LOG_MSG +#else +#define DYN_LOG +#endif + +#if C_FPU +#define CPU_FPU 1 //Enable FPU escape instructions +#endif + + +// the emulated x86 registers +#define DRC_REG_EAX 0 +#define DRC_REG_ECX 1 +#define DRC_REG_EDX 2 +#define DRC_REG_EBX 3 +#define DRC_REG_ESP 4 +#define DRC_REG_EBP 5 +#define DRC_REG_ESI 6 +#define DRC_REG_EDI 7 + +// the emulated x86 segment registers +#define DRC_SEG_ES 0 +#define DRC_SEG_CS 1 +#define DRC_SEG_SS 2 +#define DRC_SEG_DS 3 +#define DRC_SEG_FS 4 +#define DRC_SEG_GS 5 + + +// access to a general register +#define DRCD_REG(reg) (&cpu_regs.regs[reg].dword) +// access to a segment register +#define DRCD_SEG_VAL(seg) (&Segs.val[seg]) +// access to the physical value of a segment register/selector +#define DRCD_SEG_PHYS(seg) (&Segs.phys[seg]) + +// access to an 8bit general register +#define DRCD_REG_BYTE(reg,idx) (&cpu_regs.regs[reg].byte[idx]) +// access to 16/32bit general registers +#define DRCD_REG_WORD(reg,dwrd) ((dwrd)?((void*)(&cpu_regs.regs[reg].dword)):((void*)(&cpu_regs.regs[reg].word))) + + +enum BlockReturn { + BR_Normal=0, + BR_Cycles, + BR_Link1,BR_Link2, + BR_Opcode, +#if (C_DEBUG) + BR_OpcodeFull, +#endif + BR_Iret, + BR_CallBack, + BR_SMCBlock +}; + +// identificator to signal self-modification of the currently executed block +#define SMC_CURRENT_BLOCK 0xffff + + +static void IllegalOptionDynrec(const char* msg) { + E_Exit("DynrecCore: illegal option in %s",msg); +} + +static struct { + BlockReturn (*runcode)(Bit8u*); // points to code that can start a block + Bitu callback; // the occurred callback + Bitu readdata; // spare space used when reading from memory + Bit32u protected_regs[8]; // space to save/restore register values +} core_dynrec; + + +#include "core_dynrec/cache.h" + +#define X86 0x01 +#define X86_64 0x02 + +#if C_TARGETCPU == X86_64 +#include "core_dynrec/risc_x64.h" +#elif C_TARGETCPU == X86 +#include "core_dynrec/risc_x86.h" +#endif + +#include "core_dynrec/decoder.h" + +CacheBlockDynRec * LinkBlocks(BlockReturn ret) { + CacheBlockDynRec * block=NULL; + // the last instruction was a control flow modifying instruction + Bitu temp_ip=SegPhys(cs)+reg_eip; + Bitu temp_page=temp_ip >> 12; + CodePageHandlerDynRec * temp_handler=(CodePageHandlerDynRec *)paging.tlb.handler[temp_page]; + if (temp_handler->flags & PFLAG_HASCODE) { + // see if the target is an already translated block + block=temp_handler->FindCacheBlock(temp_ip & 4095); + if (!block) return NULL; + + // found it, link the current block to + cache.block.running->LinkTo(ret==BR_Link2,block); + return block; + } + return NULL; +} + +/* + The core tries to find the block that should be executed next. + If such a block is found, it is run, otherwise the instruction + stream starting at ip_point is translated (see decoder.h) and + makes up a new code block that will be run. + When control is returned to CPU_Core_Dynrec_Run (which might + be right after the block is run, or somewhen long after that + due to the direct cacheblock linking) the returncode decides + the next action. This might be continuing the translation and + execution process, or returning from the core etc. +*/ + +Bits CPU_Core_Dynrec_Run(void) { + for (;;) { + // Determine the linear address of CS:EIP + PhysPt ip_point=SegPhys(cs)+reg_eip; + #if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; + #endif + + CodePageHandlerDynRec * chandler=0; + // see if the current page is present and contains code + if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { + // page not present, throw the exception + CPU_Exception(cpu.exception.which,cpu.exception.error); + continue; + } + + // page doesn't contain code or is special + if (GCC_UNLIKELY(!chandler)) return CPU_Core_Normal_Run(); + + // find correct Dynamic Block to run + CacheBlockDynRec * block=chandler->FindCacheBlock(ip_point&4095); + if (!block) { + // no block found, thus translate the instruction stream + // unless the instruction is known to be modified + if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) { + // translate up to 32 instructions + block=CreateCacheBlock(chandler,ip_point,32); + } else { + // let the normal core handle this instruction to avoid zero-sized blocks + Bitu old_cycles=CPU_Cycles; + CPU_Cycles=1; + Bits nc_retcode=CPU_Core_Normal_Run(); + if (!nc_retcode) { + CPU_Cycles=old_cycles-1; + continue; + } + CPU_CycleLeft+=old_cycles; + return nc_retcode; + } + } + +run_block: + cache.block.running=0; + // now we're ready to run the dynamic code block +// BlockReturn ret=((BlockReturn (*)(void))(block->cache.start))(); + BlockReturn ret=core_dynrec.runcode(block->cache.start); + + switch (ret) { + case BR_Iret: +#if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif + if (!GETFLAG(TF)) break; + // trapflag is set, switch to the trap-aware decoder + cpudecoder=CPU_Core_Dynrec_Trap_Run; + return CBRET_NONE; + + case BR_Normal: + // the block was exited due to a non-predictable control flow + // modifying instruction (like ret) or some nontrivial cpu state + // changing instruction (for example switch to/from pmode), + // or the maximal number of instructions to translate was reached +#if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif + break; + + case BR_Cycles: + // cycles went negative, return from the core to handle + // external events, schedule the pic... +#if C_HEAVY_DEBUG + if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif + return CBRET_NONE; + + case BR_CallBack: + // the callback code is executed in dosbox.conf, return the callback number + FillFlags(); + return core_dynrec.callback; + + case BR_SMCBlock: +// LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip); + cpu.exception.which=0; + // fallthrough, let the normal core handle the block-modifying instruction + case BR_Opcode: + // some instruction has been encountered that could not be translated + // (thus it is not part of the code block), the normal core will + // handle this instruction + CPU_CycleLeft+=CPU_Cycles; + CPU_Cycles=1; + return CPU_Core_Normal_Run(); + +#if (C_DEBUG) + case BR_OpcodeFull: + CPU_CycleLeft+=CPU_Cycles; + CPU_Cycles=1; + return CPU_Core_Full_Run(); +#endif + + case BR_Link1: + case BR_Link2: + block=LinkBlocks(ret); + if (block) goto run_block; + break; + + default: + E_Exit("Invalid return code %d", ret); + } + } + return CBRET_NONE; +} + +Bits CPU_Core_Dynrec_Trap_Run(void) { + Bits oldCycles = CPU_Cycles; + CPU_Cycles = 1; + cpu.trap_skip = false; + + // let the normal core execute the next (only one!) instruction + Bits ret=CPU_Core_Normal_Run(); + + // trap to int1 unless the last instruction deferred this + // (allows hardware interrupts to be served without interaction) + if (!cpu.trap_skip) CPU_HW_Interrupt(1); + + CPU_Cycles = oldCycles-1; + // continue (either the trapflag was clear anyways, or the int1 cleared it) + cpudecoder = &CPU_Core_Dynrec_Run; + + return ret; +} + +void CPU_Core_Dynrec_Init(void) { +} + +void CPU_Core_Dynrec_Cache_Init(bool enable_cache) { + // Initialize code cache and dynamic blocks + cache_init(enable_cache); +} + +void CPU_Core_Dynrec_Cache_Close(void) { + cache_close(); +} + +#endif diff --git a/src/cpu/core_dynrec/Makefile.am b/src/cpu/core_dynrec/Makefile.am new file mode 100644 index 00000000..ad75110f --- /dev/null +++ b/src/cpu/core_dynrec/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = cache.h decoder.h decoder_basic.h decoder_opcodes.h \ + dyn_fpu.h operators.h risc_x64.h risc_x86.h \ No newline at end of file diff --git a/src/cpu/core_dynrec/cache.h b/src/cpu/core_dynrec/cache.h new file mode 100644 index 00000000..f0517150 --- /dev/null +++ b/src/cpu/core_dynrec/cache.h @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +class CodePageHandlerDynRec; // forward + +// basic cache block representation +class CacheBlockDynRec { +public: + void Clear(void); + // link this cache block to another block, index specifies the code + // path (always zero for unconditional links, 0/1 for conditional ones + void LinkTo(Bitu index,CacheBlockDynRec * toblock) { + assert(toblock); + link[index].to=toblock; + link[index].next=toblock->link[index].from; // set target block + toblock->link[index].from=this; // remember who links me + } + struct { + Bit16u start,end; // where in the page is the original code + CodePageHandlerDynRec * handler; // page containing this code + } page; + struct { + Bit8u * start; // where in the cache are we + Bitu size; + CacheBlockDynRec * next; + // writemap masking maskpointer/start/length + // to allow holes in the writemap + Bit8u * wmapmask; + Bit16u maskstart; + Bit16u masklen; + } cache; + struct { + Bitu index; + CacheBlockDynRec * next; + } hash; + struct { + CacheBlockDynRec * to; // this block can transfer control to the to-block + CacheBlockDynRec * next; + CacheBlockDynRec * from; // the from-block can transfer control to this block + } link[2]; // maximal two links (conditional jumps) + CacheBlockDynRec * crossblock; +}; + +static struct { + struct { + CacheBlockDynRec * first; // the first cache block in the list + CacheBlockDynRec * active; // the current cache block + CacheBlockDynRec * free; // pointer to the free list + CacheBlockDynRec * running; // the last block that was entered for execution + } block; + Bit8u * pos; // position in the cache block + CodePageHandlerDynRec * free_pages; // pointer to the free list + CodePageHandlerDynRec * used_pages; // pointer to the list of used pages + CodePageHandlerDynRec * last_page; // the last used page +} cache; + +static CacheBlockDynRec link_blocks[2]; // default linking (specially marked) + +// the CodePageHandlerDynRec class provides access to the contained +// cache blocks and intercepts writes to the code for special treatment +class CodePageHandlerDynRec : public PageHandler { +public: + CodePageHandlerDynRec() { + invalidation_map=NULL; + } + + void SetupAt(Bitu _phys_page,PageHandler * _old_pagehandler) { + // initialize this codepage handler + phys_page=_phys_page; + // save the old pagehandler to provide direct read access to the memory, + // and to be able to restore it later on + old_pagehandler=_old_pagehandler; + + // adjust flags + flags=old_pagehandler->flags|PFLAG_HASCODE; + flags&=~PFLAG_WRITEABLE; + + active_blocks=0; + active_count=16; + + // initialize the maps with zero (no cache blocks as well as code present) + memset(&hash_map,0,sizeof(hash_map)); + memset(&write_map,0,sizeof(write_map)); + if (invalidation_map!=NULL) { + free(invalidation_map); + invalidation_map=NULL; + } + } + + // clear out blocks that contain code which has been modified + bool InvalidateRange(Bitu start,Bitu end) { + Bits index=1+(end>>DYN_HASH_SHIFT); + bool is_current_block=false; // if the current block is modified, it has to be exited as soon as possible + + Bit32u ip_point=SegPhys(cs)+reg_eip; + ip_point=((paging.tlb.phys_page[ip_point>>12]-phys_page)<<12)+(ip_point&0xfff); + while (index>=0) { + Bitu map=0; + // see if there is still some code in the range + for (Bitu count=start;count<=end;count++) map+=write_map[count]; + if (!map) return is_current_block; // no more code, finished + + CacheBlockDynRec * block=hash_map[index]; + while (block) { + CacheBlockDynRec * nextblock=block->hash.next; + // test if this block is in the range + if (start<=block->page.end && end>=block->page.start) { + if (ip_point<=block->page.end && ip_point>=block->page.start) is_current_block=true; + block->Clear(); // clear the block, decrements the write_map accordingly + } + block=nextblock; + } + index--; + } + return is_current_block; + } + + // the following functions will clean all cache blocks that are invalid now due to the write + void writeb(PhysPt addr,Bitu val){ + addr&=4095; + if (host_readb(hostmem+addr)==(Bit8u)val) return; + host_writeb(hostmem+addr,val); + // see if there's code where we are writing to + if (!*(Bit8u*)&write_map[addr]) { + if (active_blocks) return; // still some blocks in this page + active_count--; + if (!active_count) Release(); // delay page releasing until active_count is zero + return; + } else if (!invalidation_map) { + invalidation_map=(Bit8u*)malloc(4096); + memset(invalidation_map,0,4096); + } + invalidation_map[addr]++; + InvalidateRange(addr,addr); + } + void writew(PhysPt addr,Bitu val){ + addr&=4095; + if (host_readw(hostmem+addr)==(Bit16u)val) return; + host_writew(hostmem+addr,val); + // see if there's code where we are writing to + if (!*(Bit16u*)&write_map[addr]) { + if (active_blocks) return; // still some blocks in this page + active_count--; + if (!active_count) Release(); // delay page releasing until active_count is zero + return; + } else if (!invalidation_map) { + invalidation_map=(Bit8u*)malloc(4096); + memset(invalidation_map,0,4096); + } + (*(Bit16u*)&invalidation_map[addr])+=0x101; + InvalidateRange(addr,addr+1); + } + void writed(PhysPt addr,Bitu val){ + addr&=4095; + if (host_readd(hostmem+addr)==(Bit32u)val) return; + host_writed(hostmem+addr,val); + // see if there's code where we are writing to + if (!*(Bit32u*)&write_map[addr]) { + if (active_blocks) return; // still some blocks in this page + active_count--; + if (!active_count) Release(); // delay page releasing until active_count is zero + return; + } else if (!invalidation_map) { + invalidation_map=(Bit8u*)malloc(4096); + memset(invalidation_map,0,4096); + } + (*(Bit32u*)&invalidation_map[addr])+=0x1010101; + InvalidateRange(addr,addr+3); + } + bool writeb_checked(PhysPt addr,Bitu val) { + addr&=4095; + if (host_readb(hostmem+addr)==(Bit8u)val) return false; + // see if there's code where we are writing to + if (!*(Bit8u*)&write_map[addr]) { + if (!active_blocks) { + // no blocks left in this page, still delay the page releasing a bit + active_count--; + if (!active_count) Release(); + } + } else { + if (!invalidation_map) { + invalidation_map=(Bit8u*)malloc(4096); + memset(invalidation_map,0,4096); + } + invalidation_map[addr]++; + if (InvalidateRange(addr,addr)) { + cpu.exception.which=SMC_CURRENT_BLOCK; + return true; + } + } + host_writeb(hostmem+addr,val); + return false; + } + bool writew_checked(PhysPt addr,Bitu val) { + addr&=4095; + if (host_readw(hostmem+addr)==(Bit16u)val) return false; + // see if there's code where we are writing to + if (!*(Bit16u*)&write_map[addr]) { + if (!active_blocks) { + // no blocks left in this page, still delay the page releasing a bit + active_count--; + if (!active_count) Release(); + } + } else { + if (!invalidation_map) { + invalidation_map=(Bit8u*)malloc(4096); + memset(invalidation_map,0,4096); + } + (*(Bit16u*)&invalidation_map[addr])+=0x101; + if (InvalidateRange(addr,addr+1)) { + cpu.exception.which=SMC_CURRENT_BLOCK; + return true; + } + } + host_writew(hostmem+addr,val); + return false; + } + bool writed_checked(PhysPt addr,Bitu val) { + addr&=4095; + if (host_readd(hostmem+addr)==(Bit32u)val) return false; + // see if there's code where we are writing to + if (!*(Bit32u*)&write_map[addr]) { + if (!active_blocks) { + // no blocks left in this page, still delay the page releasing a bit + active_count--; + if (!active_count) Release(); + } + } else { + if (!invalidation_map) { + invalidation_map=(Bit8u*)malloc(4096); + memset(invalidation_map,0,4096); + } + (*(Bit32u*)&invalidation_map[addr])+=0x1010101; + if (InvalidateRange(addr,addr+3)) { + cpu.exception.which=SMC_CURRENT_BLOCK; + return true; + } + } + host_writed(hostmem+addr,val); + return false; + } + + // add a cache block to this page and note it in the hash map + void AddCacheBlock(CacheBlockDynRec * block) { + Bitu index=1+(block->page.start>>DYN_HASH_SHIFT); + block->hash.next=hash_map[index]; // link to old block at index from the new block + block->hash.index=index; + hash_map[index]=block; // put new block at hash position + block->page.handler=this; + active_blocks++; + } + // there's a block whose code started in a different page + void AddCrossBlock(CacheBlockDynRec * block) { + block->hash.next=hash_map[0]; + block->hash.index=0; + hash_map[0]=block; + block->page.handler=this; + active_blocks++; + } + // remove a cache block + void DelCacheBlock(CacheBlockDynRec * block) { + active_blocks--; + active_count=16; + CacheBlockDynRec * * bwhere=&hash_map[block->hash.index]; + while (*bwhere!=block) { + bwhere=&((*bwhere)->hash.next); + //Will crash if a block isn't found, which should never happen. + } + *bwhere=block->hash.next; + + // remove the cleared block from the write map + if (GCC_UNLIKELY(block->cache.wmapmask!=NULL)) { + // first part is not influenced by the mask + for (Bitu i=block->page.start;icache.maskstart;i++) { + if (write_map[i]) write_map[i]--; + } + Bitu maskct=0; + // last part sticks to the writemap mask + for (Bitu i=block->cache.maskstart;i<=block->page.end;i++,maskct++) { + if (write_map[i]) { + // only adjust writemap if it isn't masked + if ((maskct>=block->cache.masklen) || (!block->cache.wmapmask[maskct])) write_map[i]--; + } + } + free(block->cache.wmapmask); + block->cache.wmapmask=NULL; + } else { + for (Bitu i=block->page.start;i<=block->page.end;i++) { + if (write_map[i]) write_map[i]--; + } + } + } + + void Release(void) { + MEM_SetPageHandler(phys_page,1,old_pagehandler); // revert to old handler + PAGING_ClearTLB(); + + // remove page from the lists + if (prev) prev->next=next; + else cache.used_pages=next; + if (next) next->prev=prev; + else cache.last_page=prev; + next=cache.free_pages; + cache.free_pages=this; + prev=0; + } + void ClearRelease(void) { + // clear out all cache blocks in this page + for (Bitu index=0;index<(1+DYN_PAGE_HASH);index++) { + CacheBlockDynRec * block=hash_map[index]; + while (block) { + CacheBlockDynRec * nextblock=block->hash.next; + block->page.handler=0; // no need, full clear + block->Clear(); + block=nextblock; + } + } + Release(); // now can release this page + } + + CacheBlockDynRec * FindCacheBlock(Bitu start) { + CacheBlockDynRec * block=hash_map[1+(start>>DYN_HASH_SHIFT)]; + // see if there's a cache block present at the start address + while (block) { + if (block->page.start==start) return block; // found + block=block->hash.next; + } + return 0; // none found + } + + HostPt GetHostReadPt(Bitu phys_page) { + hostmem=old_pagehandler->GetHostReadPt(phys_page); + return hostmem; + } + HostPt GetHostWritePt(Bitu phys_page) { + return GetHostReadPt( phys_page ); + } +public: + // the write map, there are write_map[i] cache blocks that cover the byte at address i + Bit8u write_map[4096]; + Bit8u * invalidation_map; + CodePageHandlerDynRec * next, * prev; // page linking +private: + PageHandler * old_pagehandler; + + // hash map to quickly find the cache blocks in this page + CacheBlockDynRec * hash_map[1+DYN_PAGE_HASH]; + + Bitu active_blocks; // the number of cache blocks in this page + Bitu active_count; // delaying parameter to not immediately release a page + HostPt hostmem; + Bitu phys_page; +}; + + +static INLINE void cache_addunusedblock(CacheBlockDynRec * block) { + // block has become unused, add it to the freelist + block->cache.next=cache.block.free; + cache.block.free=block; +} + +static CacheBlockDynRec * cache_getblock(void) { + // get a free cache block and advance the free pointer + CacheBlockDynRec * ret=cache.block.free; + if (!ret) E_Exit("Ran out of CacheBlocks" ); + cache.block.free=ret->cache.next; + ret->cache.next=0; + return ret; +} + +void CacheBlockDynRec::Clear(void) { + Bitu ind; + // check if this is not a cross page block + if (hash.index) for (ind=0;ind<2;ind++) { + CacheBlockDynRec * fromlink=link[ind].from; + link[ind].from=0; + while (fromlink) { + CacheBlockDynRec * nextlink=fromlink->link[ind].next; + // clear the next-link and let the block point to the standard linkcode + fromlink->link[ind].next=0; + fromlink->link[ind].to=&link_blocks[ind]; + + fromlink=nextlink; + } + if (link[ind].to!=&link_blocks[ind]) { + // not linked to the standard linkcode, find the block that links to this block + CacheBlockDynRec * * wherelink=&link[ind].to->link[ind].from; + while (*wherelink != this && *wherelink) { + wherelink = &(*wherelink)->link[ind].next; + } + // now remove the link + if(*wherelink) + *wherelink = (*wherelink)->link[ind].next; + else { + LOG(LOG_CPU,LOG_ERROR)("Cache anomaly. please investigate"); + } + } + } else + cache_addunusedblock(this); + if (crossblock) { + // clear out the crossblock (in the page before) as well + crossblock->crossblock=0; + crossblock->Clear(); + crossblock=0; + } + if (page.handler) { + // clear out the code page handler + page.handler->DelCacheBlock(this); + page.handler=0; + } + if (cache.wmapmask){ + free(cache.wmapmask); + cache.wmapmask=NULL; + } +} + + +static CacheBlockDynRec * cache_openblock(void) { + CacheBlockDynRec * block=cache.block.active; + // check for enough space in this block + Bitu size=block->cache.size; + CacheBlockDynRec * nextblock=block->cache.next; + if (block->page.handler) + block->Clear(); + // block size must be at least CACHE_MAXSIZE + while (sizecache.size; + CacheBlockDynRec * tempblock=nextblock->cache.next; + if (nextblock->page.handler) + nextblock->Clear(); + // block is free now + cache_addunusedblock(nextblock); + nextblock=tempblock; + } +skipresize: + // adjust parameters and open this block + block->cache.size=size; + block->cache.next=nextblock; + cache.pos=block->cache.start; + return block; +} + +static void cache_closeblock(void) { + CacheBlockDynRec * block=cache.block.active; + // links point to the default linking code + block->link[0].to=&link_blocks[0]; + block->link[1].to=&link_blocks[1]; + block->link[0].from=0; + block->link[1].from=0; + block->link[0].next=0; + block->link[1].next=0; + // close the block with correct alignment + Bitu written=(Bitu)(cache.pos-block->cache.start); + if (written>block->cache.size) { + if (!block->cache.next) { + if (written>block->cache.size+CACHE_MAXSIZE) E_Exit("CacheBlock overrun 1 %d",written-block->cache.size); + } else E_Exit("CacheBlock overrun 2 written %d size %d",written,block->cache.size); + } 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; + CacheBlockDynRec * newblock=cache_getblock(); + // align block now to CACHE_ALIGN + newblock->cache.start=block->cache.start+new_size; + newblock->cache.size=block->cache.size-new_size; + newblock->cache.next=block->cache.next; + block->cache.next=newblock; + block->cache.size=new_size; + } + } + // advance the active block pointer + if (!block->cache.next) { +// LOG_MSG("Cache full restarting"); + cache.block.active=cache.block.first; + } else { + cache.block.active=block->cache.next; + } +} + + +// place an 8bit value into the cache +static INLINE void cache_addb(Bit8u val) { + *cache.pos++=val; +} + +// place a 16bit value into the cache +static INLINE void cache_addw(Bit16u val) { + *(Bit16u*)cache.pos=val; + cache.pos+=2; +} + +// place a 32bit value into the cache +static INLINE void cache_addd(Bit32u val) { + *(Bit32u*)cache.pos=val; + cache.pos+=4; +} + +// place a 64bit value into the cache +static INLINE void cache_addq(Bit64u val) { + *(Bit64u*)cache.pos=val; + cache.pos+=8; +} + + +static void dyn_return(BlockReturn retcode,bool ret_exception); +static void dyn_run_code(void); + +// cache memory pointers, to be malloc'd later +static Bit8u * cache_code_start_ptr=NULL; +static Bit8u * cache_code=NULL; +static Bit8u * cache_code_link_blocks=NULL; +static CacheBlockDynRec * cache_blocks=NULL; + +/* Define temporary pagesize so the MPROTECT case and the regular case share as much code as possible */ +#if (C_HAVE_MPROTECT) +#define PAGESIZE_TEMP PAGESIZE +#else +#define PAGESIZE_TEMP 4096 +#endif + +static bool cache_initialized = false; + +static void cache_init(bool enable) { + Bits i; + if (enable) { + // see if cache is already initialized + if (cache_initialized) return; + cache_initialized = true; + if (cache_blocks == NULL) { + // allocate the cache blocks memory + cache_blocks=(CacheBlockDynRec*)malloc(CACHE_BLOCKS*sizeof(CacheBlockDynRec)); + if(!cache_blocks) E_Exit("Allocating cache_blocks has failed"); + memset(cache_blocks,0,sizeof(CacheBlockDynRec)*CACHE_BLOCKS); + cache.block.free=&cache_blocks[0]; + // initialize the cache blocks + for (i=0;icache.start=&cache_code[0]; + block->cache.size=CACHE_TOTAL; + block->cache.next=0; // last block in the list + } + // setup the default blocks for block linkage returns + cache.pos=&cache_code_link_blocks[0]; + link_blocks[0].cache.start=cache.pos; + // link code that returns with a special return code + dyn_return(BR_Link1,false); + cache.pos=&cache_code_link_blocks[32]; + link_blocks[1].cache.start=cache.pos; + // link code that returns with a special return code + dyn_return(BR_Link2,false); + + cache.pos=&cache_code_link_blocks[64]; + core_dynrec.runcode=(BlockReturn (*)(Bit8u*))cache.pos; +// link_blocks[1].cache.start=cache.pos; + dyn_run_code(); + + cache.free_pages=0; + cache.last_page=0; + cache.used_pages=0; + // setup the code pages + for (i=0;inext=cache.free_pages; + cache.free_pages=newpage; + } + } +} + +static void cache_close(void) { +/* for (;;) { + if (cache.used_pages) { + CodePageHandler * cpage=cache.used_pages; + CodePageHandler * npage=cache.used_pages->next; + cpage->ClearRelease(); + delete cpage; + cache.used_pages=npage; + } else break; + } + if (cache_blocks != NULL) { + free(cache_blocks); + cache_blocks = NULL; + } + if (cache_code_start_ptr != NULL) { + free(cache_code_start_ptr); + cache_code_start_ptr = NULL; + } + cache_code = NULL; + cache_code_link_blocks = NULL; + cache_initialized = false; */ +} diff --git a/src/cpu/core_dynrec/decoder.h b/src/cpu/core_dynrec/decoder.h new file mode 100644 index 00000000..ffa6faa3 --- /dev/null +++ b/src/cpu/core_dynrec/decoder.h @@ -0,0 +1,582 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 maximal + 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; + + case 0xb4:dyn_load_seg_off_ea(DRC_SEG_FS);break; + case 0xb5: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: +// DYN_LOG("Unhandled dual opcode 0F%02X",dual_code); + 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_get_modrm(); + dyn_fill_ea(FC_ADDR,false); + gen_mov_word_from_reg(FC_ADDR,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + 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; + + case 0xc4:dyn_load_seg_off_ea(DRC_SEG_ES);break; + case 0xc5: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 + case 0xcd:dyn_interrupt(decode_fetchb());goto finish_block; + 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); + if (cpu.pmode) dyn_check_exception(FC_RETOP); + break; + case 0xfb: //STI + gen_call_function_raw((void *)&CPU_STI); + if (cpu.pmode) 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: + if (dyn_grp4_ev()) goto core_close_block; + break; + + default: +// DYN_LOG("Dynrec unhandled opcode %X",opcode); + goto illegalopcode; + } + } + // normal exit because the maximal number of opcodes has been reached + dyn_set_eip_end(); +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; +} diff --git a/src/cpu/core_dynrec/decoder_basic.h b/src/cpu/core_dynrec/decoder_basic.h new file mode 100644 index 00000000..ebef1cb7 --- /dev/null +++ b/src/cpu/core_dynrec/decoder_basic.h @@ -0,0 +1,1091 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +/* + This file provides some definitions and basic level functions + that use code generating functions from risc_?.h + Important is the function call generation including parameter + loading, the effective address calculation and the memory + access functions. +*/ + + +// instructions that use one operand +enum SingleOps { + SOP_INC,SOP_DEC, + SOP_NOT,SOP_NEG, +}; + +// instructions that use two operand +enum DualOps { + DOP_ADD,DOP_ADC, + DOP_SUB,DOP_SBB, + DOP_CMP,DOP_XOR, + DOP_AND,DOP_OR, + DOP_TEST, + DOP_MOV, + DOP_XCHG, +}; + +// shift and rotate functions +enum ShiftOps { + SHIFT_ROL,SHIFT_ROR, + SHIFT_RCL,SHIFT_RCR, + SHIFT_SHL,SHIFT_SHR, + SHIFT_SAL,SHIFT_SAR, +}; + +// branch conditions +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 +}; + +// string instructions +enum StringOps { + STR_OUTSB=0,STR_OUTSW,STR_OUTSD, + STR_INSB=4,STR_INSW,STR_INSD, + STR_MOVSB=8,STR_MOVSW,STR_MOVSD, + STR_LODSB=12,STR_LODSW,STR_LODSD, + STR_STOSB=16,STR_STOSW,STR_STOSD, + STR_SCASB=20,STR_SCASW,STR_SCASD, + STR_CMPSB=24,STR_CMPSW,STR_CMPSD, +}; + +// repeat prefix type (for string operations) +enum REP_Type { + REP_NONE=0,REP_NZ,REP_Z +}; + +// loop type +enum LoopTypes { + LOOP_NONE,LOOP_NE,LOOP_E,LOOP_JCXZ +}; + +// rotate operand type +enum grp2_types { + grp2_1,grp2_imm,grp2_cl, +}; + +// opcode mapping for group1 instructions +static DualOps grp1_table[8]={ + DOP_ADD,DOP_OR,DOP_ADC,DOP_SBB,DOP_AND,DOP_SUB,DOP_XOR,DOP_CMP +}; + + +// decoding information used during translation of a code block +static struct DynDecode { + PhysPt code; // pointer to next byte in the instruction stream + PhysPt code_start; // pointer to the start of the current code block + PhysPt op_start; // pointer to the start of the current instruction + bool big_op; // operand modifier + bool big_addr; // address modifier + REP_Type rep; // current repeat prefix + Bitu cycles; // number cycles used by currently translated code + bool seg_prefix_used; // segment overridden + Bit8u seg_prefix; // segment prefix (if seg_prefix_used==true) + + // block that contains the first instruction translated + CacheBlockDynRec * block; + // block that contains the current byte of the instruction stream + CacheBlockDynRec * active_block; + + // the active page (containing the current byte of the instruction stream) + struct { + CodePageHandlerDynRec * code; + Bitu index; // index to the current byte of the instruction stream + Bit8u * wmap; // write map that indicates code presence for every byte of this page + Bit8u * invmap; // invalidation map + Bitu first; // page number + } page; + + // modrm state of the current instruction (if used) + struct { + Bitu val; + Bitu mod; + Bitu rm; + Bitu reg; + } modrm; +} decode; + + +static bool MakeCodePage(Bitu lin_addr,CodePageHandlerDynRec * &cph) { + Bit8u rdval; + //Ensure page contains memory: + if (GCC_UNLIKELY(mem_readb_checked_x86(lin_addr,&rdval))) return true; + + Bitu lin_page=lin_addr >> 12; + + PageHandler * handler=paging.tlb.handler[lin_page]; + if (handler->flags & PFLAG_HASCODE) { + // this is a codepage handler, and the one that we're looking for + cph=(CodePageHandlerDynRec *)handler; + return false; + } + if (handler->flags & PFLAG_NOCODE) { + LOG_MSG("DYNREC:Can't run code in this page"); + cph=0; + return false; + } + Bitu phys_page=lin_page; + // find the physical page that the linear page is mapped to + if (!PAGING_MakePhysPage(phys_page)) { + LOG_MSG("DYNREC:Can't find physpage"); + cph=0; + return false; + } + // find a free CodePage + if (!cache.free_pages) { + if (cache.used_pages!=decode.page.code) cache.used_pages->ClearRelease(); + else { + // try another page to avoid clearing our source-crosspage + if ((cache.used_pages->next) && (cache.used_pages->next!=decode.page.code)) + cache.used_pages->next->ClearRelease(); + else { + LOG_MSG("DYNREC:Invalid cache links"); + cache.used_pages->ClearRelease(); + } + } + } + CodePageHandlerDynRec * cpagehandler=cache.free_pages; + cache.free_pages=cache.free_pages->next; + + // adjust previous and next page pointer + cpagehandler->prev=cache.last_page; + cpagehandler->next=0; + if (cache.last_page) cache.last_page->next=cpagehandler; + cache.last_page=cpagehandler; + if (!cache.used_pages) cache.used_pages=cpagehandler; + + // initialize the code page handler and add the handler to the memory page + cpagehandler->SetupAt(phys_page,handler); + MEM_SetPageHandler(phys_page,1,cpagehandler); + PAGING_UnlinkPages(lin_page,1); + cph=cpagehandler; + return false; +} + +static void decode_advancepage(void) { + // Advance to the next page + decode.active_block->page.end=4095; + // trigger possible page fault here + decode.page.first++; + Bitu faddr=decode.page.first << 12; + mem_readb(faddr); + MakeCodePage(faddr,decode.page.code); + CacheBlockDynRec * newblock=cache_getblock(); + decode.active_block->crossblock=newblock; + newblock->crossblock=decode.active_block; + decode.active_block=newblock; + decode.active_block->page.start=0; + decode.page.code->AddCrossBlock(decode.active_block); + decode.page.wmap=decode.page.code->write_map; + decode.page.invmap=decode.page.code->invalidation_map; + decode.page.index=0; +} + +// fetch the next byte of the instruction stream +static Bit8u decode_fetchb(void) { + if (GCC_UNLIKELY(decode.page.index>=4096)) { + decode_advancepage(); + } + decode.page.wmap[decode.page.index]+=0x01; + decode.page.index++; + decode.code+=1; + return mem_readb(decode.code-1); +} +// fetch the next word of the instruction stream +static Bit16u decode_fetchw(void) { + if (GCC_UNLIKELY(decode.page.index>=4095)) { + Bit16u val=decode_fetchb(); + val|=decode_fetchb() << 8; + return val; + } + *(Bit16u *)&decode.page.wmap[decode.page.index]+=0x0101; + decode.code+=2;decode.page.index+=2; + return mem_readw(decode.code-2); +} +// fetch the next dword of the instruction stream +static Bit32u decode_fetchd(void) { + if (GCC_UNLIKELY(decode.page.index>=4093)) { + Bit32u val=decode_fetchb(); + val|=decode_fetchb() << 8; + val|=decode_fetchb() << 16; + val|=decode_fetchb() << 24; + return val; + /* Advance to the next page */ + } + *(Bit32u *)&decode.page.wmap[decode.page.index]+=0x01010101; + decode.code+=4;decode.page.index+=4; + return mem_readd(decode.code-4); +} + +#define START_WMMEM 64 + +// adjust writemap mask to care for map holes due to special +// codefetch functions +static void INLINE decode_increase_wmapmask(Bitu size) { + Bitu mapidx; + CacheBlockDynRec* activecb=decode.active_block; + if (GCC_UNLIKELY(!activecb->cache.wmapmask)) { + // no mask memory yet allocated, start with a small buffer + activecb->cache.wmapmask=(Bit8u*)malloc(START_WMMEM); + memset(activecb->cache.wmapmask,0,START_WMMEM); + activecb->cache.maskstart=decode.page.index; // start of buffer is current code position + activecb->cache.masklen=START_WMMEM; + mapidx=0; + } else { + mapidx=decode.page.index-activecb->cache.maskstart; + if (GCC_UNLIKELY(mapidx+size>=activecb->cache.masklen)) { + // mask buffer too small, increase + Bitu newmasklen=activecb->cache.masklen*4; + if (newmasklencache.wmapmask,activecb->cache.masklen); + free(activecb->cache.wmapmask); + activecb->cache.wmapmask=tempmem; + activecb->cache.masklen=newmasklen; + } + } + // update mask entries + switch (size) { + case 1 : activecb->cache.wmapmask[mapidx]+=0x01; break; + case 2 : (*(Bit16u*)&activecb->cache.wmapmask[mapidx])+=0x0101; break; + case 4 : (*(Bit32u*)&activecb->cache.wmapmask[mapidx])+=0x01010101; break; + } +} + +// fetch a byte, val points to the code location if possible, +// otherwise val contains the current value read from the position +static bool decode_fetchb_imm(Bitu & val) { + if (GCC_UNLIKELY(decode.page.index>=4096)) { + decode_advancepage(); + } + Bitu index=(decode.code>>12); + // see if position is directly accessible + if (paging.tlb.read[index]) { + val=(Bitu)(paging.tlb.read[index]+decode.code); + decode_increase_wmapmask(1); + decode.code++; + decode.page.index++; + return true; + } + // not directly accessible, just fetch the value + val=(Bit32u)decode_fetchb(); + return false; +} + +// fetch a word, val points to the code location if possible, +// otherwise val contains the current value read from the position +static bool decode_fetchw_imm(Bitu & val) { + if (decode.page.index<4095) { + Bitu index=(decode.code>>12); + // see if position is directly accessible + if (paging.tlb.read[index]) { + val=(Bitu)(paging.tlb.read[index]+decode.code); + decode_increase_wmapmask(2); + decode.code+=2; + decode.page.index+=2; + return true; + } + } + // not directly accessible, just fetch the value + val=decode_fetchw(); + return false; +} + +// fetch a dword, val points to the code location if possible, +// otherwise val contains the current value read from the position +static bool decode_fetchd_imm(Bitu & val) { + if (decode.page.index<4093) { + Bitu index=(decode.code>>12); + // see if position is directly accessible + if (paging.tlb.read[index]) { + val=(Bitu)(paging.tlb.read[index]+decode.code); + decode_increase_wmapmask(4); + decode.code+=4; + decode.page.index+=4; + return true; + } + } + // not directly accessible, just fetch the value + val=decode_fetchd(); + return false; +} + + +// modrm decoding helper +static void INLINE 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); +} + + + +// adjust CPU_Cycles value +static void dyn_reduce_cycles(void) { + if (!decode.cycles) decode.cycles++; + gen_sub_direct_word(&CPU_Cycles,decode.cycles,true); +} + + +// set reg to the start of the next instruction +// set reg_eip to the start of the current instruction +static INLINE void dyn_set_eip_last_end(HostReg reg) { + gen_mov_word_to_reg(reg,®_eip,true); + gen_add_imm(reg,(Bit32u)(decode.code-decode.code_start)); + gen_add_direct_word(®_eip,decode.op_start-decode.code_start,decode.big_op); +} + +// set reg_eip to the start of the current instruction +static INLINE void dyn_set_eip_last(void) { + gen_add_direct_word(®_eip,decode.op_start-decode.code_start,cpu.code.big); +} + +// set reg_eip to the start of the next instruction +static INLINE void dyn_set_eip_end(void) { + gen_add_direct_word(®_eip,decode.code-decode.code_start,cpu.code.big); +} + +// set reg_eip to the start of the next instruction plus an offset (imm) +static INLINE void dyn_set_eip_end(HostReg reg,Bit32u imm=0) { + gen_mov_word_to_reg(reg,®_eip,decode.big_op); + gen_add_imm(reg,(Bit32u)(decode.code-decode.code_start+imm)); + if (!decode.big_op) gen_extend_word(false,reg); +} + + + +// the following functions generate function calls +// parameters are loaded by generating code using gen_load_param_ which +// is architecture dependent +// R=host register; I=32bit immediate value; A=address value; m=memory + +static DRC_PTR_SIZE_IM INLINE gen_call_function_R(void * func,Bitu op) { + gen_load_param_reg(op,0); + return gen_call_function_setup(func, 1); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_R3(void * func,Bitu op) { + gen_load_param_reg(op,2); + return gen_call_function_setup(func, 3, true); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_RI(void * func,Bitu op1,Bitu op2) { + gen_load_param_imm(op2,1); + gen_load_param_reg(op1,0); + return gen_call_function_setup(func, 2); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_RA(void * func,Bitu op1,DRC_PTR_SIZE_IM op2) { + gen_load_param_addr(op2,1); + gen_load_param_reg(op1,0); + return gen_call_function_setup(func, 2); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_RR(void * func,Bitu op1,Bitu op2) { + gen_load_param_reg(op2,1); + gen_load_param_reg(op1,0); + return gen_call_function_setup(func, 2); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_IR(void * func,Bitu op1,Bitu op2) { + gen_load_param_reg(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 2); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_I(void * func,Bitu op) { + gen_load_param_imm(op,0); + return gen_call_function_setup(func, 1); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_II(void * func,Bitu op1,Bitu op2) { + gen_load_param_imm(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 2); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_III(void * func,Bitu op1,Bitu op2,Bitu op3) { + gen_load_param_imm(op3,2); + gen_load_param_imm(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 3); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_IA(void * func,Bitu op1,DRC_PTR_SIZE_IM op2) { + gen_load_param_addr(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 2); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_IIR(void * func,Bitu op1,Bitu op2,Bitu op3) { + gen_load_param_reg(op3,2); + gen_load_param_imm(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 3); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_IIIR(void * func,Bitu op1,Bitu op2,Bitu op3,Bitu op4) { + gen_load_param_reg(op4,3); + gen_load_param_imm(op3,2); + gen_load_param_imm(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 4); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_IRRR(void * func,Bitu op1,Bitu op2,Bitu op3,Bitu op4) { + gen_load_param_reg(op4,3); + gen_load_param_reg(op3,2); + gen_load_param_reg(op2,1); + gen_load_param_imm(op1,0); + return gen_call_function_setup(func, 4); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_m(void * func,Bitu op) { + gen_load_param_mem(op,2); + return gen_call_function_setup(func, 3, true); +} + +static DRC_PTR_SIZE_IM INLINE gen_call_function_mm(void * func,Bitu op1,Bitu op2) { + gen_load_param_mem(op2,3); + gen_load_param_mem(op1,2); + return gen_call_function_setup(func, 4, true); +} + + + +enum save_info_type {exception, cycle_check, string_break}; + + +// function that is called on exceptions +static BlockReturn DynRunException(Bit32u eip_add,Bit32u cycle_sub) { + reg_eip+=eip_add; + CPU_Cycles-=cycle_sub; + if (cpu.exception.which==SMC_CURRENT_BLOCK) return BR_SMCBlock; + CPU_Exception(cpu.exception.which,cpu.exception.error); + return BR_Normal; +} + + +// array with information about code that is generated at the +// end of a cache block because it is rarely reached (like exceptions) +static struct { + save_info_type type; + DRC_PTR_SIZE_IM branch_pos; + Bit32u eip_change; + Bitu cycles; +} save_info_dynrec[512]; + +Bitu used_save_info_dynrec=0; + + +// return from current block, with returncode +static void dyn_return(BlockReturn retcode,bool ret_exception=false) { + if (!ret_exception) { + gen_mov_dword_to_reg_imm(FC_RETOP,retcode); + } + gen_return_function(); +} + +static void dyn_run_code(void) { + gen_run_code(); + gen_return_function(); +} + +// fill in code at the end of the block that contains rarely-executed code +// which is executed conditionally (like exceptions) +static void dyn_fill_blocks(void) { + for (Bitu sct=0; sct>12); + if (paging.tlb.read[index]) { + *((Bit8u*)(&core_dynrec.readdata))=host_readb(paging.tlb.read[index]+address); + return false; + } else { + Bitu uval; + bool retval; + retval=paging.tlb.handler[index]->readb_checked(address, &uval); + *((Bit8u*)(&core_dynrec.readdata))=(Bit8u)uval; + return retval; + } +} + +bool DRC_CALL_CONV mem_writeb_checked_drc(PhysPt address,Bit8u val) { + Bitu index=(address>>12); + if (paging.tlb.write[index]) { + host_writeb(paging.tlb.write[index]+address,val); + return false; + } else return paging.tlb.handler[index]->writeb_checked(address,val); +} + +bool DRC_CALL_CONV mem_readw_checked_drc(PhysPt address) { +#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY) + if (!(address & 1)) { +#else + if ((address & 0xfff)<0xfff) { +#endif + Bitu index=(address>>12); + if (paging.tlb.read[index]) { + *((Bit16u*)(&core_dynrec.readdata))=host_readw(paging.tlb.read[index]+address); + return false; + } else { + Bitu uval; + bool retval; + retval=paging.tlb.handler[index]->readw_checked(address, &uval); + *((Bit16u*)(&core_dynrec.readdata))=(Bit16u)uval; + return retval; + } + } else return mem_unalignedreadw_checked_x86(address, ((Bit16u*)(&core_dynrec.readdata))); +} + +bool DRC_CALL_CONV mem_readd_checked_drc(PhysPt address) { +#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY) + if (!(address & 3)) { +#else + if ((address & 0xfff)<0xffd) { +#endif + Bitu index=(address>>12); + if (paging.tlb.read[index]) { + *((Bit32u*)(&core_dynrec.readdata))=host_readd(paging.tlb.read[index]+address); + return false; + } else { + Bitu uval; + bool retval; + retval=paging.tlb.handler[index]->readd_checked(address, &uval); + *((Bit32u*)(&core_dynrec.readdata))=(Bit32u)uval; + return retval; + } + } else return mem_unalignedreadd_checked_x86(address, ((Bit32u*)(&core_dynrec.readdata))); +} + +bool DRC_CALL_CONV mem_writew_checked_drc(PhysPt address,Bit16u val) { +#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY) + if (!(address & 1)) { +#else + if ((address & 0xfff)<0xfff) { +#endif + Bitu index=(address>>12); + if (paging.tlb.write[index]) { + host_writew(paging.tlb.write[index]+address,val); + return false; + } else return paging.tlb.handler[index]->writew_checked(address,val); + } else return mem_unalignedwritew_checked_x86(address,val); +} + +bool DRC_CALL_CONV mem_writed_checked_drc(PhysPt address,Bit32u val) { +#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY) + if (!(address & 3)) { +#else + if ((address & 0xfff)<0xffd) { +#endif + Bitu index=(address>>12); + if (paging.tlb.write[index]) { + host_writed(paging.tlb.write[index]+address,val); + return false; + } else return paging.tlb.handler[index]->writed_checked(address,val); + } else return mem_unalignedwrited_checked_x86(address,val); +} + + +// functions that enable access to the memory + +// read a byte from a given address and store it in reg_dst +static void dyn_read_byte(HostReg reg_addr,HostReg reg_dst) { + gen_mov_regs(FC_OP1,reg_addr); + gen_call_function_raw((void *)&mem_readb_checked_drc); + dyn_check_exception(FC_RETOP); + gen_mov_byte_to_reg_low(reg_dst,&core_dynrec.readdata); +} +static void dyn_read_byte_canuseword(HostReg reg_addr,HostReg reg_dst) { + gen_mov_regs(FC_OP1,reg_addr); + gen_call_function_raw((void *)&mem_readb_checked_drc); + dyn_check_exception(FC_RETOP); + gen_mov_byte_to_reg_low_canuseword(reg_dst,&core_dynrec.readdata); +} + +// write a byte from reg_val into the memory given by the address +static void dyn_write_byte(HostReg reg_addr,HostReg reg_val) { + gen_mov_regs(FC_OP2,reg_val); + gen_mov_regs(FC_OP1,reg_addr); + gen_call_function_raw((void *)&mem_writeb_checked_drc); + dyn_check_exception(FC_RETOP); +} + +// read a 32bit (dword=true) or 16bit (dword=false) value +// from a given address and store it in reg_dst +static void dyn_read_word(HostReg reg_addr,HostReg reg_dst,bool dword) { + gen_mov_regs(FC_OP1,reg_addr); + if (dword) gen_call_function_raw((void *)&mem_readd_checked_drc); + else gen_call_function_raw((void *)&mem_readw_checked_drc); + dyn_check_exception(FC_RETOP); + gen_mov_word_to_reg(reg_dst,&core_dynrec.readdata,dword); +} + +// write a 32bit (dword=true) or 16bit (dword=false) value +// from reg_val into the memory given by the address +static void dyn_write_word(HostReg reg_addr,HostReg reg_val,bool dword) { +// if (!dword) gen_extend_word(false,reg_val); + gen_mov_regs(FC_OP2,reg_val); + gen_mov_regs(FC_OP1,reg_addr); + if (dword) gen_call_function_raw((void *)&mem_writed_checked_drc); + else gen_call_function_raw((void *)&mem_writew_checked_drc); + dyn_check_exception(FC_RETOP); +} + + + +// effective address calculation helper, op2 has to be present! +// loads op1 into ea_reg and adds the scaled op2 and the immediate to it +static void dyn_lea(HostReg ea_reg,void* op1,void* op2,Bitu scale,Bits imm) { + if (scale || imm) { + if (op1!=NULL) { + gen_mov_word_to_reg(ea_reg,op1,true); + gen_mov_word_to_reg(TEMP_REG_DRC,op2,true); + + gen_lea(ea_reg,TEMP_REG_DRC,scale,imm); + } else { + gen_mov_word_to_reg(ea_reg,op2,true); + gen_lea(ea_reg,scale,imm); + } + } else { + gen_mov_word_to_reg(ea_reg,op2,true); + if (op1!=NULL) gen_add(ea_reg,op1); + } +} + +// calculate the effective address and store it in ea_reg +static void dyn_fill_ea(HostReg ea_reg,bool addseg=true) { + Bit8u seg_base=DRC_SEG_DS; + 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 + dyn_lea(ea_reg,DRCD_REG(DRC_REG_EBX),DRCD_REG(DRC_REG_ESI),0,imm); + break; + case 1:// BX+DI + dyn_lea(ea_reg,DRCD_REG(DRC_REG_EBX),DRCD_REG(DRC_REG_EDI),0,imm); + break; + case 2:// BP+SI + dyn_lea(ea_reg,DRCD_REG(DRC_REG_EBP),DRCD_REG(DRC_REG_ESI),0,imm); + seg_base=DRC_SEG_SS; + break; + case 3:// BP+DI + dyn_lea(ea_reg,DRCD_REG(DRC_REG_EBP),DRCD_REG(DRC_REG_EDI),0,imm); + seg_base=DRC_SEG_SS; + break; + case 4:// SI + gen_mov_word_to_reg(ea_reg,DRCD_REG(DRC_REG_ESI),true); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + break; + case 5:// DI + gen_mov_word_to_reg(ea_reg,DRCD_REG(DRC_REG_EDI),true); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + break; + case 6:// imm/BP + if (!decode.modrm.mod) { + imm=decode_fetchw(); + gen_mov_dword_to_reg_imm(ea_reg,(Bit32u)imm); + goto skip_extend_word; + } else { + gen_mov_word_to_reg(ea_reg,DRCD_REG(DRC_REG_EBP),true); + gen_add_imm(ea_reg,(Bit32u)imm); + seg_base=DRC_SEG_SS; + } + break; + case 7: // BX + gen_mov_word_to_reg(ea_reg,DRCD_REG(DRC_REG_EBX),true); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + break; + } + // zero out the high 16bit so ea_reg can be used as full register + gen_extend_word(false,ea_reg); +skip_extend_word: + if (addseg) { + // add the physical segment value if requested + gen_add(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base)); + } + } else { + Bits imm=0; + Bit8u base_reg; + Bit8u scaled_reg; + Bitu scale=0; + switch (decode.modrm.rm) { + case 0:base_reg=DRC_REG_EAX;break; + case 1:base_reg=DRC_REG_ECX;break; + case 2:base_reg=DRC_REG_EDX;break; + case 3:base_reg=DRC_REG_EBX;break; + case 4: // SIB + { + Bitu sib=decode_fetchb(); + bool scaled_reg_used=false; + static Bit8u scaledtable[8]={ + DRC_REG_EAX,DRC_REG_ECX,DRC_REG_EDX,DRC_REG_EBX, + 0,DRC_REG_EBP,DRC_REG_ESI,DRC_REG_EDI + }; + // see if scaling should be used and which register is to be scaled in this case + if (((sib >> 3) &7)!=4) scaled_reg_used=true; + scaled_reg=scaledtable[(sib >> 3) &7]; + scale=(sib >> 6); + + switch (sib & 7) { + case 0:base_reg=DRC_REG_EAX;break; + case 1:base_reg=DRC_REG_ECX;break; + case 2:base_reg=DRC_REG_EDX;break; + case 3:base_reg=DRC_REG_EBX;break; + case 4:base_reg=DRC_REG_ESP;seg_base=DRC_SEG_SS;break; + case 5: + if (decode.modrm.mod) { + base_reg=DRC_REG_EBP;seg_base=DRC_SEG_SS; + } else { + // no basereg, maybe scalereg + Bitu val; + // try to get a pointer to the next dword code position + if (decode_fetchd_imm(val)) { + // succeeded, use the pointer to avoid code invalidation + if (!addseg) { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,(void*)val,true); + } else { + dyn_lea(ea_reg,NULL,DRCD_REG(scaled_reg),scale,0); + gen_add(ea_reg,(void*)val); + } + } else { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + } else { + dyn_lea(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),DRCD_REG(scaled_reg),scale,0); + } + gen_add(ea_reg,(void*)val); + } + return; + } + // couldn't get a pointer, use the current value + imm=(Bit32s)val; + + if (!addseg) { + if (!scaled_reg_used) { + gen_mov_dword_to_reg_imm(ea_reg,(Bit32u)imm); + } else { + dyn_lea(ea_reg,NULL,DRCD_REG(scaled_reg),scale,imm); + } + } else { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + } else { + dyn_lea(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),DRCD_REG(scaled_reg),scale,imm); + } + } + + return; + } + break; + case 6:base_reg=DRC_REG_ESI;break; + case 7:base_reg=DRC_REG_EDI;break; + } + // basereg, maybe scalereg + switch (decode.modrm.mod) { + case 1: + imm=(Bit8s)decode_fetchb(); + break; + case 2: { + Bitu val; + // try to get a pointer to the next dword code position + if (decode_fetchd_imm(val)) { + // succeeded, use the pointer to avoid code invalidation + if (!addseg) { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,DRCD_REG(base_reg),true); + gen_add(ea_reg,(void*)val); + } else { + dyn_lea(ea_reg,DRCD_REG(base_reg),DRCD_REG(scaled_reg),scale,0); + gen_add(ea_reg,(void*)val); + } + } else { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + } else { + dyn_lea(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),DRCD_REG(scaled_reg),scale,0); + } + gen_add(ea_reg,DRCD_REG(base_reg)); + gen_add(ea_reg,(void*)val); + } + return; + } + // couldn't get a pointer, use the current value + imm=(Bit32s)val; + break; + } + } + + if (!addseg) { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,DRCD_REG(base_reg),true); + gen_add_imm(ea_reg,(Bit32u)imm); + } else { + dyn_lea(ea_reg,DRCD_REG(base_reg),DRCD_REG(scaled_reg),scale,imm); + } + } else { + if (!scaled_reg_used) { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + gen_add(ea_reg,DRCD_REG(base_reg)); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + } else { + dyn_lea(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),DRCD_REG(scaled_reg),scale,imm); + gen_add(ea_reg,DRCD_REG(base_reg)); + } + } + + return; + } + break; // SIB Break + case 5: + if (decode.modrm.mod) { + base_reg=DRC_REG_EBP;seg_base=DRC_SEG_SS; + } else { + // no base, no scalereg + + imm=(Bit32s)decode_fetchd(); + if (!addseg) { + gen_mov_dword_to_reg_imm(ea_reg,(Bit32u)imm); + } else { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + } + + return; + } + break; + case 6:base_reg=DRC_REG_ESI;break; + case 7:base_reg=DRC_REG_EDI;break; + } + + // no scalereg, but basereg + + switch (decode.modrm.mod) { + case 1: + imm=(Bit8s)decode_fetchb(); + break; + case 2: { + Bitu val; + // try to get a pointer to the next dword code position + if (decode_fetchd_imm(val)) { + // succeeded, use the pointer to avoid code invalidation + if (!addseg) { + gen_mov_word_to_reg(ea_reg,DRCD_REG(base_reg),true); + gen_add(ea_reg,(void*)val); + } else { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + gen_add(ea_reg,DRCD_REG(base_reg)); + gen_add(ea_reg,(void*)val); + } + return; + } + // couldn't get a pointer, use the current value + imm=(Bit32s)val; + break; + } + } + + if (!addseg) { + gen_mov_word_to_reg(ea_reg,DRCD_REG(base_reg),true); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + } else { + gen_mov_word_to_reg(ea_reg,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : seg_base),true); + gen_add(ea_reg,DRCD_REG(base_reg)); + if (imm) gen_add_imm(ea_reg,(Bit32u)imm); + } + } +} + + + +// add code that checks if port access is allowed +// the port is given in a register +static void dyn_add_iocheck(HostReg reg_port,Bitu access_size) { + if (cpu.pmode) { + gen_call_function_RI((void *)&CPU_IO_Exception,reg_port,access_size); + dyn_check_exception(FC_RETOP); + } +} + +// add code that checks if port access is allowed +// the port is a constant +static void dyn_add_iocheck_var(Bit8u accessed_port,Bitu access_size) { + if (cpu.pmode) { + gen_call_function_II((void *)&CPU_IO_Exception,accessed_port,access_size); + dyn_check_exception(FC_RETOP); + } +} + + + +// save back the address register +static void gen_protect_addr_reg(void) { + gen_mov_word_from_reg(FC_ADDR,&core_dynrec.protected_regs[FC_ADDR],true); +} + +// restore the address register +static void gen_restore_addr_reg(void) { + gen_mov_word_to_reg(FC_ADDR,&core_dynrec.protected_regs[FC_ADDR],true); +} + +// save back an arbitrary register +static void gen_protect_reg(HostReg reg) { + gen_mov_word_from_reg(reg,&core_dynrec.protected_regs[reg],true); +} + +// restore an arbitrary register +static void gen_restore_reg(HostReg reg) { + gen_mov_word_to_reg(reg,&core_dynrec.protected_regs[reg],true); +} + +// restore an arbitrary register into a different register +static void gen_restore_reg(HostReg reg,HostReg dest_reg) { + gen_mov_word_to_reg(dest_reg,&core_dynrec.protected_regs[reg],true); +} + + + +#include "lazyflags.h" + +// flags optimization functions +// they try to find out if a function can be replaced by another +// one that does not generate any flags at all + +static Bitu mf_functions_num=0; +static struct { + DRC_PTR_SIZE_IM pos; + Bit32u fct_ptr; +} mf_functions[64]; + +static void InitFlagsOptimization(void) { + mf_functions_num=0; +} + +// replace all queued functions with their simpler variants +// because the current instruction destroys all condition flags and +// the flags are not required before +static void InvalidateFlags(void) { +#ifdef DRC_FLAGS_INVALIDATION + for (Bitu ct=0; ct>2)&1)); + dyn_dop_byte_gencall(op); + + if ((op!=DOP_CMP) && (op!=DOP_TEST)) { + gen_restore_addr_reg(); + dyn_write_byte(FC_ADDR,FC_RETOP); + } + } else { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + gen_mov_byte_to_reg_low_canuseword(FC_OP2,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + dyn_dop_byte_gencall(op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} + +static void dyn_dop_ebgb_mov(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_mov_byte_to_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + dyn_write_byte(FC_ADDR,FC_TMP_BA1); + } else { + gen_mov_byte_to_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} + +static void dyn_dop_ebib_mov(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_mov_byte_to_reg_low_imm(FC_TMP_BA1,decode_fetchb()); + dyn_write_byte(FC_ADDR,FC_TMP_BA1); + } else { + gen_mov_byte_to_reg_low_imm(FC_TMP_BA1,decode_fetchb()); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} + +static void dyn_dop_ebgb_xchg(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_byte(FC_ADDR,FC_TMP_BA1); + gen_mov_byte_to_reg_low(FC_TMP_BA2,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + gen_restore_addr_reg(); + dyn_write_byte(FC_ADDR,FC_TMP_BA2); + } else { + gen_mov_byte_to_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + gen_mov_byte_to_reg_low(FC_TMP_BA2,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + gen_mov_byte_from_reg_low(FC_TMP_BA2,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} + +static void dyn_dop_gbeb(DualOps op) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_byte_canuseword(FC_ADDR,FC_OP2); + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + dyn_dop_byte_gencall(op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + } else { + gen_mov_byte_to_reg_low_canuseword(FC_OP2,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + dyn_dop_byte_gencall(op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + } +} + +static void dyn_dop_gbeb_mov(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_byte(FC_ADDR,FC_TMP_BA1); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + } else { + gen_mov_byte_to_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.reg&3,(decode.modrm.reg>>2)&1)); + } +} + +static void dyn_dop_evgv(DualOps op) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + gen_protect_addr_reg(); + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + dyn_dop_word_gencall(op,decode.big_op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) { + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + dyn_dop_word_gencall(op,decode.big_op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + +static void dyn_dop_evgv_mov(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + dyn_write_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + +static void dyn_dop_eviv_mov(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP1,decode_fetchd()); + else gen_mov_word_to_reg_imm(FC_OP1,decode_fetchw()); + dyn_write_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP1,decode_fetchd()); + else gen_mov_word_to_reg_imm(FC_OP1,decode_fetchw()); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + +static void dyn_dop_evgv_xchg(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + + gen_protect_reg(FC_OP1); + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_OP2,decode.big_op); + gen_restore_reg(FC_OP1); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + gen_mov_word_from_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + +static void dyn_xchg_ax(Bit8u reg) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,decode.big_op),decode.big_op); + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); + gen_mov_word_from_reg(FC_OP2,DRCD_REG_WORD(DRC_REG_EAX,decode.big_op),decode.big_op); +} + +static void dyn_dop_gvev(DualOps op) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP2,decode.big_op); + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + dyn_dop_word_gencall(op,decode.big_op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) { + gen_restore_addr_reg(); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } + } else { + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + dyn_dop_word_gencall(op,decode.big_op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } +} + +static void dyn_dop_gvev_mov(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } +} + +static void dyn_dop_byte_imm(DualOps op,Bit8u reg,Bit8u idx) { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(reg,idx)); + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,decode_fetchb()); + dyn_dop_byte_gencall(op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(reg,idx)); +} + +static void dyn_dop_byte_imm_mem(DualOps op,Bit8u reg,Bit8u idx) { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(reg,idx)); + Bitu val; + if (decode_fetchb_imm(val)) { + gen_mov_byte_to_reg_low_canuseword(FC_OP2,(void*)val); + } else { + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,(Bit8u)val); + } + dyn_dop_byte_gencall(op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(reg,idx)); +} + +static void dyn_prep_word_imm(Bit8u reg) { + Bitu val; + if (decode.big_op) { + if (decode_fetchd_imm(val)) { + gen_mov_word_to_reg(FC_OP2,(void*)val,true); + return; + } + } else { + if (decode_fetchw_imm(val)) { + gen_mov_word_to_reg(FC_OP2,(void*)val,false); + return; + } + } + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)val); + else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)val); +} + +static void dyn_dop_word_imm(DualOps op,Bit8u reg) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); + dyn_prep_word_imm(reg); + dyn_dop_word_gencall(op,decode.big_op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); +} + +static void dyn_dop_word_imm_old(DualOps op,Bit8u reg,Bitu imm) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm); + else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)imm); + dyn_dop_word_gencall(op,decode.big_op); + if ((op!=DOP_CMP) && (op!=DOP_TEST)) gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); +} + +static void dyn_mov_byte_imm(Bit8u reg,Bit8u idx,Bit8u imm) { + gen_mov_byte_to_reg_low_imm(FC_TMP_BA1,imm); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(reg,idx)); +} + +static void dyn_mov_word_imm(Bit8u reg) { + Bitu val; + if (decode.big_op) { + if (decode_fetchd_imm(val)) { + gen_mov_word_to_reg(FC_OP1,(void*)val,true); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(reg,true),true); + return; + } + } else { + if (decode_fetchw_imm(val)) { + gen_mov_word_to_reg(FC_OP1,(void*)val,false); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(reg,false),false); + return; + } + } + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP1,(Bit32u)val); + else gen_mov_word_to_reg_imm(FC_OP1,(Bit16u)val); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); +} + + +static void dyn_sop_word(SingleOps op,Bit8u reg) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); + dyn_sop_word_gencall(op,decode.big_op); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); +} + + +static void dyn_mov_byte_al_direct(Bitu imm) { + gen_mov_word_to_reg(FC_ADDR,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS),true); + gen_add_imm(FC_ADDR,imm); + dyn_read_byte(FC_ADDR,FC_TMP_BA1); + gen_mov_byte_from_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(DRC_REG_EAX,0)); +} + +static void dyn_mov_byte_ax_direct(Bitu imm) { + gen_mov_word_to_reg(FC_ADDR,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS),true); + gen_add_imm(FC_ADDR,imm); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,decode.big_op),decode.big_op); +} + +static void dyn_mov_byte_direct_al() { + gen_mov_word_to_reg(FC_ADDR,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS),true); + if (decode.big_addr) { + Bitu val; + if (decode_fetchd_imm(val)) { + gen_add(FC_ADDR,(void*)val); + } else { + gen_add_imm(FC_ADDR,(Bit32u)val); + } + } else { + gen_add_imm(FC_ADDR,decode_fetchw()); + } + gen_mov_byte_to_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(DRC_REG_EAX,0)); + dyn_write_byte(FC_ADDR,FC_TMP_BA1); +} + +static void dyn_mov_byte_direct_ax(Bitu imm) { + gen_mov_word_to_reg(FC_ADDR,DRCD_SEG_PHYS(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS),true); + gen_add_imm(FC_ADDR,imm); + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,decode.big_op),decode.big_op); + dyn_write_word(FC_ADDR,FC_OP1,decode.big_op); +} + + +static void dyn_movx_ev_gb(bool sign) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_byte(FC_ADDR,FC_TMP_BA1); + gen_extend_byte(sign,FC_TMP_BA1); + gen_mov_word_from_reg(FC_TMP_BA1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } else { + gen_mov_byte_to_reg_low(FC_TMP_BA1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + gen_extend_byte(sign,FC_TMP_BA1); + gen_mov_word_from_reg(FC_TMP_BA1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } +} + +static void dyn_movx_ev_gw(bool sign) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_OP1,false); + gen_extend_word(sign,FC_OP1); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,false),false); + gen_extend_word(sign,FC_OP1); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } +} + + +static void dyn_mov_ev_seg(void) { + dyn_get_modrm(); + gen_mov_word_to_reg(FC_OP1,DRCD_SEG_VAL(decode.modrm.reg),false); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_write_word(FC_ADDR,FC_OP1,false); + } else { + if (decode.big_op) gen_extend_word(false,FC_OP1); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + + +static void dyn_push_seg(Bit8u seg) { + gen_mov_word_to_reg(FC_OP1,DRCD_SEG_VAL(seg),false); + if (decode.big_op) { + gen_extend_word(false,FC_OP1); + gen_call_function_raw((void*)&dynrec_push_dword); + } else { + gen_call_function_raw((void*)&dynrec_push_word); + } +} + +static void dyn_pop_seg(Bit8u seg) { + gen_call_function_II((void *)&CPU_PopSeg,seg,decode.big_op); + if (cpu.pmode) dyn_check_exception(FC_RETOP); +} + +static void dyn_push_reg(Bit8u reg) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); + if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword); + else gen_call_function_raw((void*)&dynrec_push_word); +} + +static void dyn_pop_reg(Bit8u reg) { + if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword); + else gen_call_function_raw((void*)&dynrec_pop_word); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(reg,decode.big_op),decode.big_op); +} + +static void dyn_push_byte_imm(Bit8s imm) { + gen_mov_dword_to_reg_imm(FC_OP1,(Bit32u)imm); + if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword); + else gen_call_function_raw((void*)&dynrec_push_word); +} + +static void dyn_push_word_imm(Bitu imm) { + if (decode.big_op) { + gen_mov_dword_to_reg_imm(FC_OP1,imm); + gen_call_function_raw((void*)&dynrec_push_dword); + } else { + gen_mov_word_to_reg_imm(FC_OP1,(Bit16u)imm); + gen_call_function_raw((void*)&dynrec_push_word); + } +} + +static void dyn_pop_ev(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { +/* dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); // dummy read to trigger possible page faults */ + if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword); + else gen_call_function_raw((void*)&dynrec_pop_word); + dyn_fill_ea(FC_ADDR); +// gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } else { + if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword); + else gen_call_function_raw((void*)&dynrec_pop_word); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + + +static void dyn_segprefix(Bit8u seg) { + if (GCC_UNLIKELY(decode.seg_prefix_used)) IllegalOptionDynrec("dyn_segprefix"); + decode.seg_prefix=seg; + decode.seg_prefix_used=true; +} + + static void dyn_mov_seg_ev(void) { + dyn_get_modrm(); + if (GCC_UNLIKELY(decode.modrm.reg==DRC_SEG_CS)) IllegalOptionDynrec("dyn_mov_seg_ev"); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_RETOP,false); + } else { + gen_mov_word_to_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,false),false); + } + gen_call_function_IR((void *)&CPU_SetSegGeneral,decode.modrm.reg,FC_RETOP); + if (cpu.pmode) dyn_check_exception(FC_RETOP); +} + +static void dyn_load_seg_off_ea(Bit8u seg) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + gen_protect_reg(FC_OP1); + + gen_restore_addr_reg(); + gen_add_imm(FC_ADDR,decode.big_op ? 4:2); + dyn_read_word(FC_ADDR,FC_RETOP,false); + + gen_call_function_IR((void *)&CPU_SetSegGeneral,seg,FC_RETOP); + if (cpu.pmode) dyn_check_exception(FC_RETOP); + + gen_restore_reg(FC_OP1); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + } else { + IllegalOptionDynrec("dyn_load_seg_off_ea"); + } +} + + + +static void dyn_imul_gvev(Bitu immsize) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } + + switch (immsize) { + case 0: + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + break; + case 1: + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit8s)decode_fetchb()); + else gen_mov_word_to_reg_imm(FC_OP2,(Bit8s)decode_fetchb()); + break; + case 2: + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit16s)decode_fetchw()); + else gen_mov_word_to_reg_imm(FC_OP2,(Bit16s)decode_fetchw()); + break; + case 4: + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32s)decode_fetchd()); + else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)((Bit32s)decode_fetchd())); + break; + } + + if (decode.big_op) gen_call_function_raw((void*)dynrec_dimul_dword); + else gen_call_function_raw((void*)dynrec_dimul_word); + + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); +} + +static void dyn_dshift_ev_gv(bool left,bool immediate) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } + gen_mov_word_to_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + if (immediate) gen_mov_byte_to_reg_low_imm(FC_RETOP,decode_fetchb()); + else gen_mov_byte_to_reg_low(FC_RETOP,DRCD_REG_BYTE(DRC_REG_ECX,0)); + if (decode.big_op) dyn_dpshift_dword_gencall(left); + else dyn_dpshift_word_gencall(left); + + if (decode.modrm.mod<3) { + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } else { + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + + +static void dyn_grp1_eb_ib(void) { + dyn_get_modrm(); + DualOps op=grp1_table[decode.modrm.reg]; + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_byte_canuseword(FC_ADDR,FC_OP1); + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,decode_fetchb()); + dyn_dop_byte_gencall(op); + + if ((op!=DOP_CMP) && (op!=DOP_TEST)) { + gen_restore_addr_reg(); + dyn_write_byte(FC_ADDR,FC_RETOP); + } + } else { + dyn_dop_byte_imm_mem(op,decode.modrm.rm&3,(decode.modrm.rm>>2)&1); + } +} + +static void dyn_grp1_ev_iv(bool withbyte) { + dyn_get_modrm(); + DualOps op=grp1_table[decode.modrm.reg]; + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + + if (!withbyte) { + dyn_prep_word_imm(FC_OP2); + } else { + Bits imm=(Bit8s)decode_fetchb(); + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm); + else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)imm); + } + + dyn_dop_word_gencall(op,decode.big_op); + + if ((op!=DOP_CMP) && (op!=DOP_TEST)) { + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } + } else { + if (!withbyte) { + dyn_dop_word_imm(op,decode.modrm.rm); + } else { + Bits imm=withbyte ? (Bit8s)decode_fetchb() : (decode.big_op ? decode_fetchd(): decode_fetchw()); + dyn_dop_word_imm_old(op,decode.modrm.rm,imm); + } + } +} + + +static void dyn_grp2_eb(grp2_types type) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_byte_canuseword(FC_ADDR,FC_OP1); + } else { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } + switch (type) { + case grp2_1: + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,1); + dyn_shift_byte_gencall((ShiftOps)decode.modrm.reg); + break; + case grp2_imm: { + Bit8u imm=decode_fetchb(); + if (imm) { + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,imm&0x1f); + dyn_shift_byte_gencall((ShiftOps)decode.modrm.reg); + } else return; + } + break; + case grp2_cl: + gen_mov_byte_to_reg_low_canuseword(FC_OP2,DRCD_REG_BYTE(DRC_REG_ECX,0)); + gen_and_imm(FC_OP2,0x1f); + dyn_shift_byte_gencall((ShiftOps)decode.modrm.reg); + break; + } + if (decode.modrm.mod<3) { + gen_restore_addr_reg(); + dyn_write_byte(FC_ADDR,FC_RETOP); + } else { + gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} + +static void dyn_grp2_ev(grp2_types type) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } + switch (type) { + case grp2_1: + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,1); + dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op); + break; + case grp2_imm: { + Bitu val; + if (decode_fetchb_imm(val)) { + gen_mov_byte_to_reg_low_canuseword(FC_OP2,(void*)val); + gen_and_imm(FC_OP2,0x1f); + dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op); + break; + } + Bit8u imm=(Bit8u)val; + if (imm) { + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,imm&0x1f); + dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op); + } else return; + } + break; + case grp2_cl: + gen_mov_byte_to_reg_low_canuseword(FC_OP2,DRCD_REG_BYTE(DRC_REG_ECX,0)); + gen_and_imm(FC_OP2,0x1f); + dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op); + break; + } + if (decode.modrm.mod<3) { + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } else { + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + + +static void dyn_grp3_eb(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + if ((decode.modrm.reg==2) || (decode.modrm.reg==3)) gen_protect_addr_reg(); + dyn_read_byte_canuseword(FC_ADDR,FC_OP1); + } else { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } + switch (decode.modrm.reg) { + case 0x0: // test eb,ib + gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,decode_fetchb()); + dyn_dop_byte_gencall(DOP_TEST); + return; + case 0x2: // NOT Eb + dyn_sop_byte_gencall(SOP_NOT); + break; + case 0x3: // NEG Eb + dyn_sop_byte_gencall(SOP_NEG); + break; + case 0x4: // mul Eb + gen_call_function_raw((void*)&dynrec_mul_byte); + return; + case 0x5: // imul Eb + gen_call_function_raw((void*)&dynrec_imul_byte); + return; + case 0x6: // div Eb + gen_call_function_raw((void*)&dynrec_div_byte); + dyn_check_exception(FC_RETOP); + return; + case 0x7: // idiv Eb + gen_call_function_raw((void*)&dynrec_idiv_byte); + dyn_check_exception(FC_RETOP); + return; + } + // Save the result if memory op + if (decode.modrm.mod<3) { + gen_restore_addr_reg(); + dyn_write_byte(FC_ADDR,FC_RETOP); + } else { + gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} + +static void dyn_grp3_ev(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + if ((decode.modrm.reg==2) || (decode.modrm.reg==3)) gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } + switch (decode.modrm.reg) { + case 0x0: // test ev,iv + if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,decode_fetchd()); + else gen_mov_word_to_reg_imm(FC_OP2,decode_fetchw()); + dyn_dop_word_gencall(DOP_TEST,decode.big_op); + return; + case 0x2: // NOT Ev + dyn_sop_word_gencall(SOP_NOT,decode.big_op); + break; + case 0x3: // NEG Eb + dyn_sop_word_gencall(SOP_NEG,decode.big_op); + break; + case 0x4: // mul Eb + if (decode.big_op) gen_call_function_raw((void*)&dynrec_mul_dword); + else gen_call_function_raw((void*)&dynrec_mul_word); + return; + case 0x5: // imul Eb + if (decode.big_op) gen_call_function_raw((void*)&dynrec_imul_dword); + else gen_call_function_raw((void*)&dynrec_imul_word); + return; + case 0x6: // div Eb + if (decode.big_op) gen_call_function_raw((void*)&dynrec_div_dword); + else gen_call_function_raw((void*)&dynrec_div_word); + dyn_check_exception(FC_RETOP); + return; + case 0x7: // idiv Eb + if (decode.big_op) gen_call_function_raw((void*)&dynrec_idiv_dword); + else gen_call_function_raw((void*)&dynrec_idiv_word); + dyn_check_exception(FC_RETOP); + return; + } + // Save the result if memory op + if (decode.modrm.mod<3) { + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } else { + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } +} + + +static bool dyn_grp4_eb(void) { + dyn_get_modrm(); + switch (decode.modrm.reg) { + case 0x0://INC Eb + case 0x1://DEC Eb + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_byte_canuseword(FC_ADDR,FC_OP1); + dyn_sop_byte_gencall(decode.modrm.reg==0 ? SOP_INC : SOP_DEC); + gen_restore_addr_reg(); + dyn_write_byte(FC_ADDR,FC_RETOP); + } else { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + dyn_sop_byte_gencall(decode.modrm.reg==0 ? SOP_INC : SOP_DEC); + gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } + break; + case 0x7: //CALBACK Iw + gen_mov_direct_dword(&core_dynrec.callback,decode_fetchw()); + dyn_set_eip_end(); + dyn_reduce_cycles(); + dyn_return(BR_CallBack); + dyn_closeblock(); + return true; + default: + IllegalOptionDynrec("dyn_grp4_eb"); + break; + } + return false; +} + +static bool dyn_grp4_ev(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + if (decode.modrm.reg<2) gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); + } else { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } + switch (decode.modrm.reg) { + case 0x0://INC Ev + case 0x1://DEC Ev + dyn_sop_word_gencall(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,decode.big_op); + if (decode.modrm.mod<3) { + gen_restore_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + } else { + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,decode.big_op),decode.big_op); + } + break; + case 0x2: // CALL Ev + gen_mov_regs(FC_ADDR,FC_OP1); + gen_protect_addr_reg(); + gen_mov_word_to_reg(FC_OP1,decode.big_op?(void*)(®_eip):(void*)(®_ip),decode.big_op); + gen_add_imm(FC_OP1,(Bit32u)(decode.code-decode.code_start)); + if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword); + else gen_call_function_raw((void*)&dynrec_push_word); + + gen_restore_addr_reg(); + gen_mov_word_from_reg(FC_ADDR,decode.big_op?(void*)(®_eip):(void*)(®_ip),decode.big_op); + return true; + case 0x4: // JMP Ev + gen_mov_word_from_reg(FC_OP1,decode.big_op?(void*)(®_eip):(void*)(®_ip),decode.big_op); + return true; + case 0x3: // CALL Ep + case 0x5: // JMP Ep + if (!decode.big_op) gen_extend_word(false,FC_OP1); + gen_protect_reg(FC_OP1); + gen_add_imm(FC_ADDR,decode.big_op?4:2); + dyn_read_word(FC_ADDR,FC_OP2,decode.big_op); + gen_extend_word(false,FC_OP2); + + dyn_set_eip_last_end(FC_RETOP); + gen_restore_reg(FC_OP1,FC_ADDR); + gen_call_function_IRRR(decode.modrm.reg == 3 ? (void*)(&CPU_CALL) : (void*)(&CPU_JMP), + decode.big_op,FC_OP2,FC_ADDR,FC_RETOP); + return true; + case 0x6: // PUSH Ev + if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword); + else gen_call_function_raw((void*)&dynrec_push_word); + break; + default: + IllegalOptionDynrec("dyn_grp4_ev"); + } + return false; +} + + +static bool dyn_grp6(void) { + dyn_get_modrm(); + switch (decode.modrm.reg) { + case 0x00: // SLDT + case 0x01: // STR + if (decode.modrm.reg==0) gen_call_function_raw((void*)CPU_SLDT); + else gen_call_function_raw((void*)CPU_STR); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_write_word(FC_ADDR,FC_RETOP,false); + } else { + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,false),false); + } + break; + case 0x02: // LLDT + case 0x03: // LTR + case 0x04: // VERR + case 0x05: // VERW + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_RETOP,false); + } else { + gen_mov_word_to_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,false),false); + } + gen_extend_word(false,FC_RETOP); + switch (decode.modrm.reg) { + case 0x02: // LLDT +// if (cpu.cpl) return CPU_PrepareException(EXCEPTION_GP,0); + if (cpu.cpl) E_Exit("lldt cpl>0"); + gen_call_function_R((void*)CPU_LLDT,FC_RETOP); + dyn_check_exception(FC_RETOP); + break; + case 0x03: // LTR +// if (cpu.cpl) return CPU_PrepareException(EXCEPTION_GP,0); + if (cpu.cpl) E_Exit("ltr cpl>0"); + gen_call_function_R((void*)CPU_LTR,FC_RETOP); + dyn_check_exception(FC_RETOP); + break; + case 0x04: // VERR + gen_call_function_R((void*)CPU_VERR,FC_RETOP); + break; + case 0x05: // VERW + gen_call_function_R((void*)CPU_VERW,FC_RETOP); + break; + } + break; + default: IllegalOptionDynrec("dyn_grp6"); + } + return false; +} + +static bool dyn_grp7(void) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + switch (decode.modrm.reg) { + case 0x00: // SGDT + gen_call_function_raw((void*)CPU_SGDT_limit); + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,false); + gen_call_function_raw((void*)CPU_SGDT_base); + gen_restore_addr_reg(); + gen_add_imm(FC_ADDR,2); + dyn_write_word(FC_ADDR,FC_RETOP,true); + break; + case 0x01: // SIDT + gen_call_function_raw((void*)CPU_SIDT_limit); + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_write_word(FC_ADDR,FC_RETOP,false); + gen_call_function_raw((void*)CPU_SIDT_base); + gen_restore_addr_reg(); + gen_add_imm(FC_ADDR,2); + dyn_write_word(FC_ADDR,FC_RETOP,true); + break; + case 0x02: // LGDT + case 0x03: // LIDT +// if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP); + if (cpu.pmode && cpu.cpl) IllegalOptionDynrec("lgdt nonpriviledged"); + dyn_fill_ea(FC_ADDR); + gen_protect_addr_reg(); + dyn_read_word(FC_ADDR,FC_OP1,false); + gen_extend_word(false,FC_OP1); + gen_protect_reg(FC_OP1); + + gen_restore_addr_reg(); + gen_add_imm(FC_ADDR,2); + dyn_read_word(FC_ADDR,FC_OP2,true); + if (!decode.big_op) gen_and_imm(FC_OP2,0xffffff); + + gen_restore_reg(FC_OP1); + if (decode.modrm.reg==2) gen_call_function_RR((void*)CPU_LGDT,FC_OP1,FC_OP2); + else gen_call_function_RR((void*)CPU_LIDT,FC_OP1,FC_OP2); + break; + case 0x04: // SMSW + gen_call_function_raw((void*)CPU_SMSW); + dyn_fill_ea(FC_ADDR); + dyn_write_word(FC_ADDR,FC_RETOP,false); + break; + case 0x06: // LMSW + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_RETOP,false); + gen_call_function_R((void*)CPU_LMSW,FC_RETOP); + dyn_check_exception(FC_RETOP); + dyn_set_eip_end(); + dyn_reduce_cycles(); + dyn_return(BR_Normal); + dyn_closeblock(); + return true; + default: IllegalOptionDynrec("dyn_grp7_1"); + } + } else { + switch (decode.modrm.reg) { + case 0x04: // SMSW + gen_call_function_raw((void*)CPU_SMSW); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,false),false); + break; + case 0x06: // LMSW + gen_mov_word_to_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,false),false); + gen_call_function_R((void*)CPU_LMSW,FC_RETOP); + dyn_check_exception(FC_RETOP); + dyn_set_eip_end(); + dyn_reduce_cycles(); + dyn_return(BR_Normal); + dyn_closeblock(); + return true; + default: IllegalOptionDynrec("dyn_grp7_2"); + } + } + return false; +} + + +/* +static void dyn_larlsl(bool is_lar) { + dyn_get_modrm(); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_read_word(FC_ADDR,FC_RETOP,false); + } else { + gen_mov_word_to_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,false),false); + } + gen_extend_word(false,FC_RETOP); + if (is_lar) gen_call_function((void*)CPU_LAR,"%R%A",FC_RETOP,(DRC_PTR_SIZE_IM)&core_dynrec.readdata); + else gen_call_function((void*)CPU_LSL,"%R%A",FC_RETOP,(DRC_PTR_SIZE_IM)&core_dynrec.readdata); + DRC_PTR_SIZE_IM brnz=gen_create_branch_on_nonzero(FC_RETOP,true); + gen_mov_word_to_reg(FC_OP2,&core_dynrec.readdata,true); + gen_mov_word_from_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.reg,decode.big_op),decode.big_op); + gen_fill_branch(brnz); +} +*/ + + +static void dyn_mov_from_crx(void) { + dyn_get_modrm(); + gen_call_function_IA((void*)CPU_READ_CRX,decode.modrm.reg,(DRC_PTR_SIZE_IM)&core_dynrec.readdata); + dyn_check_exception(FC_RETOP); + gen_mov_word_to_reg(FC_OP2,&core_dynrec.readdata,true); + gen_mov_word_from_reg(FC_OP2,DRCD_REG_WORD(decode.modrm.rm,true),true); +} + +static void dyn_mov_to_crx(void) { + dyn_get_modrm(); + gen_mov_word_to_reg(FC_RETOP,DRCD_REG_WORD(decode.modrm.rm,true),true); + gen_call_function_IR((void*)CPU_WRITE_CRX,decode.modrm.reg,FC_RETOP); + dyn_check_exception(FC_RETOP); + dyn_set_eip_end(); + dyn_reduce_cycles(); + dyn_return(BR_Normal); + dyn_closeblock(); +} + + +static void dyn_cbw(void) { + if (decode.big_op) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,false),false); + gen_call_function_raw((void *)&dynrec_cwde); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(DRC_REG_EAX,true),true); + } else { + gen_mov_byte_to_reg_low_canuseword(FC_OP1,DRCD_REG_BYTE(DRC_REG_EAX,0)); + gen_call_function_raw((void *)&dynrec_cbw); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(DRC_REG_EAX,false),false); + } +} + +static void dyn_cwd(void) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,decode.big_op),decode.big_op); + if (decode.big_op) { + gen_call_function_raw((void *)&dynrec_cdq); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(DRC_REG_EDX,true),true); + } else { + gen_call_function_raw((void *)&dynrec_cwd); + gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(DRC_REG_EDX,false),false); + } +} + +static void dyn_sahf(void) { + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,false),false); + gen_call_function_raw((void *)&dynrec_sahf); + InvalidateFlags(); +} + + +static void dyn_exit_link(Bits eip_change) { + gen_add_direct_word(®_eip,(decode.code-decode.code_start)+eip_change,decode.big_op); + dyn_reduce_cycles(); + gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start)); + dyn_closeblock(); +} + + +static void dyn_branched_exit(BranchTypes btype,Bit32s eip_add) { + Bitu eip_base=decode.code-decode.code_start; + dyn_reduce_cycles(); + + dyn_branchflag_to_reg(btype); + DRC_PTR_SIZE_IM data=gen_create_branch_on_nonzero(FC_RETOP,true); + + // Branch not taken + gen_add_direct_word(®_eip,eip_base,decode.big_op); + gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start)); + gen_fill_branch(data); + + // Branch taken + gen_add_direct_word(®_eip,eip_base+eip_add,decode.big_op); + gen_jmp_ptr(&decode.block->link[1].to,offsetof(CacheBlockDynRec,cache.start)); + dyn_closeblock(); +} + +/* +static void dyn_set_byte_on_condition(BranchTypes btype) { + dyn_get_modrm(); + dyn_branchflag_to_reg(btype); + gen_and_imm(FC_RETOP,1); + if (decode.modrm.mod<3) { + dyn_fill_ea(FC_ADDR); + dyn_write_byte(FC_ADDR,FC_RETOP); + } else { + gen_mov_byte_from_reg_low(FC_RETOP,DRCD_REG_BYTE(decode.modrm.rm&3,(decode.modrm.rm>>2)&1)); + } +} +*/ + +static void dyn_loop(LoopTypes type) { + dyn_reduce_cycles(); + Bits eip_add=(Bit8s)decode_fetchb(); + Bitu eip_base=decode.code-decode.code_start; + DRC_PTR_SIZE_IM branch1=0; + DRC_PTR_SIZE_IM branch2=0; + switch (type) { + case LOOP_E: + dyn_branchflag_to_reg(BR_NZ); + branch1=gen_create_branch_on_nonzero(FC_RETOP,true); + break; + case LOOP_NE: + dyn_branchflag_to_reg(BR_Z); + branch1=gen_create_branch_on_nonzero(FC_RETOP,true); + break; + } + switch (type) { + case LOOP_E: + case LOOP_NE: + case LOOP_NONE: + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + gen_add_imm(FC_OP1,(Bit32u)(-1)); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + branch2=gen_create_branch_on_zero(FC_OP1,decode.big_addr); + break; + case LOOP_JCXZ: + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + branch2=gen_create_branch_on_nonzero(FC_OP1,decode.big_addr); + break; + } + gen_add_direct_word(®_eip,eip_base+eip_add,true); + gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start)); + if (branch1) { + gen_fill_branch(branch1); + gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + gen_add_imm(FC_OP1,(Bit32u)(-1)); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + } + // Branch taken + gen_fill_branch(branch2); + gen_add_direct_word(®_eip,eip_base,decode.big_op); + gen_jmp_ptr(&decode.block->link[1].to,offsetof(CacheBlockDynRec,cache.start)); + dyn_closeblock(); +} + + +static void dyn_ret_near(Bitu bytes) { + dyn_reduce_cycles(); + + if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword); + else { + gen_call_function_raw((void*)&dynrec_pop_word); + gen_extend_word(false,FC_RETOP); + } + gen_mov_word_from_reg(FC_RETOP,decode.big_op?(void*)(®_eip):(void*)(®_ip),true); + + if (bytes) gen_add_direct_word(®_esp,bytes,true); + dyn_return(BR_Normal); + dyn_closeblock(); +} + +static void dyn_call_near_imm(void) { + Bits imm; + if (decode.big_op) imm=(Bit32s)decode_fetchd(); + else imm=(Bit16s)decode_fetchw(); + dyn_set_eip_end(FC_OP1); + if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword); + else gen_call_function_raw((void*)&dynrec_push_word); + + dyn_set_eip_end(FC_OP1,imm); + gen_mov_word_from_reg(FC_OP1,decode.big_op?(void*)(®_eip):(void*)(®_ip),decode.big_op); + + dyn_reduce_cycles(); + gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start)); + dyn_closeblock(); +} + +static void dyn_ret_far(Bitu bytes) { + dyn_reduce_cycles(); + dyn_set_eip_last_end(FC_RETOP); + gen_call_function_IIR((void*)&CPU_RET,decode.big_op,bytes,FC_RETOP); + dyn_return(BR_Normal); + dyn_closeblock(); +} + +static void dyn_call_far_imm(void) { + Bitu sel,off; + off=decode.big_op ? decode_fetchd() : decode_fetchw(); + sel=decode_fetchw(); + dyn_reduce_cycles(); + dyn_set_eip_last_end(FC_RETOP); + gen_call_function_IIIR((void*)&CPU_CALL,decode.big_op,sel,off,FC_RETOP); + dyn_return(BR_Normal); + dyn_closeblock(); +} + +static void dyn_jmp_far_imm(void) { + Bitu sel,off; + off=decode.big_op ? decode_fetchd() : decode_fetchw(); + sel=decode_fetchw(); + dyn_reduce_cycles(); + dyn_set_eip_last_end(FC_RETOP); + gen_call_function_IIIR((void*)&CPU_JMP,decode.big_op,sel,off,FC_RETOP); + dyn_return(BR_Normal); + dyn_closeblock(); +} + +static void dyn_iret(void) { + dyn_reduce_cycles(); + dyn_set_eip_last_end(FC_RETOP); + gen_call_function_IR((void*)&CPU_IRET,decode.big_op,FC_RETOP); + dyn_return(BR_Iret); + dyn_closeblock(); +} + +static void dyn_interrupt(Bit8u num) { + dyn_reduce_cycles(); + dyn_set_eip_last_end(FC_RETOP); + gen_call_function_IIR((void*)&CPU_Interrupt,num,CPU_INT_SOFTWARE,FC_RETOP); + dyn_return(BR_Normal); + dyn_closeblock(); +} + + + +static void dyn_string(StringOps op) { + if (decode.rep) gen_mov_word_to_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + else gen_mov_dword_to_reg_imm(FC_OP1,1); + gen_mov_word_to_reg(FC_OP2,&cpu.direction,true); + Bit8u di_base_addr=decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS; + switch (op) { + case STR_MOVSB: + if (decode.big_addr) gen_call_function_mm((void*)&dynrec_movsb_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + else gen_call_function_mm((void*)&dynrec_movsb_word,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + break; + case STR_MOVSW: + if (decode.big_addr) gen_call_function_mm((void*)&dynrec_movsw_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + else gen_call_function_mm((void*)&dynrec_movsw_word,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + break; + case STR_MOVSD: + if (decode.big_addr) gen_call_function_mm((void*)&dynrec_movsd_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + else gen_call_function_mm((void*)&dynrec_movsd_word,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + break; + + case STR_LODSB: + if (decode.big_addr) gen_call_function_m((void*)&dynrec_lodsb_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr)); + else gen_call_function_m((void*)&dynrec_lodsb_word,(Bitu)DRCD_SEG_PHYS(di_base_addr)); + break; + case STR_LODSW: + if (decode.big_addr) gen_call_function_m((void*)&dynrec_lodsw_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr)); + else gen_call_function_m((void*)&dynrec_lodsw_word,(Bitu)DRCD_SEG_PHYS(di_base_addr)); + break; + case STR_LODSD: + if (decode.big_addr) gen_call_function_m((void*)&dynrec_lodsd_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr)); + else gen_call_function_m((void*)&dynrec_lodsd_word,(Bitu)DRCD_SEG_PHYS(di_base_addr)); + break; + + case STR_STOSB: + if (decode.big_addr) gen_call_function_m((void*)&dynrec_stosb_dword,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + else gen_call_function_m((void*)&dynrec_stosb_word,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + break; + case STR_STOSW: + if (decode.big_addr) gen_call_function_m((void*)&dynrec_stosw_dword,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + else gen_call_function_m((void*)&dynrec_stosw_word,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + break; + case STR_STOSD: + if (decode.big_addr) gen_call_function_m((void*)&dynrec_stosd_dword,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + else gen_call_function_m((void*)&dynrec_stosd_word,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES)); + break; + default: IllegalOptionDynrec("dyn_string"); + } + if (decode.rep) gen_mov_word_from_reg(FC_RETOP,DRCD_REG_WORD(DRC_REG_ECX,decode.big_addr),decode.big_addr); + + if (op +#include +#include "cross.h" +#include "mem.h" +#include "fpu.h" +#include "cpu.h" + + +static void FPU_FDECSTP(){ + TOP = (TOP - 1) & 7; +} + +static void FPU_FINCSTP(){ + TOP = (TOP + 1) & 7; +} + +static void FPU_FNSTCW(PhysPt addr){ + mem_writew(addr,fpu.cw); +} + +static void FPU_FFREE(Bitu st) { + fpu.tags[st]=TAG_Empty; +} + + +#if C_FPU_X86 +#include "../../fpu/fpu_instructions_x86.h" +#else +#include "../../fpu/fpu_instructions.h" +#endif + + +static INLINE void dyn_fpu_top() { + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + gen_add_imm(FC_OP2,decode.modrm.rm); + gen_and_imm(FC_OP2,7); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); +} + +static INLINE void dyn_fpu_top_swapped() { + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + gen_add_imm(FC_OP1,decode.modrm.rm); + gen_and_imm(FC_OP1,7); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); +} + +static void dyn_eatree() { + Bitu group=(decode.modrm.val >> 3) & 7; + switch (group){ + case 0x00: // FADD ST,STi + gen_call_function_R((void*)&FPU_FADD_EA,FC_OP1); + break; + case 0x01: // FMUL ST,STi + gen_call_function_R((void*)&FPU_FMUL_EA,FC_OP1); + break; + case 0x02: // FCOM STi + gen_call_function_R((void*)&FPU_FCOM_EA,FC_OP1); + break; + case 0x03: // FCOMP STi + gen_call_function_R((void*)&FPU_FCOM_EA,FC_OP1); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: // FSUB ST,STi + gen_call_function_R((void*)&FPU_FSUB_EA,FC_OP1); + break; + case 0x05: // FSUBR ST,STi + gen_call_function_R((void*)&FPU_FSUBR_EA,FC_OP1); + break; + case 0x06: // FDIV ST,STi + gen_call_function_R((void*)&FPU_FDIV_EA,FC_OP1); + break; + case 0x07: // FDIVR ST,STi + gen_call_function_R((void*)&FPU_FDIVR_EA,FC_OP1); + break; + default: + break; + } +} + +static void dyn_fpu_esc0(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + dyn_fpu_top(); + switch (decode.modrm.reg){ + case 0x00: //FADD ST,STi + gen_call_function_RR((void*)&FPU_FADD,FC_OP1,FC_OP2); + break; + case 0x01: // FMUL ST,STi + gen_call_function_RR((void*)&FPU_FMUL,FC_OP1,FC_OP2); + break; + case 0x02: // FCOM STi + gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2); + break; + case 0x03: // FCOMP STi + gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: // FSUB ST,STi + gen_call_function_RR((void*)&FPU_FSUB,FC_OP1,FC_OP2); + break; + case 0x05: // FSUBR ST,STi + gen_call_function_RR((void*)&FPU_FSUBR,FC_OP1,FC_OP2); + break; + case 0x06: // FDIV ST,STi + gen_call_function_RR((void*)&FPU_FDIV,FC_OP1,FC_OP2); + break; + case 0x07: // FDIVR ST,STi + gen_call_function_RR((void*)&FPU_FDIVR,FC_OP1,FC_OP2); + break; + default: + break; + } + } else { + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FLD_F32_EA,FC_ADDR); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + dyn_eatree(); + } +} + + +static void dyn_fpu_esc1(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + switch (decode.modrm.reg){ + case 0x00: /* FLD STi */ + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + gen_add_imm(FC_OP1,decode.modrm.rm); + gen_and_imm(FC_OP1,7); + gen_call_function_raw((void*)&FPU_PREP_PUSH); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2); + break; + case 0x01: /* FXCH STi */ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FXCH,FC_OP1,FC_OP2); + break; + case 0x02: /* FNOP */ + gen_call_function_raw((void*)&FPU_FNOP); + break; + case 0x03: /* FSTP STi */ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: + switch(decode.modrm.rm){ + case 0x00: /* FCHS */ + gen_call_function_raw((void*)&FPU_FCHS); + break; + case 0x01: /* FABS */ + gen_call_function_raw((void*)&FPU_FABS); + break; + case 0x02: /* UNKNOWN */ + case 0x03: /* ILLEGAL */ + LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm); + break; + case 0x04: /* FTST */ + gen_call_function_raw((void*)&FPU_FTST); + break; + case 0x05: /* FXAM */ + gen_call_function_raw((void*)&FPU_FXAM); + break; + case 0x06: /* FTSTP (cyrix)*/ + case 0x07: /* UNKNOWN */ + LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm); + break; + } + break; + case 0x05: + switch(decode.modrm.rm){ + case 0x00: /* FLD1 */ + gen_call_function_raw((void*)&FPU_FLD1); + break; + case 0x01: /* FLDL2T */ + gen_call_function_raw((void*)&FPU_FLDL2T); + break; + case 0x02: /* FLDL2E */ + gen_call_function_raw((void*)&FPU_FLDL2E); + break; + case 0x03: /* FLDPI */ + gen_call_function_raw((void*)&FPU_FLDPI); + break; + case 0x04: /* FLDLG2 */ + gen_call_function_raw((void*)&FPU_FLDLG2); + break; + case 0x05: /* FLDLN2 */ + gen_call_function_raw((void*)&FPU_FLDLN2); + break; + case 0x06: /* FLDZ*/ + gen_call_function_raw((void*)&FPU_FLDZ); + break; + case 0x07: /* ILLEGAL */ + LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm); + break; + } + break; + case 0x06: + switch(decode.modrm.rm){ + case 0x00: /* F2XM1 */ + gen_call_function_raw((void*)&FPU_F2XM1); + break; + case 0x01: /* FYL2X */ + gen_call_function_raw((void*)&FPU_FYL2X); + break; + case 0x02: /* FPTAN */ + gen_call_function_raw((void*)&FPU_FPTAN); + break; + case 0x03: /* FPATAN */ + gen_call_function_raw((void*)&FPU_FPATAN); + break; + case 0x04: /* FXTRACT */ + gen_call_function_raw((void*)&FPU_FXTRACT); + break; + case 0x05: /* FPREM1 */ + gen_call_function_raw((void*)&FPU_FPREM1); + break; + case 0x06: /* FDECSTP */ + gen_call_function_raw((void*)&FPU_FDECSTP); + break; + case 0x07: /* FINCSTP */ + gen_call_function_raw((void*)&FPU_FINCSTP); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm); + break; + } + break; + case 0x07: + switch(decode.modrm.rm){ + case 0x00: /* FPREM */ + gen_call_function_raw((void*)&FPU_FPREM); + break; + case 0x01: /* FYL2XP1 */ + gen_call_function_raw((void*)&FPU_FYL2XP1); + break; + case 0x02: /* FSQRT */ + gen_call_function_raw((void*)&FPU_FSQRT); + break; + case 0x03: /* FSINCOS */ + gen_call_function_raw((void*)&FPU_FSINCOS); + break; + case 0x04: /* FRNDINT */ + gen_call_function_raw((void*)&FPU_FRNDINT); + break; + case 0x05: /* FSCALE */ + gen_call_function_raw((void*)&FPU_FSCALE); + break; + case 0x06: /* FSIN */ + gen_call_function_raw((void*)&FPU_FSIN); + break; + case 0x07: /* FCOS */ + gen_call_function_raw((void*)&FPU_FCOS); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm); + break; + } + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm); + break; + } + } else { + switch(decode.modrm.reg){ + case 0x00: /* FLD float*/ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + dyn_fill_ea(FC_OP1); + gen_call_function_RR((void*)&FPU_FLD_F32,FC_OP1,FC_OP2); + break; + case 0x01: /* UNKNOWN */ + LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + case 0x02: /* FST float*/ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_F32,FC_ADDR); + break; + case 0x03: /* FSTP float*/ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_F32,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: /* FLDENV */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FLDENV,FC_ADDR); + break; + case 0x05: /* FLDCW */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void *)&FPU_FLDCW,FC_ADDR); + break; + case 0x06: /* FSTENV */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void *)&FPU_FSTENV,FC_ADDR); + break; + case 0x07: /* FNSTCW*/ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void *)&FPU_FNSTCW,FC_ADDR); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + } +} + +static void dyn_fpu_esc2(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + switch(decode.modrm.reg){ + case 0x05: + switch(decode.modrm.rm){ + case 0x01: /* FUCOMPP */ + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + gen_add_imm(FC_OP2,1); + gen_and_imm(FC_OP2,7); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + gen_call_function_RR((void *)&FPU_FUCOM,FC_OP1,FC_OP2); + gen_call_function_raw((void *)&FPU_FPOP); + gen_call_function_raw((void *)&FPU_FPOP); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + } else { + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FLD_I32_EA,FC_ADDR); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + dyn_eatree(); + } +} + +static void dyn_fpu_esc3(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + switch (decode.modrm.reg) { + case 0x04: + switch (decode.modrm.rm) { + case 0x00: //FNENI + case 0x01: //FNDIS + LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfuntion: %d",decode.modrm.rm); + break; + case 0x02: //FNCLEX FCLEX + gen_call_function_raw((void*)&FPU_FCLEX); + break; + case 0x03: //FNINIT FINIT + gen_call_function_raw((void*)&FPU_FINIT); + break; + case 0x04: //FNSETPM + case 0x05: //FRSTPM +// LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done"); + break; + default: + E_Exit("ESC 3:ILLEGAL OPCODE group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + } + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 3:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + } else { + switch(decode.modrm.reg){ + case 0x00: /* FILD */ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + dyn_fill_ea(FC_OP1); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + gen_call_function_RR((void*)&FPU_FLD_I32,FC_OP1,FC_OP2); + break; + case 0x01: /* FISTTP */ + LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + case 0x02: /* FIST */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_I32,FC_ADDR); + break; + case 0x03: /* FISTP */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_I32,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x05: /* FLD 80 Bits Real */ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FLD_F80,FC_ADDR); + break; + case 0x07: /* FSTP 80 Bits Real */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_F80,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + } + } +} + +static void dyn_fpu_esc4(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + switch(decode.modrm.reg){ + case 0x00: /* FADD STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FADD,FC_OP1,FC_OP2); + break; + case 0x01: /* FMUL STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FMUL,FC_OP1,FC_OP2); + break; + case 0x02: /* FCOM*/ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2); + break; + case 0x03: /* FCOMP*/ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: /* FSUBR STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FSUBR,FC_OP1,FC_OP2); + break; + case 0x05: /* FSUB STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FSUB,FC_OP1,FC_OP2); + break; + case 0x06: /* FDIVR STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FDIVR,FC_OP1,FC_OP2); + break; + case 0x07: /* FDIV STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FDIV,FC_OP1,FC_OP2); + break; + default: + break; + } + } else { + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FLD_F64_EA,FC_ADDR); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + dyn_eatree(); + } +} + +static void dyn_fpu_esc5(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + dyn_fpu_top(); + switch(decode.modrm.reg){ + case 0x00: /* FFREE STi */ + gen_call_function_R((void*)&FPU_FFREE,FC_OP2); + break; + case 0x01: /* FXCH STi*/ + gen_call_function_RR((void*)&FPU_FXCH,FC_OP1,FC_OP2); + break; + case 0x02: /* FST STi */ + gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2); + break; + case 0x03: /* FSTP STi*/ + gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: /* FUCOM STi */ + gen_call_function_RR((void*)&FPU_FUCOM,FC_OP1,FC_OP2); + break; + case 0x05: /*FUCOMP STi */ + gen_call_function_RR((void*)&FPU_FUCOM,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 5:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + } else { + switch(decode.modrm.reg){ + case 0x00: /* FLD double real*/ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + dyn_fill_ea(FC_OP1); + gen_call_function_RR((void*)&FPU_FLD_F64,FC_OP1,FC_OP2); + break; + case 0x01: /* FISTTP longint*/ + LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + case 0x02: /* FST double real*/ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_F64,FC_ADDR); + break; + case 0x03: /* FSTP double real*/ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_F64,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: /* FRSTOR */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FRSTOR,FC_ADDR); + break; + case 0x06: /* FSAVE */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FSAVE,FC_ADDR); + break; + case 0x07: /*FNSTSW */ + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + gen_call_function_R((void*)&FPU_SET_TOP,FC_OP1); + gen_mov_word_to_reg(FC_OP2,(void*)(&fpu.sw),true); + dyn_fill_ea(FC_OP1); + gen_call_function_RR((void*)&mem_writew,FC_OP1,FC_OP2); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + } + } +} + +static void dyn_fpu_esc6(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + switch(decode.modrm.reg){ + case 0x00: /*FADDP STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FADD,FC_OP1,FC_OP2); + break; + case 0x01: /* FMULP STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FMUL,FC_OP1,FC_OP2); + break; + case 0x02: /* FCOMP5*/ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2); + break; /* TODO IS THIS ALLRIGHT ????????? */ + case 0x03: /*FCOMPP*/ + if(decode.modrm.rm != 1) { + LOG(LOG_FPU,LOG_WARN)("ESC 6:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + return; + } + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + gen_add_imm(FC_OP2,1); + gen_and_imm(FC_OP2,7); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); /* extra pop at the bottom*/ + break; + case 0x04: /* FSUBRP STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FSUBR,FC_OP1,FC_OP2); + break; + case 0x05: /* FSUBP STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FSUB,FC_OP1,FC_OP2); + break; + case 0x06: /* FDIVRP STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FDIVR,FC_OP1,FC_OP2); + break; + case 0x07: /* FDIVP STi,ST*/ + dyn_fpu_top_swapped(); + gen_call_function_RR((void*)&FPU_FDIV,FC_OP1,FC_OP2); + break; + default: + break; + } + gen_call_function_raw((void*)&FPU_FPOP); + } else { + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FLD_I16_EA,FC_ADDR); + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + dyn_eatree(); + } +} + +static void dyn_fpu_esc7(){ + dyn_get_modrm(); + if (decode.modrm.val >= 0xc0) { + switch (decode.modrm.reg){ + case 0x01: /* FXCH STi*/ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FXCH,FC_OP1,FC_OP2); + break; + case 0x02: /* FSTP STi*/ + case 0x03: /* FSTP STi*/ + dyn_fpu_top(); + gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: + switch(decode.modrm.rm){ + case 0x00: /* FNSTSW AX*/ + gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); + gen_call_function_R((void*)&FPU_SET_TOP,FC_OP1); + gen_mov_word_to_reg(FC_OP1,(void*)(&fpu.sw),false); + gen_mov_word_from_reg(FC_OP1,DRCD_REG_WORD(DRC_REG_EAX,false),false); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + } else { + switch(decode.modrm.reg){ + case 0x00: /* FILD Bit16s */ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + dyn_fill_ea(FC_OP1); + gen_call_function_RR((void*)&FPU_FLD_I16,FC_OP1,FC_OP2); + break; + case 0x01: + LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + case 0x02: /* FIST Bit16s */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_I16,FC_ADDR); + break; + case 0x03: /* FISTP Bit16s */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_I16,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x04: /* FBLD packed BCD */ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + dyn_fill_ea(FC_OP1); + gen_call_function_RR((void*)&FPU_FBLD,FC_OP1,FC_OP2); + break; + case 0x05: /* FILD Bit64s */ + gen_call_function_raw((void*)&FPU_PREP_PUSH); + gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true); + dyn_fill_ea(FC_OP1); + gen_call_function_RR((void*)&FPU_FLD_I64,FC_OP1,FC_OP2); + break; + case 0x06: /* FBSTP packed BCD */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FBST,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + case 0x07: /* FISTP Bit64s */ + dyn_fill_ea(FC_ADDR); + gen_call_function_R((void*)&FPU_FST_I64,FC_ADDR); + gen_call_function_raw((void*)&FPU_FPOP); + break; + default: + LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm); + break; + } + } +} + +#endif diff --git a/src/cpu/core_dynrec/operators.h b/src/cpu/core_dynrec/operators.h new file mode 100644 index 00000000..21a6dfd4 --- /dev/null +++ b/src/cpu/core_dynrec/operators.h @@ -0,0 +1,1812 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +static Bit8u DRC_CALL_CONV dynrec_add_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=(Bit8u)(lf_var1b+lf_var2b); + lflags.type=t_ADDb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_add_byte_simple(Bit8u op1,Bit8u op2) { + return op1+op2; +} + +static Bit8u DRC_CALL_CONV dynrec_adc_byte(Bit8u op1,Bit8u op2) { + lflags.oldcf=get_CF()!=0; + lf_var1b=op1; + lf_var2b=op2; + lf_resb=(Bit8u)(lf_var1b+lf_var2b+lflags.oldcf); + lflags.type=t_ADCb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_adc_byte_simple(Bit8u op1,Bit8u op2) { + return (Bit8u)(op1+op2+(Bitu)(get_CF()!=0)); +} + +static Bit8u DRC_CALL_CONV dynrec_sub_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=(Bit8u)(lf_var1b-lf_var2b); + lflags.type=t_SUBb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_sub_byte_simple(Bit8u op1,Bit8u op2) { + return op1-op2; +} + +static Bit8u DRC_CALL_CONV dynrec_sbb_byte(Bit8u op1,Bit8u op2) { + lflags.oldcf=get_CF()!=0; + lf_var1b=op1; + lf_var2b=op2; + lf_resb=(Bit8u)(lf_var1b-(lf_var2b+lflags.oldcf)); + lflags.type=t_SBBb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_sbb_byte_simple(Bit8u op1,Bit8u op2) { + return (Bit8u)(op1-(op2+(Bitu)(get_CF()!=0))); +} + +static void DRC_CALL_CONV dynrec_cmp_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=(Bit8u)(lf_var1b-lf_var2b); + lflags.type=t_CMPb; +} + +static void DRC_CALL_CONV dynrec_cmp_byte_simple(Bit8u op1,Bit8u op2) { +} + +static Bit8u DRC_CALL_CONV dynrec_xor_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=lf_var1b ^ lf_var2b; + lflags.type=t_XORb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_xor_byte_simple(Bit8u op1,Bit8u op2) { + return op1 ^ op2; +} + +static Bit8u DRC_CALL_CONV dynrec_and_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=lf_var1b & lf_var2b; + lflags.type=t_ANDb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_and_byte_simple(Bit8u op1,Bit8u op2) { + return op1 & op2; +} + +static Bit8u DRC_CALL_CONV dynrec_or_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=lf_var1b | lf_var2b; + lflags.type=t_ORb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_or_byte_simple(Bit8u op1,Bit8u op2) { + return op1 | op2; +} + +static void DRC_CALL_CONV dynrec_test_byte(Bit8u op1,Bit8u op2) { + lf_var1b=op1; + lf_var2b=op2; + lf_resb=lf_var1b & lf_var2b; + lflags.type=t_TESTb; +} + +static void DRC_CALL_CONV dynrec_test_byte_simple(Bit8u op1,Bit8u op2) { +} + +static Bit16u DRC_CALL_CONV dynrec_add_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=(Bit16u)(lf_var1w+lf_var2w); + lflags.type=t_ADDw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_add_word_simple(Bit16u op1,Bit16u op2) { + return op1+op2; +} + +static Bit16u DRC_CALL_CONV dynrec_adc_word(Bit16u op1,Bit16u op2) { + lflags.oldcf=get_CF()!=0; + lf_var1w=op1; + lf_var2w=op2; + lf_resw=(Bit16u)(lf_var1w+lf_var2w+lflags.oldcf); + lflags.type=t_ADCw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_adc_word_simple(Bit16u op1,Bit16u op2) { + return (Bit16u)(op1+op2+(Bitu)(get_CF()!=0)); +} + +static Bit16u DRC_CALL_CONV dynrec_sub_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=(Bit16u)(lf_var1w-lf_var2w); + lflags.type=t_SUBw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_sub_word_simple(Bit16u op1,Bit16u op2) { + return op1-op2; +} + +static Bit16u DRC_CALL_CONV dynrec_sbb_word(Bit16u op1,Bit16u op2) { + lflags.oldcf=get_CF()!=0; + lf_var1w=op1; + lf_var2w=op2; + lf_resw=(Bit16u)(lf_var1w-(lf_var2w+lflags.oldcf)); + lflags.type=t_SBBw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_sbb_word_simple(Bit16u op1,Bit16u op2) { + return (Bit16u)(op1-(op2+(Bitu)(get_CF()!=0))); +} + +static void DRC_CALL_CONV dynrec_cmp_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=(Bit16u)(lf_var1w-lf_var2w); + lflags.type=t_CMPw; +} + +static void DRC_CALL_CONV dynrec_cmp_word_simple(Bit16u op1,Bit16u op2) { +} + +static Bit16u DRC_CALL_CONV dynrec_xor_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=lf_var1w ^ lf_var2w; + lflags.type=t_XORw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_xor_word_simple(Bit16u op1,Bit16u op2) { + return op1 ^ op2; +} + +static Bit16u DRC_CALL_CONV dynrec_and_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=lf_var1w & lf_var2w; + lflags.type=t_ANDw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_and_word_simple(Bit16u op1,Bit16u op2) { + return op1 & op2; +} + +static Bit16u DRC_CALL_CONV dynrec_or_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=lf_var1w | lf_var2w; + lflags.type=t_ORw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_or_word_simple(Bit16u op1,Bit16u op2) { + return op1 | op2; +} + +static void DRC_CALL_CONV dynrec_test_word(Bit16u op1,Bit16u op2) { + lf_var1w=op1; + lf_var2w=op2; + lf_resw=lf_var1w & lf_var2w; + lflags.type=t_TESTw; +} + +static void DRC_CALL_CONV dynrec_test_word_simple(Bit16u op1,Bit16u op2) { +} + +static Bit32u DRC_CALL_CONV dynrec_add_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d+lf_var2d; + lflags.type=t_ADDd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_add_dword_simple(Bit32u op1,Bit32u op2) { + return op1 + op2; +} + +static Bit32u DRC_CALL_CONV dynrec_adc_dword(Bit32u op1,Bit32u op2) { + lflags.oldcf=get_CF()!=0; + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d+lf_var2d+lflags.oldcf; + lflags.type=t_ADCd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_adc_dword_simple(Bit32u op1,Bit32u op2) { + return op1+op2+(Bitu)(get_CF()!=0); +} + +static Bit32u DRC_CALL_CONV dynrec_sub_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d-lf_var2d; + lflags.type=t_SUBd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_sub_dword_simple(Bit32u op1,Bit32u op2) { + return op1-op2; +} + +static Bit32u DRC_CALL_CONV dynrec_sbb_dword(Bit32u op1,Bit32u op2) { + lflags.oldcf=get_CF()!=0; + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d-(lf_var2d+lflags.oldcf); + lflags.type=t_SBBd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_sbb_dword_simple(Bit32u op1,Bit32u op2) { + return op1-(op2+(Bitu)(get_CF()!=0)); +} + +static void DRC_CALL_CONV dynrec_cmp_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d-lf_var2d; + lflags.type=t_CMPd; +} + +static void DRC_CALL_CONV dynrec_cmp_dword_simple(Bit32u op1,Bit32u op2) { +} + +static Bit32u DRC_CALL_CONV dynrec_xor_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d ^ lf_var2d; + lflags.type=t_XORd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_xor_dword_simple(Bit32u op1,Bit32u op2) { + return op1 ^ op2; +} + +static Bit32u DRC_CALL_CONV dynrec_and_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d & lf_var2d; + lflags.type=t_ANDd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_and_dword_simple(Bit32u op1,Bit32u op2) { + return op1 & op2; +} + +static Bit32u DRC_CALL_CONV dynrec_or_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d | lf_var2d; + lflags.type=t_ORd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_or_dword_simple(Bit32u op1,Bit32u op2) { + return op1 | op2; +} + +static void DRC_CALL_CONV dynrec_test_dword(Bit32u op1,Bit32u op2) { + lf_var1d=op1; + lf_var2d=op2; + lf_resd=lf_var1d & lf_var2d; + lflags.type=t_TESTd; +} + +static void DRC_CALL_CONV dynrec_test_dword_simple(Bit32u op1,Bit32u op2) { +} + + +static void dyn_dop_byte_gencall(DualOps op) { + switch (op) { + case DOP_ADD: + InvalidateFlags((void*)&dynrec_add_byte_simple); + gen_call_function_raw((void*)&dynrec_add_byte); + break; + case DOP_ADC: + AcquireFlags(FLAG_CF); + InvalidateFlagsPartially((void*)&dynrec_adc_byte_simple); + gen_call_function_raw((void*)&dynrec_adc_byte); + break; + case DOP_SUB: + InvalidateFlags((void*)&dynrec_sub_byte_simple); + gen_call_function_raw((void*)&dynrec_sub_byte); + break; + case DOP_SBB: + AcquireFlags(FLAG_CF); + InvalidateFlagsPartially((void*)&dynrec_sbb_byte_simple); + gen_call_function_raw((void*)&dynrec_sbb_byte); + break; + case DOP_CMP: + InvalidateFlags((void*)&dynrec_cmp_byte_simple); + gen_call_function_raw((void*)&dynrec_cmp_byte); + break; + case DOP_XOR: + InvalidateFlags((void*)&dynrec_xor_byte_simple); + gen_call_function_raw((void*)&dynrec_xor_byte); + break; + case DOP_AND: + InvalidateFlags((void*)&dynrec_and_byte_simple); + gen_call_function_raw((void*)&dynrec_and_byte); + break; + case DOP_OR: + InvalidateFlags((void*)&dynrec_or_byte_simple); + gen_call_function_raw((void*)&dynrec_or_byte); + break; + case DOP_TEST: + InvalidateFlags((void*)&dynrec_test_byte_simple); + gen_call_function_raw((void*)&dynrec_test_byte); + break; + default: IllegalOptionDynrec("dyn_dop_byte_gencall"); + } +} + +static void dyn_dop_word_gencall(DualOps op,bool dword) { + if (dword) { + switch (op) { + case DOP_ADD: + InvalidateFlags((void*)&dynrec_add_dword_simple); + gen_call_function_raw((void*)&dynrec_add_dword); + break; + case DOP_ADC: + AcquireFlags(FLAG_CF); + InvalidateFlagsPartially((void*)&dynrec_adc_dword_simple); + gen_call_function_raw((void*)&dynrec_adc_dword); + break; + case DOP_SUB: + InvalidateFlags((void*)&dynrec_sub_dword_simple); + gen_call_function_raw((void*)&dynrec_sub_dword); + break; + case DOP_SBB: + AcquireFlags(FLAG_CF); + InvalidateFlagsPartially((void*)&dynrec_sbb_dword_simple); + gen_call_function_raw((void*)&dynrec_sbb_dword); + break; + case DOP_CMP: + InvalidateFlags((void*)&dynrec_cmp_dword_simple); + gen_call_function_raw((void*)&dynrec_cmp_dword); + break; + case DOP_XOR: + InvalidateFlags((void*)&dynrec_xor_dword_simple); + gen_call_function_raw((void*)&dynrec_xor_dword); + break; + case DOP_AND: + InvalidateFlags((void*)&dynrec_and_dword_simple); + gen_call_function_raw((void*)&dynrec_and_dword); + break; + case DOP_OR: + InvalidateFlags((void*)&dynrec_or_dword_simple); + gen_call_function_raw((void*)&dynrec_or_dword); + break; + case DOP_TEST: + InvalidateFlags((void*)&dynrec_test_dword_simple); + gen_call_function_raw((void*)&dynrec_test_dword); + break; + default: IllegalOptionDynrec("dyn_dop_dword_gencall"); + } + } else { + switch (op) { + case DOP_ADD: + InvalidateFlags((void*)&dynrec_add_word_simple); + gen_call_function_raw((void*)&dynrec_add_word); + break; + case DOP_ADC: + AcquireFlags(FLAG_CF); + InvalidateFlagsPartially((void*)&dynrec_adc_word_simple); + gen_call_function_raw((void*)&dynrec_adc_word); + break; + case DOP_SUB: + InvalidateFlags((void*)&dynrec_sub_word_simple); + gen_call_function_raw((void*)&dynrec_sub_word); + break; + case DOP_SBB: + AcquireFlags(FLAG_CF); + InvalidateFlagsPartially((void*)&dynrec_sbb_word_simple); + gen_call_function_raw((void*)&dynrec_sbb_word); + break; + case DOP_CMP: + InvalidateFlags((void*)&dynrec_cmp_word_simple); + gen_call_function_raw((void*)&dynrec_cmp_word); + break; + case DOP_XOR: + InvalidateFlags((void*)&dynrec_xor_word_simple); + gen_call_function_raw((void*)&dynrec_xor_word); + break; + case DOP_AND: + InvalidateFlags((void*)&dynrec_and_word_simple); + gen_call_function_raw((void*)&dynrec_and_word); + break; + case DOP_OR: + InvalidateFlags((void*)&dynrec_or_word_simple); + gen_call_function_raw((void*)&dynrec_or_word); + break; + case DOP_TEST: + InvalidateFlags((void*)&dynrec_test_word_simple); + gen_call_function_raw((void*)&dynrec_test_word); + break; + default: IllegalOptionDynrec("dyn_dop_word_gencall"); + } + } +} + + +static Bit8u DRC_CALL_CONV dynrec_inc_byte(Bit8u op) { + LoadCF; + lf_var1b=op; + lf_resb=lf_var1b+1; + lflags.type=t_INCb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_inc_byte_simple(Bit8u op) { + return op+1; +} + +static Bit8u DRC_CALL_CONV dynrec_dec_byte(Bit8u op) { + LoadCF; + lf_var1b=op; + lf_resb=lf_var1b-1; + lflags.type=t_DECb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_dec_byte_simple(Bit8u op) { + return op-1; +} + +static Bit8u DRC_CALL_CONV dynrec_not_byte(Bit8u op) { + return ~op; +} + +static Bit8u DRC_CALL_CONV dynrec_neg_byte(Bit8u op) { + lf_var1b=op; + lf_resb=0-lf_var1b; + lflags.type=t_NEGb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_neg_byte_simple(Bit8u op) { + return 0-op; +} + +static Bit16u DRC_CALL_CONV dynrec_inc_word(Bit16u op) { + LoadCF; + lf_var1w=op; + lf_resw=lf_var1w+1; + lflags.type=t_INCw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_inc_word_simple(Bit16u op) { + return op+1; +} + +static Bit16u DRC_CALL_CONV dynrec_dec_word(Bit16u op) { + LoadCF; + lf_var1w=op; + lf_resw=lf_var1w-1; + lflags.type=t_DECw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_dec_word_simple(Bit16u op) { + return op-1; +} + +static Bit16u DRC_CALL_CONV dynrec_not_word(Bit16u op) { + return ~op; +} + +static Bit16u DRC_CALL_CONV dynrec_neg_word(Bit16u op) { + lf_var1w=op; + lf_resw=0-lf_var1w; + lflags.type=t_NEGw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_neg_word_simple(Bit16u op) { + return 0-op; +} + +static Bit32u DRC_CALL_CONV dynrec_inc_dword(Bit32u op) { + LoadCF; + lf_var1d=op; + lf_resd=lf_var1d+1; + lflags.type=t_INCd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_inc_dword_simple(Bit32u op) { + return op+1; +} + +static Bit32u DRC_CALL_CONV dynrec_dec_dword(Bit32u op) { + LoadCF; + lf_var1d=op; + lf_resd=lf_var1d-1; + lflags.type=t_DECd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_dec_dword_simple(Bit32u op) { + return op-1; +} + +static Bit32u DRC_CALL_CONV dynrec_not_dword(Bit32u op) { + return ~op; +} + +static Bit32u DRC_CALL_CONV dynrec_neg_dword(Bit32u op) { + lf_var1d=op; + lf_resd=0-lf_var1d; + lflags.type=t_NEGd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_neg_dword_simple(Bit32u op) { + return 0-op; +} + + +static void dyn_sop_byte_gencall(SingleOps op) { + switch (op) { + case SOP_INC: + InvalidateFlagsPartially((void*)&dynrec_inc_byte_simple); + gen_call_function_raw((void*)&dynrec_inc_byte); + break; + case SOP_DEC: + InvalidateFlagsPartially((void*)&dynrec_dec_byte_simple); + gen_call_function_raw((void*)&dynrec_dec_byte); + break; + case SOP_NOT: + gen_call_function_raw((void*)&dynrec_not_byte); + break; + case SOP_NEG: + InvalidateFlags((void*)&dynrec_neg_byte_simple); + gen_call_function_raw((void*)&dynrec_neg_byte); + break; + default: IllegalOptionDynrec("dyn_sop_byte_gencall"); + } +} + +static void dyn_sop_word_gencall(SingleOps op,bool dword) { + if (dword) { + switch (op) { + case SOP_INC: + InvalidateFlagsPartially((void*)&dynrec_inc_dword_simple); + gen_call_function_raw((void*)&dynrec_inc_dword); + break; + case SOP_DEC: + InvalidateFlagsPartially((void*)&dynrec_dec_dword_simple); + gen_call_function_raw((void*)&dynrec_dec_dword); + break; + case SOP_NOT: + gen_call_function_raw((void*)&dynrec_not_dword); + break; + case SOP_NEG: + InvalidateFlags((void*)&dynrec_neg_dword_simple); + gen_call_function_raw((void*)&dynrec_neg_dword); + break; + default: IllegalOptionDynrec("dyn_sop_dword_gencall"); + } + } else { + switch (op) { + case SOP_INC: + InvalidateFlagsPartially((void*)&dynrec_inc_word_simple); + gen_call_function_raw((void*)&dynrec_inc_word); + break; + case SOP_DEC: + InvalidateFlagsPartially((void*)&dynrec_dec_word_simple); + gen_call_function_raw((void*)&dynrec_dec_word); + break; + case SOP_NOT: + gen_call_function_raw((void*)&dynrec_not_word); + break; + case SOP_NEG: + InvalidateFlags((void*)&dynrec_neg_word_simple); + gen_call_function_raw((void*)&dynrec_neg_word); + break; + default: IllegalOptionDynrec("dyn_sop_word_gencall"); + } + } +} + + +static Bit8u DRC_CALL_CONV dynrec_rol_byte(Bit8u op1,Bit8u op2) { + if (!(op2&0x7)) { + if (op2&0x18) { + FillFlags(); + SETFLAGBIT(CF,op1 & 1); + SETFLAGBIT(OF,(op1 & 1) ^ (op1 >> 7)); + } + return op1; + } + FillFlags(); + lf_var1b=op1; + lf_var2b=op2&0x07; + lf_resb=(lf_var1b << lf_var2b) | (lf_var1b >> (8-lf_var2b)); + SETFLAGBIT(CF,lf_resb & 1); + SETFLAGBIT(OF,(lf_resb & 1) ^ (lf_resb >> 7)); + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_rol_byte_simple(Bit8u op1,Bit8u op2) { + if (!(op2&0x7)) return op1; + return (op1 << (op2&0x07)) | (op1 >> (8-(op2&0x07))); +} + +static Bit8u DRC_CALL_CONV dynrec_ror_byte(Bit8u op1,Bit8u op2) { + if (!(op2&0x7)) { + if (op2&0x10) { + FillFlags(); + SETFLAGBIT(CF,op1>>7); + SETFLAGBIT(OF,(op1>>7) ^ ((op1>>6) & 1)); + } + return op1; + } + FillFlags(); + lf_var1b=op1; + lf_var2b=op2&0x07; + lf_resb=(lf_var1b >> lf_var2b) | (lf_var1b << (8-lf_var2b)); + SETFLAGBIT(CF,lf_resb & 0x80); + if (lf_var2b == 1) SETFLAGBIT(OF,(lf_resb ^ lf_var1b) & 0x80); + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_ror_byte_simple(Bit8u op1,Bit8u op2) { + if (!(op2&0x7)) return op1; + return (op1 >> (op2&0x07)) | (op1 << (8-(op2&0x07))); +} + +static Bit8u DRC_CALL_CONV dynrec_rcl_byte(Bit8u op1,Bit8u op2) { + if (op2%9) { + Bit8u cf=(Bit8u)FillFlags()&0x1; + lf_var1b=op1; + lf_var2b=op2%9; + lf_resb=(lf_var1b << lf_var2b) | (cf << (lf_var2b-1)) | (lf_var1b >> (9-lf_var2b)); + SETFLAGBIT(CF,((lf_var1b >> (8-lf_var2b)) & 1)); + SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resb >> 7)); + return lf_resb; + } else return op1; +} + +static Bit8u DRC_CALL_CONV dynrec_rcr_byte(Bit8u op1,Bit8u op2) { + if (op2%9) { + Bit8u cf=(Bit8u)FillFlags()&0x1; + lf_var1b=op1; + lf_var2b=op2%9; + lf_resb=(lf_var1b >> lf_var2b) | (cf << (8-lf_var2b)) | (lf_var1b << (9-lf_var2b)); \ + SETFLAGBIT(CF,(lf_var1b >> (lf_var2b - 1)) & 1); + SETFLAGBIT(OF,(lf_resb ^ lf_var1b) & 0x80); + return lf_resb; + } else return op1; +} + +static Bit8u DRC_CALL_CONV dynrec_shl_byte(Bit8u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1b=op1; + lf_var2b=op2; + lf_resb=lf_var1b << lf_var2b; + lflags.type=t_SHLb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_shl_byte_simple(Bit8u op1,Bit8u op2) { + if (!op2) return op1; + return op1 << op2; +} + +static Bit8u DRC_CALL_CONV dynrec_shr_byte(Bit8u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1b=op1; + lf_var2b=op2; + lf_resb=lf_var1b >> lf_var2b; + lflags.type=t_SHRb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_shr_byte_simple(Bit8u op1,Bit8u op2) { + if (!op2) return op1; + return op1 >> op2; +} + +static Bit8u DRC_CALL_CONV dynrec_sar_byte(Bit8u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1b=op1; + lf_var2b=op2; + if (lf_var2b>8) lf_var2b=8; + if (lf_var1b & 0x80) { + lf_resb=(lf_var1b >> lf_var2b)| (0xff << (8 - lf_var2b)); + } else { + lf_resb=lf_var1b >> lf_var2b; + } + lflags.type=t_SARb; + return lf_resb; +} + +static Bit8u DRC_CALL_CONV dynrec_sar_byte_simple(Bit8u op1,Bit8u op2) { + if (!op2) return op1; + if (op2>8) op2=8; + if (op1 & 0x80) return (op1 >> op2) | (0xff << (8 - op2)); + else return op1 >> op2; +} + +static Bit16u DRC_CALL_CONV dynrec_rol_word(Bit16u op1,Bit8u op2) { + if (!(op2&0xf)) { + if (op2&0x10) { + FillFlags(); + SETFLAGBIT(CF,op1 & 1); + } + return op1; + } + FillFlags(); + lf_var1w=op1; + lf_var2b=op2&0xf; + lf_resw=(lf_var1w << lf_var2b) | (lf_var1w >> (16-lf_var2b)); + SETFLAGBIT(CF,lf_resw & 1); + SETFLAGBIT(OF,(lf_resw & 1) ^ (lf_resw >> 15)); + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_rol_word_simple(Bit16u op1,Bit8u op2) { + if (!(op2&0xf)) return op1; + return (op1 << (op2&0xf)) | (op1 >> (16-(op2&0xf))); +} + +static Bit16u DRC_CALL_CONV dynrec_ror_word(Bit16u op1,Bit8u op2) { + if (!(op2&0xf)) { + if (op2&0x10) { + FillFlags(); + SETFLAGBIT(CF,op1>>15); + } + return op1; + } + FillFlags(); + lf_var1w=op1; + lf_var2b=op2&0xf; + lf_resw=(lf_var1w >> lf_var2b) | (lf_var1w << (16-lf_var2b)); + SETFLAGBIT(CF,lf_resw & 0x8000); + if (lf_var2b == 1) SETFLAGBIT(OF,(lf_resw ^ lf_var1w) & 0x8000); + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_ror_word_simple(Bit16u op1,Bit8u op2) { + if (!(op2&0xf)) return op1; + return (op1 >> (op2&0xf)) | (op1 << (16-(op2&0xf))); +} + +static Bit16u DRC_CALL_CONV dynrec_rcl_word(Bit16u op1,Bit8u op2) { + if (op2%17) { + Bit16u cf=(Bit16u)FillFlags()&0x1; + lf_var1w=op1; + lf_var2b=op2%17; + lf_resw=(lf_var1w << lf_var2b) | (cf << (lf_var2b-1)) | (lf_var1w >> (17-lf_var2b)); + SETFLAGBIT(CF,((lf_var1w >> (16-lf_var2b)) & 1)); + SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resw >> 15)); + return lf_resw; + } else return op1; +} + +static Bit16u DRC_CALL_CONV dynrec_rcr_word(Bit16u op1,Bit8u op2) { + if (op2%17) { + Bit16u cf=(Bit16u)FillFlags()&0x1; + lf_var1w=op1; + lf_var2b=op2%17; + lf_resw=(lf_var1w >> lf_var2b) | (cf << (16-lf_var2b)) | (lf_var1w << (17-lf_var2b)); + SETFLAGBIT(CF,(lf_var1w >> (lf_var2b - 1)) & 1); + SETFLAGBIT(OF,(lf_resw ^ lf_var1w) & 0x8000); + return lf_resw; + } else return op1; +} + +static Bit16u DRC_CALL_CONV dynrec_shl_word(Bit16u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1w=op1; + lf_var2b=op2; + lf_resw=lf_var1w << lf_var2b; + lflags.type=t_SHLw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_shl_word_simple(Bit16u op1,Bit8u op2) { + if (!op2) return op1; + return op1 << op2; +} + +static Bit16u DRC_CALL_CONV dynrec_shr_word(Bit16u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1w=op1; + lf_var2b=op2; + lf_resw=lf_var1w >> lf_var2b; + lflags.type=t_SHRw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_shr_word_simple(Bit16u op1,Bit8u op2) { + if (!op2) return op1; + return op1 >> op2; +} + +static Bit16u DRC_CALL_CONV dynrec_sar_word(Bit16u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1w=op1; + lf_var2b=op2; + if (lf_var2b>16) lf_var2b=16; + if (lf_var1w & 0x8000) { + lf_resw=(lf_var1w >> lf_var2b) | (0xffff << (16 - lf_var2b)); + } else { + lf_resw=lf_var1w >> lf_var2b; + } + lflags.type=t_SARw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_sar_word_simple(Bit16u op1,Bit8u op2) { + if (!op2) return op1; + if (op2>16) op2=16; + if (op1 & 0x8000) return (op1 >> op2) | (0xffff << (16 - op2)); + else return op1 >> op2; +} + +static Bit32u DRC_CALL_CONV dynrec_rol_dword(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + FillFlags(); + lf_var1d=op1; + lf_var2b=op2; + lf_resd=(lf_var1d << lf_var2b) | (lf_var1d >> (32-lf_var2b)); + SETFLAGBIT(CF,lf_resd & 1); + SETFLAGBIT(OF,(lf_resd & 1) ^ (lf_resd >> 31)); + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_rol_dword_simple(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + return (op1 << op2) | (op1 >> (32-op2)); +} + +static Bit32u DRC_CALL_CONV dynrec_ror_dword(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + FillFlags(); + lf_var1d=op1; + lf_var2b=op2; + lf_resd=(lf_var1d >> lf_var2b) | (lf_var1d << (32-lf_var2b)); + SETFLAGBIT(CF,lf_resd & 0x80000000); + if (lf_var2b == 1) SETFLAGBIT(OF,(lf_resd ^ lf_var1d) & 0x80000000); + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_ror_dword_simple(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + return (op1 >> op2) | (op1 << (32-op2)); +} + +static Bit32u DRC_CALL_CONV dynrec_rcl_dword(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + Bit32u cf=(Bit32u)FillFlags()&0x1; + lf_var1d=op1; + lf_var2b=op2; + if (lf_var2b==1) { + lf_resd=(lf_var1d << 1) | cf; + } else { + lf_resd=(lf_var1d << lf_var2b) | (cf << (lf_var2b-1)) | (lf_var1d >> (33-lf_var2b)); + } + SETFLAGBIT(CF,((lf_var1d >> (32-lf_var2b)) & 1)); + SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resd >> 31)); + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_rcr_dword(Bit32u op1,Bit8u op2) { + if (op2) { + Bit32u cf=(Bit32u)FillFlags()&0x1; + lf_var1d=op1; + lf_var2b=op2; + if (lf_var2b==1) { + lf_resd=lf_var1d >> 1 | cf << 31; + } else { + lf_resd=(lf_var1d >> lf_var2b) | (cf << (32-lf_var2b)) | (lf_var1d << (33-lf_var2b)); + } + SETFLAGBIT(CF,(lf_var1d >> (lf_var2b - 1)) & 1); + SETFLAGBIT(OF,(lf_resd ^ lf_var1d) & 0x80000000); + return lf_resd; + } else return op1; +} + +static Bit32u DRC_CALL_CONV dynrec_shl_dword(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1d=op1; + lf_var2b=op2; + lf_resd=lf_var1d << lf_var2b; + lflags.type=t_SHLd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_shl_dword_simple(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + return op1 << op2; +} + +static Bit32u DRC_CALL_CONV dynrec_shr_dword(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + lf_var1d=op1; + lf_var2b=op2; + lf_resd=lf_var1d >> lf_var2b; + lflags.type=t_SHRd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_shr_dword_simple(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + return op1 >> op2; +} + +static Bit32u DRC_CALL_CONV dynrec_sar_dword(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + lf_var2b=op2; + lf_var1d=op1; + if (lf_var1d & 0x80000000) { + lf_resd=(lf_var1d >> lf_var2b) | (0xffffffff << (32 - lf_var2b)); + } else { + lf_resd=lf_var1d >> lf_var2b; + } + lflags.type=t_SARd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_sar_dword_simple(Bit32u op1,Bit8u op2) { + if (!op2) return op1; + if (op1 & 0x80000000) return (op1 >> op2) | (0xffffffff << (32 - op2)); + else return op1 >> op2; +} + +static void dyn_shift_byte_gencall(ShiftOps op) { + switch (op) { + case SHIFT_ROL: + InvalidateFlagsPartially((void*)&dynrec_rol_byte_simple); + gen_call_function_raw((void*)&dynrec_rol_byte); + break; + case SHIFT_ROR: + InvalidateFlagsPartially((void*)&dynrec_ror_byte_simple); + gen_call_function_raw((void*)&dynrec_ror_byte); + break; + case SHIFT_RCL: + AcquireFlags(FLAG_CF); + gen_call_function_raw((void*)&dynrec_rcl_byte); + break; + case SHIFT_RCR: + AcquireFlags(FLAG_CF); + gen_call_function_raw((void*)&dynrec_rcr_byte); + break; + case SHIFT_SHL: + case SHIFT_SAL: + InvalidateFlagsPartially((void*)&dynrec_shl_byte_simple); + gen_call_function_raw((void*)&dynrec_shl_byte); + break; + case SHIFT_SHR: + InvalidateFlagsPartially((void*)&dynrec_shr_byte_simple); + gen_call_function_raw((void*)&dynrec_shr_byte); + break; + case SHIFT_SAR: + InvalidateFlagsPartially((void*)&dynrec_sar_byte_simple); + gen_call_function_raw((void*)&dynrec_sar_byte); + break; + default: IllegalOptionDynrec("dyn_shift_byte_gencall"); + } +} + +static void dyn_shift_word_gencall(ShiftOps op,bool dword) { + if (dword) { + switch (op) { + case SHIFT_ROL: + InvalidateFlagsPartially((void*)&dynrec_rol_dword_simple); + gen_call_function_raw((void*)&dynrec_rol_dword); + break; + case SHIFT_ROR: + InvalidateFlagsPartially((void*)&dynrec_ror_dword_simple); + gen_call_function_raw((void*)&dynrec_ror_dword); + break; + case SHIFT_RCL: + AcquireFlags(FLAG_CF); + gen_call_function_raw((void*)&dynrec_rcl_dword); + break; + case SHIFT_RCR: + AcquireFlags(FLAG_CF); + gen_call_function_raw((void*)&dynrec_rcr_dword); + break; + case SHIFT_SHL: + case SHIFT_SAL: + InvalidateFlagsPartially((void*)&dynrec_shl_dword_simple); + gen_call_function_raw((void*)&dynrec_shl_dword); + break; + case SHIFT_SHR: + InvalidateFlagsPartially((void*)&dynrec_shr_dword_simple); + gen_call_function_raw((void*)&dynrec_shr_dword); + break; + case SHIFT_SAR: + InvalidateFlagsPartially((void*)&dynrec_sar_dword_simple); + gen_call_function_raw((void*)&dynrec_sar_dword); + break; + default: IllegalOptionDynrec("dyn_shift_dword_gencall"); + } + } else { + switch (op) { + case SHIFT_ROL: + InvalidateFlagsPartially((void*)&dynrec_rol_word_simple); + gen_call_function_raw((void*)&dynrec_rol_word); + break; + case SHIFT_ROR: + InvalidateFlagsPartially((void*)&dynrec_ror_word_simple); + gen_call_function_raw((void*)&dynrec_ror_word); + break; + case SHIFT_RCL: + AcquireFlags(FLAG_CF); + gen_call_function_raw((void*)&dynrec_rcl_word); + break; + case SHIFT_RCR: + AcquireFlags(FLAG_CF); + gen_call_function_raw((void*)&dynrec_rcr_word); + break; + case SHIFT_SHL: + case SHIFT_SAL: + InvalidateFlagsPartially((void*)&dynrec_shl_word_simple); + gen_call_function_raw((void*)&dynrec_shl_word); + break; + case SHIFT_SHR: + InvalidateFlagsPartially((void*)&dynrec_shr_word_simple); + gen_call_function_raw((void*)&dynrec_shr_word); + break; + case SHIFT_SAR: + InvalidateFlagsPartially((void*)&dynrec_sar_word_simple); + gen_call_function_raw((void*)&dynrec_sar_word); + break; + default: IllegalOptionDynrec("dyn_shift_word_gencall"); + } + } +} + +static Bit16u DRC_CALL_CONV dynrec_dshl_word(Bit16u op1,Bit16u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + lf_var2b=val; + lf_var1d=(op1<<16)|op2; + Bit32u tempd=lf_var1d << lf_var2b; + if (lf_var2b>16) tempd |= (op2 << (lf_var2b - 16)); + lf_resw=(Bit16u)(tempd >> 16); + lflags.type=t_DSHLw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_dshl_word_simple(Bit16u op1,Bit16u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + Bit32u tempd=(Bit32u)((((Bit32u)op1)<<16)|op2) << val; + if (val>16) tempd |= (op2 << (val - 16)); + return (Bit16u)(tempd >> 16); +} + +static Bit32u DRC_CALL_CONV dynrec_dshl_dword(Bit32u op1,Bit32u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + lf_var2b=val; + lf_var1d=op1; + lf_resd=(lf_var1d << lf_var2b) | (op2 >> (32-lf_var2b)); + lflags.type=t_DSHLd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_dshl_dword_simple(Bit32u op1,Bit32u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + return (op1 << val) | (op2 >> (32-val)); +} + +static Bit16u DRC_CALL_CONV dynrec_dshr_word(Bit16u op1,Bit16u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + lf_var2b=val; + lf_var1d=(op2<<16)|op1; + Bit32u tempd=lf_var1d >> lf_var2b; + if (lf_var2b>16) tempd |= (op2 << (32-lf_var2b )); + lf_resw=(Bit16u)(tempd); + lflags.type=t_DSHRw; + return lf_resw; +} + +static Bit16u DRC_CALL_CONV dynrec_dshr_word_simple(Bit16u op1,Bit16u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + Bit32u tempd=(Bit32u)((((Bit32u)op2)<<16)|op1) >> val; + if (val>16) tempd |= (op2 << (32-val)); + return (Bit16u)(tempd); +} + +static Bit32u DRC_CALL_CONV dynrec_dshr_dword(Bit32u op1,Bit32u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + lf_var2b=val; + lf_var1d=op1; + lf_resd=(lf_var1d >> lf_var2b) | (op2 << (32-lf_var2b)); + lflags.type=t_DSHRd; + return lf_resd; +} + +static Bit32u DRC_CALL_CONV dynrec_dshr_dword_simple(Bit32u op1,Bit32u op2,Bit8u op3) { + Bit8u val=op3 & 0x1f; + if (!val) return op1; + return (op1 >> val) | (op2 << (32-val)); +} + +static void dyn_dpshift_word_gencall(bool left) { + if (left) { + DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshl_word,FC_RETOP); + InvalidateFlagsPartially((void*)&dynrec_dshl_word_simple,proc_addr); + } else { + DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshr_word,FC_RETOP); + InvalidateFlagsPartially((void*)&dynrec_dshr_word_simple,proc_addr); + } +} + +static void dyn_dpshift_dword_gencall(bool left) { + if (left) { + DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshl_dword,FC_RETOP); + InvalidateFlagsPartially((void*)&dynrec_dshl_dword_simple,proc_addr); + } else { + DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshr_dword,FC_RETOP); + InvalidateFlagsPartially((void*)&dynrec_dshr_dword_simple,proc_addr); + } +} + + + +static Bit32u DRC_CALL_CONV dynrec_get_of(void) { return TFLG_O; } +static Bit32u DRC_CALL_CONV dynrec_get_nof(void) { return TFLG_NO; } +static Bit32u DRC_CALL_CONV dynrec_get_cf(void) { return TFLG_B; } +static Bit32u DRC_CALL_CONV dynrec_get_ncf(void) { return TFLG_NB; } +static Bit32u DRC_CALL_CONV dynrec_get_zf(void) { return TFLG_Z; } +static Bit32u DRC_CALL_CONV dynrec_get_nzf(void) { return TFLG_NZ; } +static Bit32u DRC_CALL_CONV dynrec_get_sf(void) { return TFLG_S; } +static Bit32u DRC_CALL_CONV dynrec_get_nsf(void) { return TFLG_NS; } +static Bit32u DRC_CALL_CONV dynrec_get_pf(void) { return TFLG_P; } +static Bit32u DRC_CALL_CONV dynrec_get_npf(void) { return TFLG_NP; } + +static Bit32u DRC_CALL_CONV dynrec_get_cf_or_zf(void) { return TFLG_BE; } +static Bit32u DRC_CALL_CONV dynrec_get_ncf_and_nzf(void) { return TFLG_NBE; } +static Bit32u DRC_CALL_CONV dynrec_get_sf_neq_of(void) { return TFLG_L; } +static Bit32u DRC_CALL_CONV dynrec_get_sf_eq_of(void) { return TFLG_NL; } +static Bit32u DRC_CALL_CONV dynrec_get_zf_or_sf_neq_of(void) { return TFLG_LE; } +static Bit32u DRC_CALL_CONV dynrec_get_nzf_and_sf_eq_of(void) { return TFLG_NLE; } + + +static void dyn_branchflag_to_reg(BranchTypes btype) { + switch (btype) { + case BR_O:gen_call_function_raw((void*)&dynrec_get_of);break; + case BR_NO:gen_call_function_raw((void*)&dynrec_get_nof);break; + case BR_B:gen_call_function_raw((void*)&dynrec_get_cf);break; + case BR_NB:gen_call_function_raw((void*)&dynrec_get_ncf);break; + case BR_Z:gen_call_function_raw((void*)&dynrec_get_zf);break; + case BR_NZ:gen_call_function_raw((void*)&dynrec_get_nzf);break; + case BR_BE:gen_call_function_raw((void*)&dynrec_get_cf_or_zf);break; + case BR_NBE:gen_call_function_raw((void*)&dynrec_get_ncf_and_nzf);break; + + case BR_S:gen_call_function_raw((void*)&dynrec_get_sf);break; + case BR_NS:gen_call_function_raw((void*)&dynrec_get_nsf);break; + case BR_P:gen_call_function_raw((void*)&dynrec_get_pf);break; + case BR_NP:gen_call_function_raw((void*)&dynrec_get_npf);break; + case BR_L:gen_call_function_raw((void*)&dynrec_get_sf_neq_of);break; + case BR_NL:gen_call_function_raw((void*)&dynrec_get_sf_eq_of);break; + case BR_LE:gen_call_function_raw((void*)&dynrec_get_zf_or_sf_neq_of);break; + case BR_NLE:gen_call_function_raw((void*)&dynrec_get_nzf_and_sf_eq_of);break; + } +} + + +static void DRC_CALL_CONV dynrec_mul_byte(Bit8u op) { + FillFlags(); + reg_ax=reg_al*op; + SETFLAGBIT(ZF,reg_al == 0); + if (reg_ax & 0xff00) { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } else { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } +} + +static void DRC_CALL_CONV dynrec_imul_byte(Bit8u op) { + FillFlags(); + reg_ax=((Bit8s)reg_al) * ((Bit8s)op); + if ((reg_ax & 0xff80)==0xff80 || (reg_ax & 0xff80)==0x0000) { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } else { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } +} + +static void DRC_CALL_CONV dynrec_mul_word(Bit16u op) { + FillFlags(); + Bitu tempu=(Bitu)reg_ax*(Bitu)op; + reg_ax=(Bit16u)(tempu); + reg_dx=(Bit16u)(tempu >> 16); + SETFLAGBIT(ZF,reg_ax == 0); + if (reg_dx) { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } else { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } +} + +static void DRC_CALL_CONV dynrec_imul_word(Bit16u op) { + FillFlags(); + Bits temps=((Bit16s)reg_ax)*((Bit16s)op); + reg_ax=(Bit16s)(temps); + reg_dx=(Bit16s)(temps >> 16); + if (((temps & 0xffff8000)==0xffff8000 || (temps & 0xffff8000)==0x0000)) { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } else { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } +} + +static void DRC_CALL_CONV dynrec_mul_dword(Bit32u op) { + FillFlags(); + Bit64u tempu=(Bit64u)reg_eax*(Bit64u)op; + reg_eax=(Bit32u)(tempu); + reg_edx=(Bit32u)(tempu >> 32); + SETFLAGBIT(ZF,reg_eax == 0); + if (reg_edx) { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } else { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } +} + +static void DRC_CALL_CONV dynrec_imul_dword(Bit32u op) { + FillFlags(); + Bit64s temps=((Bit64s)((Bit32s)reg_eax))*((Bit64s)((Bit32s)op)); + reg_eax=(Bit32u)(temps); + reg_edx=(Bit32u)(temps >> 32); + if ((reg_edx==0xffffffff) && (reg_eax & 0x80000000) ) { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } else if ( (reg_edx==0x00000000) && (reg_eax< 0x80000000) ) { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } else { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } +} + + +static bool DRC_CALL_CONV dynrec_div_byte(Bit8u op) { + Bitu val=op; + if (val==0) return CPU_PrepareException(0,0); + Bitu quo=reg_ax / val; + Bit8u rem=(Bit8u)(reg_ax % val); + Bit8u quo8=(Bit8u)(quo&0xff); + if (quo>0xff) return CPU_PrepareException(0,0); + reg_ah=rem; + reg_al=quo8; + return false; +} + +static bool DRC_CALL_CONV dynrec_idiv_byte(Bit8u op) { + Bits val=(Bit8s)op; + if (val==0) return CPU_PrepareException(0,0); + Bits quo=((Bit16s)reg_ax) / val; + Bit8s rem=(Bit8s)((Bit16s)reg_ax % val); + Bit8s quo8s=(Bit8s)(quo&0xff); + if (quo!=(Bit16s)quo8s) return CPU_PrepareException(0,0); + reg_ah=rem; + reg_al=quo8s; + return false; +} + +static bool DRC_CALL_CONV dynrec_div_word(Bit16u op) { + Bitu val=op; + if (val==0) return CPU_PrepareException(0,0); + Bitu num=((Bit32u)reg_dx<<16)|reg_ax; + Bitu quo=num/val; + Bit16u rem=(Bit16u)(num % val); + Bit16u quo16=(Bit16u)(quo&0xffff); + if (quo!=(Bit32u)quo16) return CPU_PrepareException(0,0); + reg_dx=rem; + reg_ax=quo16; + return false; +} + +static bool DRC_CALL_CONV dynrec_idiv_word(Bit16u op) { + Bits val=(Bit16s)op; + if (val==0) return CPU_PrepareException(0,0); + Bits num=(Bit32s)((reg_dx<<16)|reg_ax); + Bits quo=num/val; + Bit16s rem=(Bit16s)(num % val); + Bit16s quo16s=(Bit16s)quo; + if (quo!=(Bit32s)quo16s) return CPU_PrepareException(0,0); + reg_dx=rem; + reg_ax=quo16s; + return false; +} + +static bool DRC_CALL_CONV dynrec_div_dword(Bit32u op) { + Bitu val=op; + if (val==0) return CPU_PrepareException(0,0); + Bit64u num=(((Bit64u)reg_edx)<<32)|reg_eax; + Bit64u quo=num/val; + Bit32u rem=(Bit32u)(num % val); + Bit32u quo32=(Bit32u)(quo&0xffffffff); + if (quo!=(Bit64u)quo32) return CPU_PrepareException(0,0); + reg_edx=rem; + reg_eax=quo32; + return false; +} + +static bool DRC_CALL_CONV dynrec_idiv_dword(Bit32u op) { + Bits val=(Bit32s)op; + if (val==0) return CPU_PrepareException(0,0); + Bit64s num=(((Bit64u)reg_edx)<<32)|reg_eax; + Bit64s quo=num/val; + Bit32s rem=(Bit32s)(num % val); + Bit32s quo32s=(Bit32s)(quo&0xffffffff); + if (quo!=(Bit64s)quo32s) return CPU_PrepareException(0,0); + reg_edx=rem; + reg_eax=quo32s; + return false; +} + + +static Bit16u DRC_CALL_CONV dynrec_dimul_word(Bit16u op1,Bit16u op2) { + FillFlags(); + Bits res=((Bit16s)op1) * ((Bit16s)op2); + if ((res>-32768) && (res<32767)) { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } else { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } + return (Bit16u)(res & 0xffff); +} + +static Bit32u DRC_CALL_CONV dynrec_dimul_dword(Bit32u op1,Bit32u op2) { + FillFlags(); + Bit64s res=((Bit64s)((Bit32s)op1))*((Bit64s)((Bit32s)op2)); + if ((res>-((Bit64s)(2147483647)+1)) && (res<(Bit64s)2147483647)) { + SETFLAGBIT(CF,false); + SETFLAGBIT(OF,false); + } else { + SETFLAGBIT(CF,true); + SETFLAGBIT(OF,true); + } + return (Bit32s)res; +} + + + +static Bit16u DRC_CALL_CONV dynrec_cbw(Bit8u op) { + return (Bit8s)op; +} + +static Bit32u DRC_CALL_CONV dynrec_cwde(Bit16u op) { + return (Bit16s)op; +} + +static Bit16u DRC_CALL_CONV dynrec_cwd(Bit16u op) { + if (op & 0x8000) return 0xffff; + else return 0; +} + +static Bit32u DRC_CALL_CONV dynrec_cdq(Bit32u op) { + if (op & 0x80000000) return 0xffffffff; + else return 0; +} + + +static void DRC_CALL_CONV dynrec_sahf(Bit16u op) { + SETFLAGBIT(OF,get_OF()); + lflags.type=t_UNKNOWN; + CPU_SetFlags(op>>8,FMASK_NORMAL & 0xff); +} + + +static void DRC_CALL_CONV dynrec_cmc(void) { + FillFlags(); + SETFLAGBIT(CF,!(reg_flags & FLAG_CF)); +} +static void DRC_CALL_CONV dynrec_clc(void) { + FillFlags(); + SETFLAGBIT(CF,false); +} +static void DRC_CALL_CONV dynrec_stc(void) { + FillFlags(); + SETFLAGBIT(CF,true); +} +static void DRC_CALL_CONV dynrec_cld(void) { + SETFLAGBIT(DF,false); + cpu.direction=1; +} +static void DRC_CALL_CONV dynrec_std(void) { + SETFLAGBIT(DF,true); + cpu.direction=-1; +} + + +static Bit16u DRC_CALL_CONV dynrec_movsb_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + for (;count>0;count--) { + mem_writeb(di_base+reg_di,mem_readb(si_base+reg_si)); + reg_si+=add_index; + reg_di+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_movsb_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + for (;count>0;count--) { + mem_writeb(di_base+reg_edi,mem_readb(si_base+reg_esi)); + reg_esi+=add_index; + reg_edi+=add_index; + } + return count_left; +} + +static Bit16u DRC_CALL_CONV dynrec_movsw_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=1; + for (;count>0;count--) { + mem_writew(di_base+reg_di,mem_readw(si_base+reg_si)); + reg_si+=add_index; + reg_di+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_movsw_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=1; + for (;count>0;count--) { + mem_writew(di_base+reg_edi,mem_readw(si_base+reg_esi)); + reg_esi+=add_index; + reg_edi+=add_index; + } + return count_left; +} + +static Bit16u DRC_CALL_CONV dynrec_movsd_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=2; + for (;count>0;count--) { + mem_writed(di_base+reg_di,mem_readd(si_base+reg_si)); + reg_si+=add_index; + reg_di+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_movsd_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=2; + for (;count>0;count--) { + mem_writed(di_base+reg_edi,mem_readd(si_base+reg_esi)); + reg_esi+=add_index; + reg_edi+=add_index; + } + return count_left; +} + + +static Bit16u DRC_CALL_CONV dynrec_lodsb_word(Bit16u count,Bit16s add_index,PhysPt si_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + for (;count>0;count--) { + reg_al=mem_readb(si_base+reg_si); + reg_si+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_lodsb_dword(Bit32u count,Bit32s add_index,PhysPt si_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + for (;count>0;count--) { + reg_al=mem_readb(si_base+reg_esi); + reg_esi+=add_index; + } + return count_left; +} + +static Bit16u DRC_CALL_CONV dynrec_lodsw_word(Bit16u count,Bit16s add_index,PhysPt si_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=1; + for (;count>0;count--) { + reg_ax=mem_readw(si_base+reg_si); + reg_si+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_lodsw_dword(Bit32u count,Bit32s add_index,PhysPt si_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=1; + for (;count>0;count--) { + reg_ax=mem_readw(si_base+reg_esi); + reg_esi+=add_index; + } + return count_left; +} + +static Bit16u DRC_CALL_CONV dynrec_lodsd_word(Bit16u count,Bit16s add_index,PhysPt si_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=2; + for (;count>0;count--) { + reg_eax=mem_readd(si_base+reg_si); + reg_si+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_lodsd_dword(Bit32u count,Bit32s add_index,PhysPt si_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=2; + for (;count>0;count--) { + reg_eax=mem_readd(si_base+reg_esi); + reg_esi+=add_index; + } + return count_left; +} + + +static Bit16u DRC_CALL_CONV dynrec_stosb_word(Bit16u count,Bit16s add_index,PhysPt di_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + for (;count>0;count--) { + mem_writeb(di_base+reg_di,reg_al); + reg_di+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_stosb_dword(Bit32u count,Bit32s add_index,PhysPt di_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + for (;count>0;count--) { + mem_writeb(di_base+reg_edi,reg_al); + reg_edi+=add_index; + } + return count_left; +} + +static Bit16u DRC_CALL_CONV dynrec_stosw_word(Bit16u count,Bit16s add_index,PhysPt di_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=1; + for (;count>0;count--) { + mem_writew(di_base+reg_di,reg_ax); + reg_di+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_stosw_dword(Bit32u count,Bit32s add_index,PhysPt di_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=1; + for (;count>0;count--) { + mem_writew(di_base+reg_edi,reg_ax); + reg_edi+=add_index; + } + return count_left; +} + +static Bit16u DRC_CALL_CONV dynrec_stosd_word(Bit16u count,Bit16s add_index,PhysPt di_base) { + Bit16u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=(Bit16u)(count-CPU_Cycles); + count=(Bit16u)CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=2; + for (;count>0;count--) { + mem_writed(di_base+reg_di,reg_eax); + reg_di+=add_index; + } + return count_left; +} + +static Bit32u DRC_CALL_CONV dynrec_stosd_dword(Bit32u count,Bit32s add_index,PhysPt di_base) { + Bit32u count_left; + if (count<(Bitu)CPU_Cycles) { + count_left=0; + } else { + count_left=count-CPU_Cycles; + count=CPU_Cycles; + CPU_Cycles=0; + } + add_index<<=2; + for (;count>0;count--) { + mem_writed(di_base+reg_edi,reg_eax); + reg_edi+=add_index; + } + return count_left; +} + + +static void DRC_CALL_CONV dynrec_push_word(Bit16u value) { + Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-2)&cpu.stack.mask); + mem_writew(SegPhys(ss) + (new_esp & cpu.stack.mask),value); + reg_esp=new_esp; +} + +static void DRC_CALL_CONV dynrec_push_dword(Bit32u value) { + Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-4)&cpu.stack.mask); + mem_writed(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value); + reg_esp=new_esp; +} + +static Bit16u DRC_CALL_CONV dynrec_pop_word(void) { + Bit16u val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask)); + reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask); + return val; +} + +static Bit32u DRC_CALL_CONV dynrec_pop_dword(void) { + Bit32u val=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask)); + reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask); + return val; +} diff --git a/src/cpu/core_dynrec/risc_x64.h b/src/cpu/core_dynrec/risc_x64.h new file mode 100644 index 00000000..e75667e5 --- /dev/null +++ b/src/cpu/core_dynrec/risc_x64.h @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +// some configuring defines that specify the capabilities of this architecture +// or aspects of the recompiling + +// try to use non-flags generating functions if possible +// #define DRC_FLAGS_INVALIDATION + +// type with the same size as a pointer +#define DRC_PTR_SIZE_IM Bit64u + +// calling convention modifier +#define DRC_CALL_CONV + + +// register mapping +typedef Bit8u HostReg; + +#define HOST_EAX 0 +#define HOST_ECX 1 +#define HOST_EDX 2 +#define HOST_EBX 3 +#define HOST_ESI 6 +#define HOST_EDI 7 + + +// register that holds function return values +#define FC_RETOP HOST_EAX + +// register used for address calculations, +#define FC_ADDR HOST_EBX + +// register that holds the first parameter +#define FC_OP1 HOST_EDI + +// register that holds the second parameter +#define FC_OP2 HOST_ESI + +// register that holds byte-accessible temporary values +#define FC_TMP_BA1 HOST_ECX + +// register that holds byte-accessible temporary values +#define FC_TMP_BA2 HOST_EDX + + +// temporary register for LEA +#define TEMP_REG_DRC HOST_ESI + + +// move a full register from reg_src to reg_dst +static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { + cache_addb(0x8b); // mov reg_dst,reg_src + cache_addb(0xc0+(reg_dst<<3)+reg_src); +} + + +static INLINE void gen_memaddr(HostReg reg,void* data) { + Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+5); + if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { + cache_addb(0x05+(reg<<3)); + // RIP-relative addressing is offset after the instruction + cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); + } else if ((Bit64u)data<0x100000000LL) { + cache_addw(0x2504+(reg<<3)); + cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL)); + } else { + E_Exit("DRC64:Unhandled memory reference"); + } +} + + +// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg +// 16bit moves must preserve the upper 16bit of the destination register +static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { + if (!dword) cache_addb(0x66); + cache_addb(0x8b); // mov reg,[data] + gen_memaddr(dest_reg,data); +} + + +// move a 16bit constant value into dest_reg +// the upper 16bit of the destination register must be preserved +static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { + cache_addb(0x66); + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addw(imm); +} + +// move a 32bit constant value into dest_reg +static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addd(imm); +} + +// move 32bit (dword==true) or 16bit (dword==false) of a register into memory +static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { + if (!dword) cache_addb(0x66); + cache_addb(0x89); // mov [data],reg + gen_memaddr(src_reg,dest); +} + +// move an 8bit value from memory into dest_reg +// the upper 24bit of the destination register must be preserved +static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { + cache_addb(0x8a); // mov reg,[data] + gen_memaddr(dest_reg,data); +} + +// move an 8bit value from memory into dest_reg +// the upper 16bit of the destination register must be preserved +// this function is allowed to load 16bit from memory as well if the host architecture +// does not provide 8bit register access for function parameter operands (FC_OP1/FC_OP2) +static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { + cache_addb(0x66); + cache_addb(0x8b); // mov reg,[data] + gen_memaddr(dest_reg,data); +} + +// move an 8bit constant value into dest_reg +// the upper 16bit of the destination register must be preserved +static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { + cache_addb(0xb0+dest_reg); // mov reg,imm + cache_addb(imm); +} + +// move an 8bit constant value into dest_reg +// the upper 16bit of the destination register must be preserved +// this function is allowed to load 16bit from memory as well if the host architecture +// does not provide 8bit register access for function parameter operands (FC_OP1/FC_OP2) +static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { + cache_addb(0x66); + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addw(imm); +} + +// move the lowest 8bit of a register into memory +static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { + cache_addb(0x88); // mov [data],reg + gen_memaddr(src_reg,dest); +} + + + +// convert an 8bit word to a 32bit dword +// the register is zero-extended (sign==false) or sign-extended (sign==true) +static void gen_extend_byte(bool sign,HostReg reg) { + cache_addw(0xb60f+(sign?0x800:0)); // movsx/movzx + cache_addb(0xc0+(reg<<3)+reg); +} + +// convert a 16bit word to a 32bit dword +// the register is zero-extended (sign==false) or sign-extended (sign==true) +static void gen_extend_word(bool sign,HostReg reg) { + cache_addw(0xb70f+(sign?0x800:0)); // movsx/movzx + cache_addb(0xc0+(reg<<3)+reg); +} + + + +// add a 32bit value from memory to a full register +static void gen_add(HostReg reg,void* op) { + cache_addb(0x03); // add reg,[data] + gen_memaddr(reg,op); +} + +// add a 32bit constant value to a full register +static void gen_add_imm(HostReg reg,Bit32u imm) { + cache_addw(0xc081+(reg<<8)); // add reg,imm + cache_addd(imm); +} + +// and a 32bit constant value with a full register +static void gen_and_imm(HostReg reg,Bit32u imm) { + cache_addw(0xe081+(reg<<8)); // and reg,imm + cache_addd(imm); +} + + + +// move a 32bit constant value into memory +static void gen_mov_direct_dword(void* dest,Bit32u imm) { + cache_addw(0x04c7); // mov [data],imm + cache_addb(0x25); + cache_addd((Bit32u)(((Bit64u)dest)&0xffffffffLL)); + cache_addd(imm); +} + +// move a 64bit constant value into a full register +static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm) { + cache_addb(0x48); + cache_addb(0xb8+dest_reg); // mov dest_reg,imm + cache_addq(imm); +} + +// move an address into memory +static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { + gen_mov_reg_qword(HOST_EAX,imm); + cache_addb(0x48); + gen_mov_word_from_reg(HOST_EAX,dest,true); +} + + +// add an 8bit constant value to a memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + cache_addw(0x0483); // add [data],imm + cache_addb(0x25); + cache_addd((Bit32u)(((Bit64u)dest)&0xffffffffLL)); + cache_addb(imm); +} + +// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value +static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if ((imm<128) && dword) { + gen_add_direct_byte(dest,(Bit8s)imm); + return; + } + if (!dword) cache_addb(0x66); + cache_addw(0x0481); // add [data],imm + cache_addb(0x25); + cache_addd((Bit32u)(((Bit64u)dest)&0xffffffffLL)); + if (dword) cache_addd((Bit32u)imm); + else cache_addw((Bit16u)imm); +} +/* +// add an 8bit constant value to a memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + cache_addb(0x83); // add [data],imm + gen_memaddr(0,dest,1); + cache_addb(imm); +} + +// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value +static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if ((imm<128) && dword) { + gen_add_direct_byte(dest,(Bit8s)imm); + return; + } + if (!dword) cache_addb(0x66); + cache_addw(0x81); // add [data],imm + gen_memaddr(0,dest,dword?1:2); + if (dword) cache_addd((Bit32u)imm); + else cache_addw((Bit16u)imm); +} +*/ + +// subtract an 8bit constant value from a memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + cache_addw(0x2c83); // sub [data],imm + cache_addb(0x25); + cache_addd((Bit32u)(((Bit64u)dest)&0xffffffffLL)); + cache_addb(imm); +} + +// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value +static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + if ((imm<128) && dword) { + gen_sub_direct_byte(dest,(Bit8s)imm); + return; + } + if (!dword) cache_addb(0x66); + cache_addw(0x2c81); // sub [data],imm + cache_addb(0x25); + cache_addd((Bit32u)(((Bit64u)dest)&0xffffffffLL)); + if (dword) cache_addd((Bit32u)imm); + else cache_addw((Bit16u)imm); +} +/* +// subtract an 8bit constant value from a memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + cache_addb(0x83); // sub [data],imm + gen_memaddr(5,dest,1); + cache_addb(imm); +} + +// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value +static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + if ((imm<128) && dword) { + gen_sub_direct_byte(dest,(Bit8s)imm); + return; + } + if (!dword) cache_addb(0x66); + cache_addb(0x81); // sub [data],imm + gen_memaddr(5,dest,dword?1:2); + if (dword) cache_addd((Bit32u)imm); + else cache_addw((Bit16u)imm); +} +*/ + + +// effective address calculation, destination is dest_reg +// scale_reg is scaled by scale (scale_reg*(2^scale)) and +// added to dest_reg, then the immediate value is added +static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) { + Bit8u rm_base; + Bitu imm_size; + if (!imm) { + 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 + } + + // ea_reg := ea_reg+TEMP_REG_DRC*(2^scale)+imm + // ea_reg := op1 + op2 *(2^scale)+imm + cache_addb(0x48); + cache_addb(0x8d); //LEA + cache_addb(0x04+(dest_reg << 3)+rm_base); //The sib indicator + cache_addb(dest_reg+(TEMP_REG_DRC<<3)+(scale<<6)); + + switch (imm_size) { + case 0: break; + case 1:cache_addb(imm);break; + case 4:cache_addd(imm);break; + } +} + +// effective address calculation, destination is dest_reg +// dest_reg is scaled by scale (dest_reg*(2^scale)), +// then the immediate value is added +static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { + // ea_reg := ea_reg*(2^scale)+imm + // ea_reg := op2 *(2^scale)+imm + cache_addb(0x48); + cache_addb(0x8d); //LEA + cache_addb(0x04+(dest_reg<<3)); + cache_addb(0x05+(dest_reg<<3)+(scale<<6)); + + cache_addd(imm); // always add dword immediate +} + + + +// generate a call to a parameterless function +static void INLINE gen_call_function_raw(void * func) { + cache_addb(0x48); + cache_addb(0xb8); // mov reg,imm64 + cache_addq((Bit64u)func); + cache_addw(0xd0ff); +} + +// generate a call to a function with paramcount parameters +// note: the parameters are loaded in the architecture specific way +// using the gen_load_param_ functions below +static Bit64u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) { + // align the stack + cache_addb(0x48); + cache_addw(0xc48b); // mov rax,rsp + + cache_addb(0x48); + cache_addw(0xec83); // sub rsp,0x08 + cache_addb(0x08); // 0x08==return address pushed onto stack by call + + cache_addb(0x48); + cache_addw(0xe483); // and esp,0xfffffffffffffff0 + cache_addb(0xf0); + + cache_addb(0x48); + cache_addw(0xc483); // add rsp,0x08 + cache_addb(0x08); + + cache_addb(0x50); // push rax (==old rsp) + + Bit64u proc_addr=(Bit64u)cache.pos; + + // Do the actual call to the procedure + cache_addb(0x48); + cache_addb(0xb8); // mov reg,imm64 + cache_addq((Bit64u)func); + + cache_addw(0xd0ff); + + // restore stack + cache_addb(0x5c); // pop rsp + + return proc_addr; +} + + +// load an immediate value as param'th function parameter +static void INLINE gen_load_param_imm(Bitu imm,Bitu param) { + // move an immediate 32bit value into a 64bit param reg + switch (param) { + case 0: // mov param1,imm32 + gen_mov_dword_to_reg_imm(FC_OP1,(Bit32u)imm); + break; + case 1: // mov param2,imm32 + gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm); + break; +#if defined (_MSC_VER) + case 2: // mov r8,imm32 + cache_addw(0xb849); + cache_addq((Bit32u)imm); + break; + case 3: // mov r9,imm32 + cache_addw(0xb949); + cache_addq((Bit32u)imm); + break; +#else + case 2: // mov rdx,imm32 + gen_mov_dword_to_reg_imm(HOST_EDX,(Bit32u)imm); + break; + case 3: // mov rcx,imm32 + gen_mov_dword_to_reg_imm(HOST_ECX,(Bit32u)imm); + break; +#endif + default: + E_Exit("I(mm) >4 params unsupported"); + break; + } +} + +// load an address as param'th function parameter +static void INLINE gen_load_param_addr(DRC_PTR_SIZE_IM addr,Bitu param) { + // move an immediate 64bit value into a 64bit param reg + switch (param) { + case 0: // mov param1,addr64 + gen_mov_reg_qword(FC_OP1,addr); + break; + case 1: // mov param2,addr64 + gen_mov_reg_qword(FC_OP2,addr); + break; +#if defined (_MSC_VER) + case 2: // mov r8,addr64 + cache_addw(0xb849); + cache_addq(addr); + break; + case 3: // mov r9,addr64 + cache_addw(0xb949); + cache_addq(addr); + break; +#else + case 2: // mov rdx,addr64 + gen_mov_reg_qword(HOST_EDX,addr); + break; + case 3: // mov rcx,addr64 + gen_mov_reg_qword(HOST_ECX,addr); + break; +#endif + default: + E_Exit("A(ddr) >4 params unsupported"); + break; + } +} + +// load a host-register as param'th function parameter +static void INLINE gen_load_param_reg(Bitu reg,Bitu param) { + // move a register into a 64bit param reg, {inputregs}!={outputregs} + switch (param) { + case 0: // mov param1,reg&7 + gen_mov_regs(FC_OP1,reg&7); + break; + case 1: // mov param2,reg&7 + gen_mov_regs(FC_OP2,reg&7); + break; +#if defined (_MSC_VER) + case 2: // mov r8,reg&7 + cache_addb(0x49); + gen_mov_regs(0,reg&7); + break; + case 3: // mov r9,reg&7 + cache_addb(0x49); + gen_mov_regs(1,reg&7); + break; +#else + case 2: // mov rdx,reg&7 + gen_mov_regs(HOST_EDX,reg&7); + break; + case 3: // mov rcx,reg&7 + gen_mov_regs(HOST_ECX,reg&7); + break; +#endif + default: + E_Exit("R(eg) >4 params unsupported"); + break; + } +} + +// load a value from memory as param'th function parameter +static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { + // move memory content into a 64bit param reg + switch (param) { + case 0: // mov param1,[mem] + gen_mov_word_to_reg(FC_OP1,(void*)mem,true); + break; + case 1: // mov param2,[mem] + gen_mov_word_to_reg(FC_OP2,(void*)mem,true); + break; +#if defined (_MSC_VER) + case 2: // mov r8,[mem] + cache_addb(0x49); + gen_mov_word_to_reg(0,(void*)mem,true); + break; + case 3: // mov r9,[mem] + cache_addb(0x49); + gen_mov_word_to_reg(1,(void*)mem,true); + break; +#else + case 2: // mov rdx,[mem] + gen_mov_word_to_reg(HOST_EDX,(void*)mem,true); + break; + case 3: // mov rcx,[mem] + gen_mov_word_to_reg(HOST_ECX,(void*)mem,true); + break; +#endif + default: + E_Exit("R(eg) >4 params unsupported"); + break; + } +} + + + +// jump to an address pointed at by ptr, offset is in imm +static void gen_jmp_ptr(void * ptr,Bits imm=0) { + cache_addw(0xa148); // mov rax,[data] + cache_addq((Bit64u)ptr); + + cache_addb(0xff); // jmp [rax+imm] + if (!imm) { + cache_addb(0x20); + } else if ((imm>=-128 && imm<=127)) { + cache_addb(0x60); + cache_addb(imm); + } else { + cache_addb(0xa0); + cache_addd(imm); + } +} + + +// short conditional jump (+-127 bytes) if register is zero +// the destination is set by gen_fill_branch() later +static Bit64u gen_create_branch_on_zero(HostReg reg,bool dword) { + if (!dword) cache_addb(0x66); + cache_addb(0x0b); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x0074); // jz addr + return ((Bit64u)cache.pos-1); +} + +// short conditional jump (+-127 bytes) if register is nonzero +// the destination is set by gen_fill_branch() later +static Bit64u gen_create_branch_on_nonzero(HostReg reg,bool dword) { + if (!dword) cache_addb(0x66); + cache_addb(0x0b); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x0075); // jnz addr + return ((Bit64u)cache.pos-1); +} + +// short conditional jump (+-127 bytes) if register +// (as set by a boolean operation) is nonzero +// the destination is set by gen_fill_branch() later +static Bit64u gen_create_branch_on_nonzero_bool(HostReg reg) { + cache_addb(0x0a); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x0075); // jnz addr + return ((Bit64u)cache.pos-1); +} + +// calculate relative offset and fill it into the location pointed to by data +static void gen_fill_branch(DRC_PTR_SIZE_IM data) { +#if C_DEBUG + Bit64s len=(Bit64u)cache.pos-data; + if (len<0) len=-len; + if (len>126) LOG_MSG("Big jump %d",len); +#endif + *(Bit8u*)data=(Bit8u)((Bit64u)cache.pos-data-1); +} + +// conditional jump if register is nonzero +// for isdword==true the 32bit of the register are tested +// for isdword==false the lowest 8bit of the register are tested +static Bit64u gen_create_branch_long_nonzero(HostReg reg,bool isdword) { + // isdword: cmp reg32,0 + // not isdword: cmp reg8,0 + cache_addb(0x0a+(isdword?1:0)); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x850f); // jnz + cache_addd(0); + return ((Bit64u)cache.pos-4); +} + +// compare 32bit-register against zero and jump if value less/equal than zero +static Bit64u gen_create_branch_long_leqzero(HostReg reg) { + cache_addw(0xf883+(reg<<8)); + cache_addb(0x00); // cmp reg,0 + + cache_addw(0x8e0f); // jle + cache_addd(0); + return ((Bit64u)cache.pos-4); +} + +// calculate long relative offset and fill it into the location pointed to by data +static void gen_fill_branch_long(Bit64u data) { + *(Bit32u*)data=(Bit32u)((Bit64u)cache.pos-data-4); +} + + +static void gen_run_code(void) { + cache_addb(0x53); // push rbx + cache_addw(0xd0ff+(FC_OP1<<8)); // call rdi + cache_addb(0x5b); // pop rbx +} + +// return from a function +static void gen_return_function(void) { + cache_addb(0xc3); // ret +} diff --git a/src/cpu/core_dynrec/risc_x86.h b/src/cpu/core_dynrec/risc_x86.h new file mode 100644 index 00000000..00b0e432 --- /dev/null +++ b/src/cpu/core_dynrec/risc_x86.h @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +// some configuring defines that specify the capabilities of this architecture +// or aspects of the recompiling + +// try to use non-flags generating functions if possible +#define DRC_FLAGS_INVALIDATION + +// type with the same size as a pointer +#define DRC_PTR_SIZE_IM Bit32u + +// calling convention modifier +#define DRC_CALL_CONV _fastcall + + +// register mapping +enum HostReg { + HOST_EAX=0, + HOST_ECX, + HOST_EDX, + HOST_EBX, + HOST_ESP, + HOST_EBP, + HOST_ESI, + HOST_EDI +}; + + +// register that holds function return values +#define FC_RETOP HOST_EAX + +// register used for address calculations, +#define FC_ADDR HOST_EBX + +// register that holds the first parameter +#define FC_OP1 HOST_ECX + +// register that holds the second parameter +#define FC_OP2 HOST_EDX + +// register that holds byte-accessible temporary values +#define FC_TMP_BA1 HOST_ECX + +// register that holds byte-accessible temporary values +#define FC_TMP_BA2 HOST_EDX + + +// temporary register for LEA +#define TEMP_REG_DRC HOST_ESI + + +// move a full register from reg_src to reg_dst +static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { + cache_addb(0x8b); // mov reg_dst,reg_src + cache_addb(0xc0+(reg_dst<<3)+reg_src); +} + +// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg +// 16bit moves must preserve the upper 16bit of the destination register +static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { + if (!dword) cache_addb(0x66); + cache_addw(0x058b+(dest_reg<<11)); // mov reg,[data] + cache_addd((Bit32u)data); +} + +// move a 16bit constant value into dest_reg +// the upper 16bit of the destination register must be preserved +static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { + cache_addb(0x66); + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addw(imm); +} + +// move a 32bit constant value into dest_reg +static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addd(imm); +} + +// move 32bit (dword==true) or 16bit (dword==false) of a register into memory +static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { + if (!dword) cache_addb(0x66); + cache_addw(0x0589+(src_reg<<11)); // mov [data],reg + cache_addd((Bit32u)dest); +} + +// move an 8bit value from memory into dest_reg +// the upper 16bit of the destination register must be preserved +static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { + cache_addw(0x058a+(dest_reg<<11)); // mov reg,[data] + cache_addd((Bit32u)data); +} + +// move an 8bit value from memory into dest_reg +// the upper 16bit of the destination register must be preserved +// this function is allowed to load 16bit from memory as well if the host architecture +// does not provide 8bit register access for function parameter operands (FC_OP1/FC_OP2) +static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { + cache_addb(0x66); + cache_addw(0x058b+(dest_reg<<11)); // mov reg,[data] + cache_addd((Bit32u)data); +} + +// move an 8bit constant value into dest_reg +// the upper 16bit of the destination register must be preserved +static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { + cache_addb(0xb0+dest_reg); // mov reg,imm + cache_addb(imm); +} + +// move an 8bit constant value into dest_reg +// the upper 16bit of the destination register must be preserved +// this function is allowed to load 16bit from memory as well if the host architecture +// does not provide 8bit register access for function parameter operands (FC_OP1/FC_OP2) +static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { + cache_addb(0x66); + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addw(imm); +} + +// move the lowest 8bit of a register into memory +static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { + cache_addw(0x0588+(src_reg<<11)); // mov [data],reg + cache_addd((Bit32u)dest); +} + + + +// convert an 8bit word to a 32bit dword +// the register is zero-extended (sign==false) or sign-extended (sign==true) +static void gen_extend_byte(bool sign,HostReg reg) { + cache_addw(0xb60f+(sign?0x800:0)); // movsx/movzx + cache_addb(0xc0+(reg<<3)+reg); +} + +// convert a 16bit word to a 32bit dword +// the register is zero-extended (sign==false) or sign-extended (sign==true) +static void gen_extend_word(bool sign,HostReg reg) { + cache_addw(0xb70f+(sign?0x800:0)); // movsx/movzx + cache_addb(0xc0+(reg<<3)+reg); +} + + + +// add a 32bit value from memory to a full register +static void gen_add(HostReg reg,void* op) { + cache_addw(0x0503+(reg<<11)); // add reg,[data] + cache_addd((Bit32u)op); +} + +// add a 32bit constant value to a full register +static void gen_add_imm(HostReg reg,Bit32u imm) { + cache_addw(0xc081+(reg<<8)); // add reg,imm + cache_addd(imm); +} + +// and a 32bit constant value with a full register +static void gen_and_imm(HostReg reg,Bit32u imm) { + cache_addw(0xe081+(reg<<8)); // and reg,imm + cache_addd(imm); +} + + + +// move a 32bit constant value into memory +static void gen_mov_direct_dword(void* dest,Bit32u imm) { + cache_addw(0x05c7); // mov [data],imm + cache_addd((Bit32u)dest); + cache_addd(imm); +} + +// move an address into memory +static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { + gen_mov_direct_dword(dest,(Bit32u)imm); +} + + +// add an 8bit constant value to a memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + cache_addw(0x0583); // add [data],imm + cache_addd((Bit32u)dest); + cache_addb(imm); +} + +// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value +static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if ((imm<128) && dword) { + gen_add_direct_byte(dest,(Bit8s)imm); + return; + } + if (!dword) cache_addb(0x66); + cache_addw(0x0581); // add [data],imm + cache_addd((Bit32u)dest); + if (dword) cache_addd((Bit32u)imm); + else cache_addw((Bit16u)imm); +} + +// subtract an 8bit constant value from a memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + cache_addw(0x2d83); // sub [data],imm + cache_addd((Bit32u)dest); + cache_addb(imm); +} + +// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value +static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + if ((imm<128) && dword) { + gen_sub_direct_byte(dest,(Bit8s)imm); + return; + } + if (!dword) cache_addb(0x66); + cache_addw(0x2d81); // sub [data],imm + cache_addd((Bit32u)dest); + if (dword) cache_addd((Bit32u)imm); + else cache_addw((Bit16u)imm); +} + + + +// effective address calculation, destination is dest_reg +// scale_reg is scaled by scale (scale_reg*(2^scale)) and +// added to dest_reg, then the immediate value is added +static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) { + Bit8u rm_base; + Bitu imm_size; + if (!imm) { + 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 + } + + // ea_reg := ea_reg+TEMP_REG_DRC*(2^scale)+imm + // ea_reg := op1 + op2 *(2^scale)+imm + cache_addb(0x8d); //LEA + cache_addb(0x04+(dest_reg << 3)+rm_base); //The sib indicator + cache_addb(dest_reg+(TEMP_REG_DRC<<3)+(scale<<6)); + + switch (imm_size) { + case 0: break; + case 1:cache_addb(imm);break; + case 4:cache_addd(imm);break; + } +} + +// effective address calculation, destination is dest_reg +// dest_reg is scaled by scale (dest_reg*(2^scale)), +// then the immediate value is added +static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { + // ea_reg := ea_reg*(2^scale)+imm + // ea_reg := op2 *(2^scale)+imm + cache_addb(0x8d); //LEA + cache_addb(0x04+(dest_reg<<3)); + cache_addb(0x05+(dest_reg<<3)+(scale<<6)); + + cache_addd(imm); // always add dword immediate +} + + + +// generate a call to a parameterless function +static void INLINE gen_call_function_raw(void * func) { + cache_addb(0xe8); + cache_addd((Bit32u)func - (Bit32u)cache.pos-4); +} + +// generate a call to a function with paramcount parameters +// note: the parameters are loaded in the architecture specific way +// using the gen_load_param_ functions below +static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) { + // Do the actual call to the procedure + cache_addb(0xe8); + Bit32u proc_addr=(Bit32u)cache.pos; + 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((!fastcall)?paramcount*4:0); + } + return proc_addr; +} + + +// load an immediate value as param'th function parameter +static void INLINE gen_load_param_imm(Bitu imm,Bitu param) { + cache_addb(0x68); // push immediate + cache_addd(imm); +} + +// load an address as param'th function parameter +static void INLINE gen_load_param_addr(Bitu addr,Bitu param) { + cache_addb(0x68); // push immediate (address) + cache_addd(addr); +} + +// load a host-register as param'th function parameter +static void INLINE gen_load_param_reg(Bitu reg,Bitu param) { + cache_addb(0x50+(reg&7)); // push reg +} + +// load a value from memory as param'th function parameter +static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { + cache_addw(0x35ff); // push [] + cache_addd(mem); +} + + + +// jump to an address pointed at by ptr, offset is in imm +static void gen_jmp_ptr(void * ptr,Bits imm=0) { + gen_mov_word_to_reg(HOST_EAX,ptr,true); + cache_addb(0xff); // jmp [eax+imm] + if (!imm) { + cache_addb(0x20); + } else if ((imm>=-128 && imm<=127)) { + cache_addb(0x60); + cache_addb(imm); + } else { + cache_addb(0xa0); + cache_addd(imm); + } +} + + +// short conditional jump (+-127 bytes) if register is zero +// the destination is set by gen_fill_branch() later +static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) { + if (!dword) cache_addb(0x66); + cache_addb(0x0b); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x0074); // jz addr + return ((Bit32u)cache.pos-1); +} + +// short conditional jump (+-127 bytes) if register is nonzero +// the destination is set by gen_fill_branch() later +static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) { + if (!dword) cache_addb(0x66); + cache_addb(0x0b); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x0075); // jnz addr + return ((Bit32u)cache.pos-1); +} + +// short conditional jump (+-127 bytes) if register +// (as set by a boolean operation) is nonzero +// the destination is set by gen_fill_branch() later +static Bit32u gen_create_branch_on_nonzero_bool(HostReg reg) { + cache_addb(0x0a); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x0075); // jnz addr + return ((Bit32u)cache.pos-1); +} + +// calculate relative offset and fill it into the location pointed to by data +static void gen_fill_branch(DRC_PTR_SIZE_IM data) { +#if C_DEBUG + Bits len=(Bit32u)cache.pos-data; + if (len<0) len=-len; + if (len>126) LOG_MSG("Big jump %d",len); +#endif + *(Bit8u*)data=(Bit8u)((Bit32u)cache.pos-data-1); +} + +// conditional jump if register is nonzero +// for isdword==true the 32bit of the register are tested +// for isdword==false the lowest 8bit of the register are tested +static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) { + // isdword: cmp reg32,0 + // not isdword: cmp reg8,0 + cache_addb(0x0a+(isdword?1:0)); // or reg,reg + cache_addb(0xc0+reg+(reg<<3)); + + cache_addw(0x850f); // jnz + cache_addd(0); + return ((Bit32u)cache.pos-4); +} + +// compare 32bit-register against zero and jump if value less/equal than zero +static Bit32u gen_create_branch_long_leqzero(HostReg reg) { + cache_addw(0xf883+(reg<<8)); + cache_addb(0x00); // cmp reg,0 + + cache_addw(0x8e0f); // jle + cache_addd(0); + return ((Bit32u)cache.pos-4); +} + +// calculate long relative offset and fill it into the location pointed to by data +static void gen_fill_branch_long(Bit32u data) { + *(Bit32u*)data=((Bit32u)cache.pos-data-4); +} + + +static void gen_run_code(void) { + cache_addd(0x0424448b); // mov eax,[esp+4] + cache_addb(0x53); // push ebx + cache_addw(0xd0ff); // call eax + cache_addb(0x5b); // pop ebx +} + +// return from a function +static void gen_return_function(void) { + cache_addb(0xc3); // ret +} diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index ba6e72a7..ad9a8b11 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: cpu.cpp,v 1.100 2007-06-01 16:40:40 c2woody Exp $ */ +/* $Id: cpu.cpp,v 1.101 2007-06-02 11:47:05 c2woody Exp $ */ #include #include @@ -68,6 +68,10 @@ void CPU_Core_Dyn_X86_Init(void); void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache); void CPU_Core_Dyn_X86_Cache_Close(void); void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu); +#elif (C_DYNREC) +void CPU_Core_Dynrec_Init(void); +void CPU_Core_Dynrec_Cache_Init(bool enable_cache); +void CPU_Core_Dynrec_Cache_Close(void); #endif /* In debug mode exceptions are tested and dosbox exits when @@ -2071,6 +2075,8 @@ public: CPU_Core_Full_Init(); #if (C_DYNAMIC_X86) CPU_Core_Dyn_X86_Init(); +#elif (C_DYNREC) + CPU_Core_Dynrec_Init(); #endif MAPPER_AddHandler(CPU_CycleDecrease,MK_f11,MMOD1,"cycledown","Dec Cycles"); MAPPER_AddHandler(CPU_CycleIncrease,MK_f12,MMOD1,"cycleup" ,"Inc Cycles"); @@ -2174,6 +2180,10 @@ public: cpudecoder=&CPU_Core_Normal_Run; CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CORE; } +#elif (C_DYNREC) + else if (!strcasecmp(core,"dynamic")) { + cpudecoder=&CPU_Core_Dynrec_Run; + } #endif else { LOG_MSG("CPU:Unknown core type %s, switching back to normal.",core); @@ -2181,6 +2191,8 @@ public: #if (C_DYNAMIC_X86) CPU_Core_Dyn_X86_Cache_Init(!strcasecmp(core,"dynamic") || !strcasecmp(core,"dynamic_nodhfpu")); +#elif (C_DYNREC) + CPU_Core_Dynrec_Cache_Init(!strcasecmp(core,"dynamic")); #endif if(CPU_CycleMax <= 0) CPU_CycleMax = 3000; @@ -2198,6 +2210,8 @@ static CPU * test; void CPU_ShutDown(Section* sec) { #if (C_DYNAMIC_X86) CPU_Core_Dyn_X86_Cache_Close(); +#elif (C_DYNREC) + CPU_Core_Dynrec_Cache_Close(); #endif delete test; } diff --git a/visualc_net/dosbox.vcproj b/visualc_net/dosbox.vcproj index 9c7bef85..4a454829 100644 --- a/visualc_net/dosbox.vcproj +++ b/visualc_net/dosbox.vcproj @@ -167,6 +167,9 @@ + + @@ -278,6 +281,34 @@ RelativePath="..\src\cpu\core_dyn_x86\string.h"> + + + + + + + + + + + + + + + + + +