1
0
Fork 0

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:
Sebastian Strohhäcker 2007-06-02 11:47:06 +00:00
parent 43de744e01
commit f65e8e5f09
15 changed files with 7637 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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
View 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

View 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
View 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; */
}

View 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;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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
}

View 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
}

View file

@ -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;
}

View file

@ -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"