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
This commit is contained in:
parent
43de744e01
commit
f65e8e5f09
15 changed files with 7637 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
core_dyn_x86.cpp core_dynrec.cpp
|
||||
|
|
314
src/cpu/core_dynrec.cpp
Normal file
314
src/cpu/core_dynrec.cpp
Normal file
|
@ -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 <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if (C_HAVE_MPROTECT)
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <limits.h>
|
||||
#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
|
2
src/cpu/core_dynrec/Makefile.am
Normal file
2
src/cpu/core_dynrec/Makefile.am
Normal file
|
@ -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
|
633
src/cpu/core_dynrec/cache.h
Normal file
633
src/cpu/core_dynrec/cache.h
Normal file
|
@ -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;i<block->cache.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 (size<CACHE_MAXSIZE) {
|
||||
if (!nextblock)
|
||||
goto skipresize;
|
||||
// merge blocks
|
||||
size+=nextblock->cache.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;i<CACHE_BLOCKS-1;i++) {
|
||||
cache_blocks[i].link[0].to=(CacheBlockDynRec *)1;
|
||||
cache_blocks[i].link[1].to=(CacheBlockDynRec *)1;
|
||||
cache_blocks[i].cache.next=&cache_blocks[i+1];
|
||||
}
|
||||
}
|
||||
if (cache_code_start_ptr==NULL) {
|
||||
// allocate the code cache memory
|
||||
cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
|
||||
if(!cache_code_start_ptr) E_Exit("Allocating dynamic cache failed");
|
||||
|
||||
// align the cache at a page boundary
|
||||
cache_code=(Bit8u*)(((long)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1)); //MEM LEAK. store old pointer if you want to free it.
|
||||
|
||||
cache_code_link_blocks=cache_code;
|
||||
cache_code=cache_code+PAGESIZE_TEMP;
|
||||
|
||||
#if (C_HAVE_MPROTECT)
|
||||
if(mprotect(cache_code_link_blocks,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP,PROT_WRITE|PROT_READ|PROT_EXEC))
|
||||
LOG_MSG("Setting excute permission on the code cache has failed");
|
||||
#endif
|
||||
CacheBlockDynRec * block=cache_getblock();
|
||||
cache.block.first=block;
|
||||
cache.block.active=block;
|
||||
block->cache.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;i<CACHE_PAGES;i++) {
|
||||
CodePageHandlerDynRec * newpage=new CodePageHandlerDynRec();
|
||||
newpage->next=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; */
|
||||
}
|
582
src/cpu/core_dynrec/decoder.h
Normal file
582
src/cpu/core_dynrec/decoder.h
Normal file
|
@ -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;
|
||||
}
|
1091
src/cpu/core_dynrec/decoder_basic.h
Normal file
1091
src/cpu/core_dynrec/decoder_basic.h
Normal file
File diff suppressed because it is too large
Load diff
1415
src/cpu/core_dynrec/decoder_opcodes.h
Normal file
1415
src/cpu/core_dynrec/decoder_opcodes.h
Normal file
File diff suppressed because it is too large
Load diff
679
src/cpu/core_dynrec/dyn_fpu.h
Normal file
679
src/cpu/core_dynrec/dyn_fpu.h
Normal file
|
@ -0,0 +1,679 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 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_FPU
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#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
|
1812
src/cpu/core_dynrec/operators.h
Normal file
1812
src/cpu/core_dynrec/operators.h
Normal file
File diff suppressed because it is too large
Load diff
631
src/cpu/core_dynrec/risc_x64.h
Normal file
631
src/cpu/core_dynrec/risc_x64.h
Normal file
|
@ -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
|
||||
}
|
427
src/cpu/core_dynrec/risc_x86.h
Normal file
427
src/cpu/core_dynrec/risc_x86.h
Normal file
|
@ -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
|
||||
}
|
|
@ -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 <assert.h>
|
||||
#include <sstream>
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -167,6 +167,9 @@
|
|||
<File
|
||||
RelativePath="..\src\cpu\core_dyn_x86.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_full.cpp">
|
||||
</File>
|
||||
|
@ -278,6 +281,34 @@
|
|||
RelativePath="..\src\cpu\core_dyn_x86\string.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="core_dynrec"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\cache.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\decoder.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\decoder_basic.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\decoder_opcodes.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\dyn_fpu.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\operators.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\risc_x86.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\risc_x64.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="debug"
|
||||
|
|
Loading…
Add table
Reference in a new issue