x86 dynamic core
Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@1515
This commit is contained in:
parent
f5c2365e73
commit
f4059666c3
8 changed files with 2676 additions and 2 deletions
|
@ -1,6 +1,7 @@
|
|||
SUBDIRS = core_full core_normal
|
||||
SUBDIRS = core_full core_normal core_dyn_x86
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
noinst_LIBRARIES = libcpu.a
|
||||
libcpu_a_SOURCES = callback.cpp cpu.cpp flags.cpp modrm.cpp modrm.h core_full.cpp instructions.h \
|
||||
paging.cpp lazyflags.h core_normal.cpp
|
||||
paging.cpp lazyflags.h core_normal.cpp \
|
||||
core_dyn_x86.cpp
|
344
src/cpu/core_dyn_x86.cpp
Normal file
344
src/cpu/core_dyn_x86.cpp
Normal file
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2003 The DOSBox Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if (C_DYNAMIC_X86)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "callback.h"
|
||||
#include "regs.h"
|
||||
#include "mem.h"
|
||||
#include "cpu.h"
|
||||
#include "debug.h"
|
||||
#include "paging.h"
|
||||
#include "inout.h"
|
||||
|
||||
#define CACHE_TOTAL (1024*1024*2)
|
||||
#define CACHE_MAXSIZE (4096)
|
||||
#define CACHE_BLOCKS (50*1024)
|
||||
#define CACHE_ALIGN (16)
|
||||
#define DYN_HASH_SHIFT (4)
|
||||
#define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT)
|
||||
#define DYN_LINKS (16)
|
||||
|
||||
#if 1
|
||||
#define DYN_LOG LOG_MSG
|
||||
#else
|
||||
#define DYN_LOG
|
||||
#endif
|
||||
|
||||
enum {
|
||||
G_EAX,G_ECX,G_EDX,G_EBX,
|
||||
G_ESP,G_EBP,G_ESI,G_EDI,
|
||||
G_ES,G_CS,G_SS,G_DS,G_FS,G_GS,
|
||||
G_FLAGS,G_SMASK,G_EIP,
|
||||
G_EA,G_STACK,G_CYCLES,
|
||||
G_TMPB,G_TMPW,G_SHIFT,
|
||||
G_EXIT,
|
||||
G_MAX,
|
||||
};
|
||||
|
||||
enum SingleOps {
|
||||
SOP_INC,SOP_DEC,
|
||||
SOP_NOT,SOP_NEG,
|
||||
};
|
||||
|
||||
enum DualOps {
|
||||
DOP_ADD,DOP_ADC,
|
||||
DOP_SUB,DOP_SBB,
|
||||
DOP_CMP,DOP_XOR,
|
||||
DOP_AND,DOP_OR,
|
||||
DOP_MOV,
|
||||
DOP_TEST,
|
||||
DOP_IMUL,
|
||||
DOP_XCHG,
|
||||
};
|
||||
|
||||
enum ShiftOps {
|
||||
SHIFT_ROL,SHIFT_ROR,
|
||||
SHIFT_RCL,SHIFT_RCR,
|
||||
SHIFT_SHL,SHIFT_SHR,
|
||||
SHIFT_SAR,
|
||||
};
|
||||
|
||||
enum BranchTypes {
|
||||
BR_O,BR_NO,BR_B,BR_NB,
|
||||
BR_Z,BR_NZ,BR_BE,BR_NBE,
|
||||
BR_S,BR_NS,BR_P,BR_NP,
|
||||
BR_L,BR_NL,BR_LE,BR_NLE
|
||||
};
|
||||
|
||||
enum BlockType {
|
||||
BT_Free=0,
|
||||
BT_Normal,
|
||||
BT_SingleLink,
|
||||
BT_DualLink,
|
||||
BT_CheckFlags,
|
||||
};
|
||||
|
||||
enum BlockReturn {
|
||||
BR_Normal=0,
|
||||
BR_Cycles,
|
||||
BR_Link1,BR_Link2,
|
||||
BR_Opcode,
|
||||
BR_CallBack,
|
||||
};
|
||||
|
||||
#define DYNFLG_HAS16 0x1 //Would like 8-bit host reg support
|
||||
#define DYNFLG_HAS8 0x2 //Would like 16-bit host reg support
|
||||
#define DYNFLG_LOAD 0x4 //Load value when accessed
|
||||
#define DYNFLG_SAVE 0x8 //Needs to be saved back at the end of block
|
||||
#define DYNFLG_CHANGED 0x10 //Load value only once because of save
|
||||
#define DYNFLG_LOADONCE 0x20 //Load value only once because of save
|
||||
|
||||
class GenReg;
|
||||
class CodePageHandler;
|
||||
|
||||
struct DynReg {
|
||||
Bitu flags;
|
||||
GenReg * genreg;
|
||||
void * data;
|
||||
};
|
||||
|
||||
enum DynAccess {
|
||||
DA_d,DA_w,
|
||||
DA_bh,DA_bl
|
||||
};
|
||||
|
||||
enum ByteCombo {
|
||||
BC_ll,BC_lh,
|
||||
BC_hl,BC_hh,
|
||||
};
|
||||
|
||||
static DynReg DynRegs[G_MAX];
|
||||
#define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ]
|
||||
|
||||
static struct {
|
||||
Bitu ea,tmpb,tmpd,stack,shift;
|
||||
} extra_regs;
|
||||
|
||||
static void IllegalOption(void) {
|
||||
E_Exit("Illegal option");
|
||||
}
|
||||
|
||||
#include "core_dyn_x86\cache.h"
|
||||
|
||||
static struct {
|
||||
Bitu callback;
|
||||
CacheBlock * lastblock;
|
||||
} core_dyn;
|
||||
|
||||
|
||||
#include "core_dyn_x86\risc_x86.h"
|
||||
|
||||
struct DynState {
|
||||
DynReg regs[G_MAX];
|
||||
};
|
||||
|
||||
static void dyn_releaseregs(void) {
|
||||
for (Bitu i=0;i<G_MAX;i++) gen_releasereg(&DynRegs[i]);
|
||||
}
|
||||
static void dyn_load_flags(void) {
|
||||
/* Load the host flags with emulated flags */
|
||||
gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(FLAGS));
|
||||
gen_dop_word_imm(DOP_AND,true,DREG(TMPW),FMASK_TEST);
|
||||
gen_load_flags(DREG(TMPW));
|
||||
gen_releasereg(DREG(TMPW));
|
||||
gen_releasereg(DREG(FLAGS));
|
||||
}
|
||||
|
||||
static void dyn_save_flags(bool stored=false) {
|
||||
/* Store the host flags back in emulated ones */
|
||||
gen_save_flags(DREG(EXIT),stored);
|
||||
gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
|
||||
gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FMASK_TEST);
|
||||
gen_dop_word(DOP_OR,true,DREG(FLAGS),DREG(EXIT)); //flags are marked for save
|
||||
gen_releasereg(DREG(EXIT));
|
||||
}
|
||||
|
||||
|
||||
static void dyn_savestate(DynState * state) {
|
||||
for (Bitu i=0;i<G_MAX;i++) {
|
||||
state->regs[i].flags=DynRegs[i].flags;
|
||||
state->regs[i].genreg=DynRegs[i].genreg;
|
||||
}
|
||||
}
|
||||
|
||||
static void dyn_loadstate(DynState * state) {
|
||||
for (Bitu i=0;i<G_MAX;i++) {
|
||||
gen_setupreg(&DynRegs[i],&state->regs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dyn_synchstate(DynState * state) {
|
||||
for (Bitu i=0;i<G_MAX;i++) {
|
||||
gen_synchreg(&DynRegs[i],&state->regs[i]);
|
||||
}
|
||||
}
|
||||
#include "core_dyn_x86\decoder.h"
|
||||
|
||||
Bits CPU_Core_Dyn_X86_Run(void) {
|
||||
/* Determine the linear address of CS:EIP */
|
||||
restart_core:
|
||||
PhysPt ip_point=SegPhys(cs)+reg_eip;
|
||||
Bitu ip_page=ip_point>>12;
|
||||
mem_readb(ip_point); //Init whatever page we are in
|
||||
PageHandler * handler=paging.tlb.handler[ip_page];
|
||||
CodePageHandler * chandler=0;
|
||||
#if C_HEAVY_DEBUG
|
||||
if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
|
||||
#endif
|
||||
if (handler->flags & PFLAG_HASCODE) {
|
||||
/* Find correct Dynamic Block to run */
|
||||
chandler=(CodePageHandler *)handler;
|
||||
findblock:;
|
||||
CacheBlock * block=chandler->FindCacheBlock(ip_point&4095);
|
||||
if (!block) {
|
||||
block=CreateCacheBlock(ip_point,cpu.code.big,128);
|
||||
DYN_LOG("Created block size %x type %d",block->cache.size,block->type);
|
||||
chandler->AddCacheBlock(block);
|
||||
if (block->page.end>=4096) {
|
||||
DYN_LOG("block crosses page boundary");
|
||||
}
|
||||
}
|
||||
run_block:
|
||||
BlockReturn ret=gen_runcode(block->cache.start);
|
||||
switch (ret) {
|
||||
case BR_Normal:
|
||||
/* Maybe check if we staying in the same page? */
|
||||
#if C_HEAVY_DEBUG
|
||||
if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
|
||||
#endif
|
||||
goto restart_core;
|
||||
case BR_Cycles:
|
||||
#if C_HEAVY_DEBUG
|
||||
if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
|
||||
#endif
|
||||
return CBRET_NONE;
|
||||
case BR_CallBack:
|
||||
return core_dyn.callback;
|
||||
case BR_Opcode:
|
||||
CPU_CycleLeft+=CPU_Cycles;
|
||||
CPU_Cycles=1;
|
||||
return CPU_Core_Normal_Run();
|
||||
case BR_Link1:
|
||||
case BR_Link2:
|
||||
{
|
||||
Bitu temp_ip=SegPhys(cs)+reg_eip;
|
||||
Bitu temp_page=temp_ip >> 12;
|
||||
CodePageHandler * temp_handler=(CodePageHandler *)paging.tlb.handler[temp_page];
|
||||
if (temp_handler->flags & PFLAG_HASCODE) {
|
||||
block=temp_handler->FindCacheBlock(temp_ip & 4095);
|
||||
if (!block) goto restart_core;
|
||||
cache_linkblocks(core_dyn.lastblock,block,ret==BR_Link2);
|
||||
goto run_block;
|
||||
}
|
||||
}
|
||||
goto restart_core;
|
||||
}
|
||||
} else {
|
||||
if (handler->flags & PFLAG_NOCODE) {
|
||||
LOG_MSG("can't run code in this page");
|
||||
return CPU_Core_Normal_Run();
|
||||
}
|
||||
Bitu phys_page=ip_page;
|
||||
if (!PAGING_MakePhysPage(phys_page)) {
|
||||
LOG_MSG("Can't find physpage");
|
||||
return CPU_Core_Normal_Run();
|
||||
}
|
||||
chandler=new CodePageHandler(handler);
|
||||
MEM_SetPageHandler(phys_page,1,chandler); //Setup the handler
|
||||
PAGING_UnlinkPages(ip_page,1);
|
||||
goto findblock;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CPU_Core_Dyn_X86_Init(void) {
|
||||
Bits i;
|
||||
/* Setup the global registers and their flags */
|
||||
for (i=0;i<G_MAX;i++) DynRegs[i].genreg=0;
|
||||
DynRegs[G_EAX].data=®_eax;
|
||||
DynRegs[G_EAX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_ECX].data=®_ecx;
|
||||
DynRegs[G_ECX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_EDX].data=®_edx;
|
||||
DynRegs[G_EDX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_EBX].data=®_ebx;
|
||||
DynRegs[G_EBX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
|
||||
DynRegs[G_EBP].data=®_ebp;
|
||||
DynRegs[G_EBP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_ESP].data=®_esp;
|
||||
DynRegs[G_ESP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_EDI].data=®_edi;
|
||||
DynRegs[G_EDI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_ESI].data=®_esi;
|
||||
DynRegs[G_ESI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
|
||||
DynRegs[G_ES].data=&Segs.phys[es];
|
||||
DynRegs[G_ES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_CS].data=&Segs.phys[cs];
|
||||
DynRegs[G_CS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_SS].data=&Segs.phys[ss];
|
||||
DynRegs[G_SS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_DS].data=&Segs.phys[ds];
|
||||
DynRegs[G_DS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_FS].data=&Segs.phys[fs];
|
||||
DynRegs[G_FS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
DynRegs[G_GS].data=&Segs.phys[gs];
|
||||
DynRegs[G_GS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
|
||||
DynRegs[G_FLAGS].data=®_flags;
|
||||
DynRegs[G_FLAGS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
|
||||
DynRegs[G_SMASK].data=&cpu.stack.mask;
|
||||
DynRegs[G_SMASK].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
|
||||
DynRegs[G_EIP].data=®_eip;
|
||||
DynRegs[G_EIP].flags=DYNFLG_LOAD|DYNFLG_SAVE;
|
||||
|
||||
DynRegs[G_EA].data=&extra_regs.ea;
|
||||
DynRegs[G_EA].flags=0;
|
||||
DynRegs[G_STACK].data=&extra_regs.stack;
|
||||
DynRegs[G_STACK].flags=0;
|
||||
DynRegs[G_CYCLES].data=&CPU_Cycles;
|
||||
DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE;;
|
||||
DynRegs[G_TMPB].data=&extra_regs.tmpb;
|
||||
DynRegs[G_TMPB].flags=DYNFLG_HAS8|DYNFLG_HAS16;
|
||||
DynRegs[G_TMPW].data=&extra_regs.tmpd;
|
||||
DynRegs[G_TMPW].flags=DYNFLG_HAS16;
|
||||
DynRegs[G_SHIFT].data=&extra_regs.shift;
|
||||
DynRegs[G_SHIFT].flags=DYNFLG_HAS8|DYNFLG_HAS16;
|
||||
DynRegs[G_EXIT].data=0;
|
||||
DynRegs[G_EXIT].flags=DYNFLG_HAS16;
|
||||
/* Initialize code cache and dynamic blocks */
|
||||
cache_init();
|
||||
/* Init the generator */
|
||||
gen_init();
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
1
src/cpu/core_dyn_x86/Makefile.am
Normal file
1
src/cpu/core_dyn_x86/Makefile.am
Normal file
|
@ -0,0 +1 @@
|
|||
noinst_HEADERS = cache.h helpers.h decoder.h risc_x86.h string.h
|
300
src/cpu/core_dyn_x86/cache.h
Normal file
300
src/cpu/core_dyn_x86/cache.h
Normal file
|
@ -0,0 +1,300 @@
|
|||
struct CacheBlock {
|
||||
public:
|
||||
struct {
|
||||
Bit16s start,end; //Where the page is the original code
|
||||
Bitu first,last;
|
||||
} page;
|
||||
struct {
|
||||
Bit8u * start; //Where in the cache are we
|
||||
Bitu size;
|
||||
} cache;
|
||||
BlockType type;
|
||||
CodePageHandler * code_page; //Page containing this code
|
||||
CacheBlock * page_link; //For code crossing a page boundary
|
||||
struct {
|
||||
CacheBlock * next;
|
||||
} hash;
|
||||
CacheBlock * list_next;
|
||||
struct {
|
||||
CacheBlock * to[2];
|
||||
CacheBlock * from[DYN_LINKS];
|
||||
Bitu index;
|
||||
} link;
|
||||
CacheBlock * links[2];
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
CacheBlock * first;
|
||||
CacheBlock * active;
|
||||
CacheBlock * free;
|
||||
} block;
|
||||
Bit8u * pos;
|
||||
CacheBlock linkblocks[2];
|
||||
} cache;
|
||||
|
||||
static Bit8u cache_code_link_blocks[2][16];
|
||||
static Bit8u cache_code[CACHE_TOTAL+CACHE_MAXSIZE];
|
||||
static CacheBlock cache_blocks[CACHE_BLOCKS];
|
||||
|
||||
static void cache_resetblock(CacheBlock * block);
|
||||
|
||||
class CodePageHandler :public PageHandler {
|
||||
public:
|
||||
CodePageHandler(PageHandler * _old_pagehandler) {
|
||||
old_pagehandler=_old_pagehandler;
|
||||
flags=old_pagehandler->flags|PFLAG_HASCODE;
|
||||
flags&=~PFLAG_WRITEABLE;
|
||||
memset(&hash_map,0,sizeof(hash_map));
|
||||
memset(&write_map,0,sizeof(write_map));
|
||||
}
|
||||
void InvalidateRange(Bits start,Bits end) {
|
||||
Bits maps=start>>DYN_HASH_SHIFT;
|
||||
Bits map=maps;
|
||||
Bits count=write_map[maps];
|
||||
while (map>=0 && count>0) {
|
||||
CacheBlock * block=hash_map[map];
|
||||
CacheBlock * * where=&hash_map[map];
|
||||
while (block) {
|
||||
CacheBlock * nextblock=block->hash.next;
|
||||
if (start<=block->page.end && end>=block->page.start) {
|
||||
for (Bitu i=block->page.first;i<=block->page.last;i++) write_map[i]--;
|
||||
block->code_page=0; //Else resetblock will do double work
|
||||
count--;
|
||||
cache_resetblock(block);
|
||||
*where=nextblock;
|
||||
} else {
|
||||
where=&block->hash.next;
|
||||
}
|
||||
block=nextblock;
|
||||
}
|
||||
map--;
|
||||
}
|
||||
}
|
||||
void writeb(PhysPt addr,Bitu val){
|
||||
if (val!=host_readb(hostmem+(addr&4095))) {
|
||||
InvalidateRange(addr&4095,addr&4095);
|
||||
host_writeb(hostmem+(addr&4095),val);
|
||||
}
|
||||
}
|
||||
void writew(PhysPt addr,Bitu val){
|
||||
if (val!=host_readw(hostmem+(addr&4095))) {
|
||||
InvalidateRange(addr&4095,(addr&4095)+1);
|
||||
host_writew(hostmem+(addr&4095),val);
|
||||
}
|
||||
}
|
||||
void writed(PhysPt addr,Bitu val){
|
||||
if (val!=host_readd(hostmem+(addr&4095))) {
|
||||
InvalidateRange(addr&4095,(addr&4095)+3);
|
||||
host_writed(hostmem+(addr&4095),val);
|
||||
}
|
||||
}
|
||||
void AddCacheBlock(CacheBlock * block) {
|
||||
Bit16u first,last;
|
||||
if (block->page.start<0) first=0;
|
||||
else first=block->page.start>>DYN_HASH_SHIFT;
|
||||
block->hash.next=hash_map[first];
|
||||
hash_map[first]=block;
|
||||
if (block->page.end>=4096) last=DYN_PAGE_HASH-1;
|
||||
else last=block->page.end>>DYN_HASH_SHIFT;
|
||||
block->page.first=first;
|
||||
block->page.last=last;
|
||||
for (;first<=last;first++) {
|
||||
write_map[first]++;
|
||||
}
|
||||
block->code_page=this;
|
||||
}
|
||||
void DelCacheBlock(CacheBlock * block) {
|
||||
CacheBlock * * where=&hash_map[block->page.first];
|
||||
while (*where) {
|
||||
if (*where==block) {
|
||||
*where=block->hash.next;
|
||||
break;
|
||||
}
|
||||
where=&((*where)->hash.next);
|
||||
}
|
||||
for (Bitu i=block->page.first;i<=block->page.last;i++) {
|
||||
write_map[i]--;
|
||||
}
|
||||
}
|
||||
CacheBlock * FindCacheBlock(Bitu start) {
|
||||
CacheBlock * block=hash_map[start>>DYN_HASH_SHIFT];
|
||||
while (block) {
|
||||
if (block->page.start==start) return block;
|
||||
block=block->hash.next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
HostPt GetHostPt(Bitu phys_page) {
|
||||
hostmem=old_pagehandler->GetHostPt(phys_page);
|
||||
return hostmem;
|
||||
}
|
||||
private:
|
||||
PageHandler * old_pagehandler;
|
||||
CacheBlock * hash_map[DYN_PAGE_HASH];
|
||||
Bit8u write_map[DYN_PAGE_HASH];
|
||||
HostPt hostmem;
|
||||
};
|
||||
|
||||
|
||||
static INLINE void cache_addunsedblock(CacheBlock * block) {
|
||||
block->list_next=cache.block.free;
|
||||
cache.block.free=block;
|
||||
}
|
||||
|
||||
static CacheBlock * cache_getblock(void) {
|
||||
CacheBlock * ret=cache.block.free;
|
||||
if (!ret) E_Exit("Ran out of CacheBlocks" );
|
||||
cache.block.free=ret->list_next;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static INLINE void cache_clearlinkfrom(CacheBlock * block,CacheBlock * from) {
|
||||
for (Bitu i=0;i<DYN_LINKS;i++) {
|
||||
if (block->link.from[i]==from) block->link.from[i]=0;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE void cache_clearlinkto(CacheBlock * block,CacheBlock * to) {
|
||||
if (block->link.to[0]==to) block->link.to[0]=&cache.linkblocks[0];
|
||||
if (block->link.to[1]==to) block->link.to[1]=&cache.linkblocks[1];
|
||||
}
|
||||
|
||||
static void cache_linkblocks(CacheBlock * from,CacheBlock * to,Bitu link) {
|
||||
from->link.to[link]=to;
|
||||
CacheBlock * clear=to->link.from[to->link.index];
|
||||
if (clear) {
|
||||
DYN_LOG("backlink buffer full");
|
||||
cache_clearlinkto(to->link.from[to->link.index],to);
|
||||
}
|
||||
to->link.from[to->link.index]=from;
|
||||
to->link.index++;
|
||||
if (to->link.index>=DYN_LINKS) to->link.index=0;
|
||||
}
|
||||
|
||||
static void cache_resetblock(CacheBlock * block) {
|
||||
Bits i;
|
||||
DYN_LOG("Resetted block");
|
||||
block->type=BT_Free;
|
||||
/* Clear all links to this block from other blocks */
|
||||
for (i=0;i<DYN_LINKS;i++) {
|
||||
if (block->link.from[i]) cache_clearlinkto(block->link.from[i],block);
|
||||
block->link.from[i]=0;
|
||||
}
|
||||
/* Clear all links from this block to other blocks */
|
||||
if (block->link.to[0]!=&cache.linkblocks[0]) {
|
||||
cache_clearlinkfrom(block->link.to[0],block);
|
||||
block->link.to[0]=&cache.linkblocks[0];
|
||||
}
|
||||
if (block->link.to[1]!=&cache.linkblocks[1]) {
|
||||
cache_clearlinkfrom(block->link.to[1],block);
|
||||
block->link.to[1]=&cache.linkblocks[1];
|
||||
}
|
||||
block->link.index=0;
|
||||
if (block->code_page) block->code_page->DelCacheBlock(block);
|
||||
}
|
||||
|
||||
static CacheBlock * cache_openblock(void) {
|
||||
CacheBlock * block=cache.block.active;
|
||||
/* check for enough space in this block */
|
||||
Bitu size=block->cache.size;
|
||||
CacheBlock * nextblock=block->list_next;
|
||||
while (size<CACHE_MAXSIZE) {
|
||||
if (!nextblock) goto skipresize;
|
||||
size+=nextblock->cache.size;
|
||||
CacheBlock * tempblock=nextblock->list_next;
|
||||
if (nextblock->type!=BT_Free) cache_resetblock(nextblock);
|
||||
cache_addunsedblock(nextblock);
|
||||
nextblock=tempblock;
|
||||
}
|
||||
skipresize:
|
||||
block->cache.size=size;
|
||||
block->list_next=nextblock;
|
||||
cache.pos=block->cache.start;
|
||||
return block;
|
||||
}
|
||||
|
||||
static void cache_closeblock(BlockType type) {
|
||||
CacheBlock * block=cache.block.active;
|
||||
/* Setup some structures in the block determined by type */
|
||||
block->type=type;
|
||||
switch (type) {
|
||||
case BT_Normal:
|
||||
break;
|
||||
case BT_SingleLink:
|
||||
block->link.to[0]=&cache.linkblocks[0];
|
||||
break;
|
||||
case BT_DualLink:
|
||||
block->link.to[0]=&cache.linkblocks[0];
|
||||
block->link.to[1]=&cache.linkblocks[1];
|
||||
break;
|
||||
}
|
||||
/* Close the block with correct alignments */
|
||||
Bitu written=cache.pos-block->cache.start;
|
||||
if (written>block->cache.size) {
|
||||
if (!block->list_next) {
|
||||
if (written>block->cache.size+CACHE_MAXSIZE) E_Exit("CacheBlock overrun");
|
||||
} else E_Exit("CacheBlock overrun");
|
||||
} else {
|
||||
Bitu new_size;
|
||||
Bitu left=block->cache.size-written;
|
||||
/* Smaller than cache align then don't bother to resize */
|
||||
if (left>CACHE_ALIGN) {
|
||||
new_size=((written-1)|(CACHE_ALIGN-1))+1;
|
||||
} else new_size=block->cache.size;
|
||||
CacheBlock * newblock=cache_getblock();
|
||||
newblock->cache.start=block->cache.start+new_size;
|
||||
newblock->cache.size=block->cache.size-new_size;
|
||||
newblock->list_next=block->list_next;
|
||||
newblock->type=BT_Free;
|
||||
block->cache.size=new_size;
|
||||
block->list_next=newblock;
|
||||
}
|
||||
/* Advance the active block pointer */
|
||||
if (!block->list_next) {
|
||||
DYN_LOG("Cache full restarting");
|
||||
cache.block.active=cache.block.first;
|
||||
} else {
|
||||
cache.block.active=block->list_next;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE void cache_addb(Bit8u val) {
|
||||
*cache.pos++=val;
|
||||
}
|
||||
|
||||
static INLINE void cache_addw(Bit16u val) {
|
||||
*(Bit16u*)cache.pos=val;
|
||||
cache.pos+=2;
|
||||
}
|
||||
|
||||
static INLINE void cache_addd(Bit32u val) {
|
||||
*(Bit32u*)cache.pos=val;
|
||||
cache.pos+=4;
|
||||
}
|
||||
|
||||
|
||||
static void gen_return(BlockReturn retcode);
|
||||
|
||||
static void cache_init(void) {
|
||||
Bits i;
|
||||
memset(&cache_blocks,0,sizeof(cache_blocks));
|
||||
cache.block.free=&cache_blocks[0];
|
||||
for (i=0;i<CACHE_BLOCKS-1;i++) {
|
||||
cache_blocks[i].list_next=&cache_blocks[i+1];
|
||||
cache_blocks[i].link.to[0]=&cache.linkblocks[0];
|
||||
cache_blocks[i].link.to[1]=&cache.linkblocks[1];
|
||||
}
|
||||
CacheBlock * block=cache_getblock();
|
||||
cache.block.first=block;
|
||||
cache.block.active=block;
|
||||
block->cache.start=&cache_code[0];
|
||||
block->cache.size=CACHE_TOTAL;
|
||||
block->list_next=0; //Last block in the list
|
||||
cache.pos=&cache_code_link_blocks[0][0];
|
||||
cache.linkblocks[0].cache.start=cache.pos;
|
||||
gen_return(BR_Link1);
|
||||
cache.pos=&cache_code_link_blocks[1][0];
|
||||
cache.linkblocks[1].cache.start=cache.pos;
|
||||
gen_return(BR_Link2);
|
||||
}
|
1252
src/cpu/core_dyn_x86/decoder.h
Normal file
1252
src/cpu/core_dyn_x86/decoder.h
Normal file
File diff suppressed because it is too large
Load diff
57
src/cpu/core_dyn_x86/helpers.h
Normal file
57
src/cpu/core_dyn_x86/helpers.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
static Bitu dyn_helper_divb(Bit8u val) {
|
||||
if (!val) return 1;
|
||||
Bitu quo=reg_ax / val;
|
||||
reg_ah=(Bit8u)(reg_ax % val);
|
||||
reg_al=(Bit8u)quo;
|
||||
if (quo>0xff) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bitu dyn_helper_idivb(Bit8s val) {
|
||||
if (!val) return 1;
|
||||
Bits quo=(Bit16s)reg_ax / val;
|
||||
reg_ah=(Bit8s)((Bit16s)reg_ax % val);
|
||||
reg_al=(Bit8s)quo;
|
||||
if (quo!=(Bit8s)reg_al) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bitu dyn_helper_divw(Bit16u val) {
|
||||
if (!val) return 1;
|
||||
Bitu num=(reg_dx<<16)|reg_ax;
|
||||
Bitu quo=num/val;
|
||||
reg_dx=(Bit16u)(num % val);
|
||||
reg_ax=(Bit16u)quo;
|
||||
if (quo!=reg_ax) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bitu dyn_helper_idivw(Bit16s val) {
|
||||
if (!val) return 1;
|
||||
Bits num=(reg_dx<<16)|reg_ax;
|
||||
Bits quo=num/val;
|
||||
reg_dx=(Bit16s)(num % val);
|
||||
reg_ax=(Bit16s)quo;
|
||||
if (quo!=(Bit16s)reg_ax) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bitu dyn_helper_divd(Bit32u val) {
|
||||
if (!val) return 1;
|
||||
Bit64u num=(((Bit64u)reg_edx)<<32)|reg_eax;
|
||||
Bit64u quo=num/val;
|
||||
reg_edx=(Bit32u)(num % val);
|
||||
reg_eax=(Bit32u)quo;
|
||||
if (quo!=(Bit64u)reg_eax) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bitu dyn_helper_idivd(Bit32s val) {
|
||||
if (!val) return 1;
|
||||
Bit64s num=(((Bit64u)reg_edx)<<32)|reg_eax;
|
||||
Bit64s quo=num/val;
|
||||
reg_edx=(Bit32s)(num % val);
|
||||
reg_eax=(Bit32s)(quo);
|
||||
if (quo!=(Bit64s)((Bit32s)reg_eax)) return 1;
|
||||
return 0;
|
||||
}
|
719
src/cpu/core_dyn_x86/risc_x86.h
Normal file
719
src/cpu/core_dyn_x86/risc_x86.h
Normal file
|
@ -0,0 +1,719 @@
|
|||
#define GEN_HAS_IMM 1
|
||||
|
||||
static void gen_init(void);
|
||||
|
||||
/* End of needed */
|
||||
|
||||
#define X86_REGS 7
|
||||
#define X86_REG_EAX 0x00
|
||||
#define X86_REG_ECX 0x01
|
||||
#define X86_REG_EDX 0x02
|
||||
#define X86_REG_EBX 0x03
|
||||
#define X86_REG_EBP 0x04
|
||||
#define X86_REG_ESI 0x05
|
||||
#define X86_REG_EDI 0x06
|
||||
|
||||
#define X86_REG_MASK(_REG_) (1 << X86_REG_ ## _REG_)
|
||||
|
||||
static struct {
|
||||
Bitu last_used;
|
||||
Bitu stored_flags;
|
||||
GenReg * regs[X86_REGS];
|
||||
} x86gen;
|
||||
|
||||
class GenReg {
|
||||
public:
|
||||
GenReg(Bit8u _index,bool _protect) {
|
||||
index=_index;protect=_protect;
|
||||
notusable=false;dynreg=0;
|
||||
}
|
||||
DynReg * dynreg;
|
||||
Bitu last_used; //Keeps track of last assigned regs
|
||||
Bit8u index;
|
||||
bool notusable;
|
||||
bool protect;
|
||||
void Load(DynReg * _dynreg) {
|
||||
if (!_dynreg) return;
|
||||
if (dynreg) Clear();
|
||||
dynreg=_dynreg;
|
||||
last_used=x86gen.last_used;
|
||||
dynreg->flags&=~DYNFLG_CHANGED;
|
||||
dynreg->genreg=this;
|
||||
if (dynreg->flags & (DYNFLG_LOAD|DYNFLG_LOADONCE)) {
|
||||
dynreg->flags&=~DYNFLG_LOADONCE;
|
||||
cache_addw(0x058b+(index << (8+3))); //Mov reg,[data]
|
||||
cache_addd((Bit32u)dynreg->data);
|
||||
}
|
||||
}
|
||||
void Save(void) {
|
||||
if (!dynreg) IllegalOption();
|
||||
dynreg->flags&=~DYNFLG_CHANGED;
|
||||
cache_addw(0x0589+(index << (8+3))); //Mov [data],reg
|
||||
cache_addd((Bit32u)dynreg->data);
|
||||
}
|
||||
void Release(void) {
|
||||
if (!dynreg) return;
|
||||
if (dynreg->flags&DYNFLG_CHANGED && dynreg->flags&DYNFLG_SAVE) {
|
||||
Save();
|
||||
}
|
||||
dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_LOADONCE);
|
||||
dynreg->genreg=0;dynreg=0;
|
||||
}
|
||||
void Clear(void) {
|
||||
if (!dynreg) return;
|
||||
if (dynreg->flags&DYNFLG_CHANGED) {
|
||||
dynreg->flags|=DYNFLG_LOADONCE;
|
||||
Save();
|
||||
}
|
||||
dynreg->genreg=0;dynreg=0;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
static Bit32u kut=10;
|
||||
static BlockReturn gen_runcode(Bit8u * code) {
|
||||
BlockReturn retval;
|
||||
#define NEW_MASK $FMASK_TEST
|
||||
__asm__ volatile (
|
||||
|
||||
"pushfl \n"
|
||||
"movl %1,%%ebx \n"
|
||||
"andl %2,%%ebx \n"
|
||||
"popl %%ecx \n"
|
||||
"andl %3,%%ecx \n"
|
||||
"orl %%ebx,%%ecx \n"
|
||||
"pushl %%ecx \n"
|
||||
"popfl \n"
|
||||
"call %4 \n"
|
||||
"pushfl \n"
|
||||
"movl %1,%%ebx \n"
|
||||
"andl %3,%%ebx \n"
|
||||
"popl %%ecx \n"
|
||||
"andl %2,%%ecx \n"
|
||||
"orl %%ecx,%%ebx \n"
|
||||
"movl %%ebx,%1 \n"
|
||||
:"=a" (retval)
|
||||
:"m" (reg_flags), "n" (FMASK_TEST),"n" (~FMASK_TEST),"m" (code)
|
||||
:"%ecx","%edx","%ebx","%ebp","%edi","%esi","cc","memory"
|
||||
);
|
||||
#if 0
|
||||
/* Prepare the flags */
|
||||
pushfd
|
||||
mov ebx,[reg_flags]
|
||||
and ebx,FMASK_TEST
|
||||
pop ecx
|
||||
and ecx,~FMASK_TEST
|
||||
or ecx,ebx
|
||||
push ecx
|
||||
popfd
|
||||
call dword ptr [code];
|
||||
/* Restore the flags */
|
||||
pushfd
|
||||
mov ebx,[reg_flags]
|
||||
and ebx,~FMASK_TEST
|
||||
pop ecx
|
||||
and ecx,FMASK_TEST
|
||||
or ebx,ecx
|
||||
mov [reg_flags],ebx
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebp
|
||||
pop ebx
|
||||
mov [retval],eax
|
||||
}
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static GenReg * FindDynReg(DynReg * dynreg) {
|
||||
x86gen.last_used++;
|
||||
if (dynreg->genreg) {
|
||||
dynreg->genreg->last_used=x86gen.last_used;
|
||||
return dynreg->genreg;
|
||||
}
|
||||
/* Find best match for selected global reg */
|
||||
Bits i;
|
||||
Bits first_used,first_index;
|
||||
first_used=-1;
|
||||
if (dynreg->flags & DYNFLG_HAS8) {
|
||||
/* Has to be eax,ebx,ecx,edx */
|
||||
for (i=first_index=0;i<=X86_REG_EDX;i++) {
|
||||
GenReg * genreg=x86gen.regs[i];
|
||||
if (genreg->notusable) continue;
|
||||
if (!(genreg->dynreg)) {
|
||||
genreg->Load(dynreg);
|
||||
return genreg;
|
||||
}
|
||||
if (genreg->last_used<first_used) {
|
||||
first_used=genreg->last_used;
|
||||
first_index=i;
|
||||
}
|
||||
}
|
||||
/* No free register found use earliest assigned one */
|
||||
GenReg * newreg=x86gen.regs[first_index];
|
||||
newreg->Load(dynreg);
|
||||
return newreg;
|
||||
} else {
|
||||
for (i=first_index=X86_REGS-1;i>=0;i--) {
|
||||
GenReg * genreg=x86gen.regs[i];
|
||||
if (genreg->notusable) continue;
|
||||
if (!(genreg->dynreg)) {
|
||||
genreg->Load(dynreg);
|
||||
return genreg;
|
||||
}
|
||||
if (genreg->last_used<first_used) {
|
||||
first_used=genreg->last_used;
|
||||
first_index=i;
|
||||
}
|
||||
}
|
||||
/* No free register found use earliest assigned one */
|
||||
GenReg * newreg=x86gen.regs[first_index];
|
||||
newreg->Load(dynreg);
|
||||
return newreg;
|
||||
}
|
||||
}
|
||||
|
||||
static GenReg * ForceDynReg(GenReg * genreg,DynReg * dynreg) {
|
||||
genreg->last_used=++x86gen.last_used;
|
||||
if (dynreg->genreg==genreg) return genreg;
|
||||
if (genreg->dynreg) genreg->Clear();
|
||||
if (dynreg->genreg) dynreg->genreg->Clear();
|
||||
genreg->Load(dynreg);
|
||||
return genreg;
|
||||
}
|
||||
static void gen_releasereg(DynReg * dynreg) {
|
||||
GenReg * genreg=dynreg->genreg;
|
||||
if (genreg) genreg->Release();
|
||||
else dynreg->flags&=~(DYNFLG_LOADONCE|DYNFLG_CHANGED);
|
||||
}
|
||||
|
||||
static void gen_setupreg(DynReg * dnew,DynReg * dsetup) {
|
||||
dnew->flags=dsetup->flags;
|
||||
if (dnew->genreg==dsetup->genreg) return;
|
||||
/* Not the same genreg must be wrong */
|
||||
if (dnew->genreg) {
|
||||
/* Check if the genreg i'm changing is actually linked to me */
|
||||
if (dnew->genreg->dynreg==dnew) dnew->genreg->dynreg=0;
|
||||
}
|
||||
dnew->genreg=dsetup->genreg;
|
||||
if (dnew->genreg) dnew->genreg->dynreg=dnew;
|
||||
}
|
||||
|
||||
static void gen_synchreg(DynReg * dnew,DynReg * dsynch) {
|
||||
/* First make sure the registers match */
|
||||
if (dnew->genreg!=dsynch->genreg) {
|
||||
if (dnew->genreg) dnew->genreg->Clear();
|
||||
if (dsynch->genreg) {
|
||||
dsynch->genreg->Load(dnew);
|
||||
}
|
||||
}
|
||||
/* Always use the loadonce flag from either state */
|
||||
dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_LOADONCE);
|
||||
if ((dnew->flags ^ dsynch->flags) & DYNFLG_CHANGED) {
|
||||
/* Ensure the changed value gets saved */
|
||||
if (dnew->flags & DYNFLG_CHANGED) {
|
||||
dnew->genreg->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_storeflags(void) {
|
||||
if (!x86gen.stored_flags) {
|
||||
cache_addb(0x9c); //PUSHFD
|
||||
}
|
||||
x86gen.stored_flags++;
|
||||
}
|
||||
|
||||
static void gen_restoreflags(bool noreduce=false) {
|
||||
if (noreduce) {
|
||||
cache_addb(0x9d);
|
||||
return;
|
||||
}
|
||||
if (x86gen.stored_flags) {
|
||||
x86gen.stored_flags--;
|
||||
if (!x86gen.stored_flags)
|
||||
cache_addb(0x9d); //POPFD
|
||||
} else IllegalOption();
|
||||
}
|
||||
|
||||
static void gen_reinit(void) {
|
||||
x86gen.last_used=0;
|
||||
x86gen.stored_flags=0;
|
||||
for (Bitu i=0;i<X86_REGS;i++) {
|
||||
x86gen.regs[i]->dynreg=0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_dop_byte(DualOps op,DynReg * dr1,Bit8u di1,DynReg * dr2,Bit8u di2) {
|
||||
GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
|
||||
switch (op) {
|
||||
case DOP_ADD:cache_addb(0x02);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_OR: cache_addb(0x0a);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_ADC:cache_addb(0x12);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_SBB:cache_addb(0x1a);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_AND:cache_addb(0x22);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_SUB:cache_addb(0x2a);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_XOR:cache_addb(0x32);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_CMP:cache_addb(0x3a);break;
|
||||
case DOP_MOV:cache_addb(0x8a);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_XCHG:cache_addb(0x86);dr1->flags|=DYNFLG_CHANGED;dr2->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_TEST:cache_addb(0x84);break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
cache_addb(0xc0+((gr1->index+di1)<<3)+gr2->index+di2);
|
||||
}
|
||||
|
||||
static void gen_dop_byte_imm(DualOps op,DynReg * dr1,Bit8u di1,Bitu imm) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
switch (op) {
|
||||
case DOP_ADD:
|
||||
cache_addw(0xc080+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_OR:
|
||||
cache_addw(0xc880+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_ADC:
|
||||
cache_addw(0xd080+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_SBB:
|
||||
cache_addw(0xd880+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_AND:
|
||||
cache_addw(0xe080+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_SUB:
|
||||
cache_addw(0xe880+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_XOR:
|
||||
cache_addw(0xf080+((gr1->index+di1)<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_CMP:
|
||||
cache_addw(0xf880+((gr1->index+di1)<<8));
|
||||
break;//Doesn't change
|
||||
case DOP_MOV:
|
||||
cache_addb(0xb0+gr1->index+di1);
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
break;
|
||||
case DOP_TEST:
|
||||
cache_addw(0xc0f6+((gr1->index+di1)<<8));
|
||||
break;//Doesn't change
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
cache_addb(imm);
|
||||
}
|
||||
|
||||
static void gen_sop_byte(SingleOps op,DynReg * dr1,Bit8u di1) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
switch (op) {
|
||||
case SOP_INC:cache_addw(0xc0FE + ((gr1->index+di1)<<8));break;
|
||||
case SOP_DEC:cache_addw(0xc8FE + ((gr1->index+di1)<<8));break;
|
||||
case SOP_NOT:cache_addw(0xd0f6 + ((gr1->index+di1)<<8));break;
|
||||
case SOP_NEG:cache_addw(0xd8f6 + ((gr1->index+di1)<<8));break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
|
||||
static void gen_extend_word(bool sign,DynReg * ddr,DynReg * dsr) {
|
||||
GenReg * gdr=FindDynReg(ddr);GenReg * gsr=FindDynReg(dsr);
|
||||
if (sign) cache_addw(0xbf0f);
|
||||
else cache_addw(0xb70f);
|
||||
cache_addb(0xc0+(gdr->index<<3)+(gsr->index));
|
||||
ddr->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_extend_byte(bool sign,bool dword,DynReg * ddr,DynReg * dsr,Bit8u dsi) {
|
||||
GenReg * gdr=FindDynReg(ddr);GenReg * gsr=FindDynReg(dsr);
|
||||
if (!dword) cache_addb(0x66);
|
||||
if (sign) cache_addw(0xbe0f);
|
||||
else cache_addw(0xb60f);
|
||||
cache_addb(0xc0+(gdr->index<<3)+(gsr->index+dsi));
|
||||
ddr->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_lea(DynReg * ddr,DynReg * dsr1,DynReg * dsr2,Bitu scale,Bits imm) {
|
||||
GenReg * gdr=FindDynReg(ddr);
|
||||
Bitu imm_size;
|
||||
Bit8u rm_base=(gdr->index << 3);
|
||||
if (dsr1) {
|
||||
GenReg * gsr1=FindDynReg(dsr1);
|
||||
if (!imm && (gsr1->index!=0x5)) {
|
||||
imm_size=0; rm_base+=0x0; //no imm
|
||||
} else if ((imm>=-128 && imm<=127)) {
|
||||
imm_size=1;rm_base+=0x40; //Signed byte imm
|
||||
} else {
|
||||
imm_size=4;rm_base+=0x80; //Signed dword imm
|
||||
}
|
||||
if (dsr2) {
|
||||
GenReg * gsr2=FindDynReg(dsr2);
|
||||
cache_addb(0x8d); //LEA
|
||||
cache_addb(rm_base+0x4); //The sib indicator
|
||||
Bit8u sib=(gsr1->index)+(gsr2->index<<3)+(scale<<6);
|
||||
cache_addb(sib);
|
||||
} else {
|
||||
cache_addb(0x8d); //LEA
|
||||
cache_addb(rm_base+gsr1->index);
|
||||
}
|
||||
} else {
|
||||
if (dsr2) {
|
||||
GenReg * gsr2=FindDynReg(dsr2);
|
||||
cache_addb(0x8d); //LEA
|
||||
cache_addb(rm_base+0x4); //The sib indicator
|
||||
Bit8u sib=(5+(gsr2->index<<3)+(scale<<6));
|
||||
cache_addb(sib);
|
||||
imm_size=4;
|
||||
} else {
|
||||
cache_addb(0x8d); //LEA
|
||||
cache_addb(rm_base+0x05); //dword imm
|
||||
imm_size=4;
|
||||
}
|
||||
}
|
||||
switch (imm_size) {
|
||||
case 0: break;
|
||||
case 1:cache_addb(imm);break;
|
||||
case 4:cache_addd(imm);break;
|
||||
}
|
||||
ddr->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_dop_word(DualOps op,bool dword,DynReg * dr1,DynReg * dr2) {
|
||||
GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
|
||||
if (!dword) cache_addb(0x66);
|
||||
switch (op) {
|
||||
case DOP_ADD:cache_addb(0x03);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_OR: cache_addb(0x0b);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_ADC:cache_addb(0x13);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_SBB:cache_addb(0x1b);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_AND:cache_addb(0x23);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_SUB:cache_addb(0x2b);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_XOR:cache_addb(0x33);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_CMP:cache_addb(0x3b);break;
|
||||
case DOP_MOV:cache_addb(0x8b);dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_XCHG:cache_addb(0x87);dr1->flags|=DYNFLG_CHANGED;dr2->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_TEST:cache_addb(0x85);break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
cache_addb(0xc0+(gr1->index<<3)+gr2->index);
|
||||
}
|
||||
|
||||
static void gen_dop_word_imm(DualOps op,bool dword,DynReg * dr1,Bits imm) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
if (!dword) cache_addb(0x66);
|
||||
switch (op) {
|
||||
case DOP_ADD:cache_addw(0xc081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_OR: cache_addw(0xc881+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_ADC:cache_addw(0xd081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_SBB:cache_addw(0xd881+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_AND:cache_addw(0xe081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_SUB:cache_addw(0xe881+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_XOR:cache_addw(0xf081+(gr1->index<<8));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_CMP:cache_addw(0xf881+(gr1->index<<8));break;//Doesn't change
|
||||
case DOP_MOV:cache_addb(0xb8+(gr1->index));dr1->flags|=DYNFLG_CHANGED;break;
|
||||
case DOP_TEST:cache_addw(0xc0f7+(gr1->index<<8));break;//Doesn't change
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
if (dword) cache_addd(imm);
|
||||
else cache_addw(imm);
|
||||
}
|
||||
static void gen_sop_word(SingleOps op,bool dword,DynReg * dr1) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
if (!dword) cache_addb(0x66);
|
||||
switch (op) {
|
||||
case SOP_INC:cache_addb(0x40+gr1->index);break;
|
||||
case SOP_DEC:cache_addb(0x48+gr1->index);break;
|
||||
case SOP_NOT:cache_addw(0xd0f7+(gr1->index<<8));break;
|
||||
case SOP_NEG:cache_addw(0xd8f7+(gr1->index<<8));break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_shift_byte(ShiftOps op,DynReg * drecx,DynReg * dr1,Bit8u di1) {
|
||||
ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
switch (op) {
|
||||
case SHIFT_ROL:cache_addw(0xc0d2+((gr1->index+di1)<<8));break;
|
||||
case SHIFT_ROR:cache_addw(0xc8d2+((gr1->index+di1)<<8));break;
|
||||
case SHIFT_RCL:cache_addw(0xd0d2+((gr1->index+di1)<<8));break;
|
||||
case SHIFT_RCR:cache_addw(0xd8d2+((gr1->index+di1)<<8));break;
|
||||
case SHIFT_SHL:cache_addw(0xe0d2+((gr1->index+di1)<<8));break;
|
||||
case SHIFT_SHR:cache_addw(0xe8d2+((gr1->index+di1)<<8));break;
|
||||
case SHIFT_SAR:cache_addw(0xf8d2+((gr1->index+di1)<<8));break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_shift_word(ShiftOps op,DynReg * drecx,bool dword,DynReg * dr1) {
|
||||
ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
if (!dword) cache_addb(0x66);
|
||||
switch (op) {
|
||||
case SHIFT_ROL:cache_addw(0xc0d3+((gr1->index)<<8));break;
|
||||
case SHIFT_ROR:cache_addw(0xc8d3+((gr1->index)<<8));break;
|
||||
case SHIFT_RCL:cache_addw(0xd0d3+((gr1->index)<<8));break;
|
||||
case SHIFT_RCR:cache_addw(0xd8d3+((gr1->index)<<8));break;
|
||||
case SHIFT_SHL:cache_addw(0xe0d3+((gr1->index)<<8));break;
|
||||
case SHIFT_SHR:cache_addw(0xe8d3+((gr1->index)<<8));break;
|
||||
case SHIFT_SAR:cache_addw(0xf8d3+((gr1->index)<<8));break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
|
||||
static void gen_mul_byte(bool imul,DynReg * dyn_ax,DynReg * dr1,Bit8u di1) {
|
||||
ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
if (imul) cache_addw(0xe8f6+((gr1->index+di1)<<8));
|
||||
else cache_addw(0xe0f6+((gr1->index+di1)<<8));
|
||||
dyn_ax->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_mul_word(bool imul,DynReg * dyn_ax,DynReg * dyn_dx,bool dword,DynReg * dr1) {
|
||||
ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
|
||||
ForceDynReg(x86gen.regs[X86_REG_EDX],dyn_dx);
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
if (!dword) cache_addb(0x66);
|
||||
if (imul) cache_addw(0xe8f7+(gr1->index<<8));
|
||||
else cache_addw(0xe0f7+(gr1->index<<8));
|
||||
dyn_ax->flags|=DYNFLG_CHANGED;
|
||||
dyn_dx->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_dshift_imm(bool dword,bool left,DynReg * dr1,DynReg * dr2,Bitu imm) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
GenReg * gr2=FindDynReg(dr2);
|
||||
if (!dword) cache_addb(0x66);
|
||||
if (left) cache_addw(0xa40f); //SHLD IMM
|
||||
else cache_addw(0xac0f); //SHRD IMM
|
||||
cache_addb(0xc0+gr1->index+(gr2->index<<8));
|
||||
cache_addb(imm);
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_dshift_cl(bool dword,bool left,DynReg * dr1,DynReg * dr2,DynReg * drecx) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
GenReg * gr2=FindDynReg(dr2);
|
||||
ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
|
||||
if (!dword) cache_addb(0x66);
|
||||
if (left) cache_addw(0xa50f); //SHLD CL
|
||||
else cache_addw(0xad0f); //SHRD CL
|
||||
cache_addb(0xc0+gr1->index+(gr2->index<<8));
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_call_function(void * func,char * ops,...) {
|
||||
Bits paramcount=0;
|
||||
struct ParamInfo {
|
||||
char * line;
|
||||
Bitu value;
|
||||
} pinfo[32];
|
||||
ParamInfo * retparam=0;
|
||||
/* Clear the EAX Genreg for usage */
|
||||
x86gen.regs[X86_REG_EAX]->Clear();
|
||||
x86gen.regs[X86_REG_EAX]->notusable=true;;
|
||||
/* Save the flags */
|
||||
gen_storeflags();
|
||||
/* Scan for the amount of params */
|
||||
if (ops) {
|
||||
va_list params;
|
||||
va_start(params,ops);
|
||||
Bits pindex=0;
|
||||
while (*ops) {
|
||||
if (*ops=='%') {
|
||||
pinfo[pindex].line=ops+1;
|
||||
pinfo[pindex].value=va_arg(params,Bitu);
|
||||
pindex++;
|
||||
}
|
||||
ops++;
|
||||
}
|
||||
paramcount=0;
|
||||
while (pindex) {
|
||||
pindex--;
|
||||
char * scan=pinfo[pindex].line;
|
||||
switch (*scan++) {
|
||||
case 'I': /* immediate value */
|
||||
paramcount++;
|
||||
cache_addb(0x68); //Push immediate
|
||||
cache_addd(pinfo[pindex].value); //Push value
|
||||
break;
|
||||
case 'D': /* Dynamic register */
|
||||
{
|
||||
bool release=false;
|
||||
paramcount++;
|
||||
DynReg * dynreg=(DynReg *)pinfo[pindex].value;
|
||||
GenReg * genreg=FindDynReg(dynreg);
|
||||
scanagain:
|
||||
switch (*scan++) {
|
||||
case 'd':
|
||||
cache_addb(0x50+genreg->index); //Push reg
|
||||
break;
|
||||
case 'w':
|
||||
cache_addw(0xb70f); //MOVZX EAX,reg
|
||||
cache_addb(0xc0+genreg->index);
|
||||
cache_addb(0x50); //Push EAX
|
||||
break;
|
||||
case 'l':
|
||||
cache_addw(0xb60f); //MOVZX EAX,reg[0]
|
||||
cache_addb(0xc0+genreg->index);
|
||||
cache_addb(0x50); //Push EAX
|
||||
break;
|
||||
case 'h':
|
||||
cache_addw(0xb60f); //MOVZX EAX,reg[1]
|
||||
cache_addb(0xc4+genreg->index);
|
||||
cache_addb(0x50); //Push EAX
|
||||
break;
|
||||
case 'r': /* release the reg afterwards */
|
||||
release=true;
|
||||
goto scanagain;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
if (release) gen_releasereg(dynreg);
|
||||
}
|
||||
break;
|
||||
case 'R': /* Dynamic register to get the return value */
|
||||
retparam =&pinfo[pindex];
|
||||
pinfo[pindex].line=scan;
|
||||
break;
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Clear some unprotected registers */
|
||||
x86gen.regs[X86_REG_ECX]->Clear();
|
||||
x86gen.regs[X86_REG_EDX]->Clear();
|
||||
/* Do the actual call to the procedure */
|
||||
cache_addb(0xe8);
|
||||
cache_addd((Bit32u)func - (Bit32u)cache.pos-4);
|
||||
/* Restore the params of the stack */
|
||||
if (paramcount) {
|
||||
cache_addw(0xc483); //add ESP,imm byte
|
||||
cache_addb(paramcount*4);
|
||||
}
|
||||
/* Save the return value in correct register */
|
||||
if (retparam) {
|
||||
DynReg * dynreg=(DynReg *)retparam->value;
|
||||
GenReg * genreg=FindDynReg(dynreg);
|
||||
switch (*retparam->line) {
|
||||
case 'd':
|
||||
cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax
|
||||
break;
|
||||
case 'w':
|
||||
cache_addb(0x66);
|
||||
cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax
|
||||
break;
|
||||
case 'l':
|
||||
cache_addw(0xc08a+(genreg->index <<(8+3))); //mov reg,eax
|
||||
break;
|
||||
case 'h':
|
||||
cache_addw(0xc08a+((genreg->index+4) <<(8+3))); //mov reg,eax
|
||||
break;
|
||||
}
|
||||
dynreg->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
gen_restoreflags();
|
||||
/* Restore EAX registers to be used again */
|
||||
x86gen.regs[X86_REG_EAX]->notusable=false;
|
||||
}
|
||||
|
||||
static Bit8u * gen_create_branch(BranchTypes type) {
|
||||
/* First free all registers */
|
||||
cache_addb(0x70+type);
|
||||
cache_addb(0);
|
||||
return (cache.pos-1);
|
||||
}
|
||||
|
||||
static void gen_fill_branch(Bit8u * data) {
|
||||
*data=(cache.pos-data-1);
|
||||
}
|
||||
|
||||
static void gen_jmp_ptr(void * ptr,Bits imm=0) {
|
||||
cache_addb(0xa1);
|
||||
cache_addd((Bit32u)ptr);
|
||||
cache_addb(0xff); //JMP EA
|
||||
if (!imm) { //NO EBP
|
||||
cache_addb(0x20);
|
||||
} else if ((imm>=-128 && imm<=127)) {
|
||||
cache_addb(0x60);
|
||||
cache_addb(imm);
|
||||
} else {
|
||||
cache_addb(0xa0);
|
||||
cache_addd(imm);
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_save_flags(DynReg * dynreg,bool stored) {
|
||||
GenReg * genreg=FindDynReg(dynreg);
|
||||
if (!stored) cache_addb(0x9c); //Pushfd
|
||||
cache_addb(0x58+genreg->index); //POP 32 REG
|
||||
dynreg->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_load_flags(DynReg * dynreg) {
|
||||
GenReg * genreg=FindDynReg(dynreg);
|
||||
cache_addb(0x50+genreg->index); //PUSH 32
|
||||
cache_addb(0x9d); //POPFD
|
||||
}
|
||||
|
||||
static void gen_save_host_direct(void * data,Bits imm) {
|
||||
cache_addw(0x05c7); //MOV [],dword
|
||||
cache_addd((Bit32u)data);
|
||||
cache_addd(imm);
|
||||
}
|
||||
|
||||
static void gen_load_host(void * data,DynReg * dr1,Bitu size) {
|
||||
GenReg * gr1=FindDynReg(dr1);
|
||||
switch (size) {
|
||||
case 1:cache_addw(0xb60f);break; //movzx byte
|
||||
case 2:cache_addw(0xb70f);break; //movzx word
|
||||
case 4:cache_addb(0x8b);break; //mov
|
||||
default:
|
||||
IllegalOption();
|
||||
}
|
||||
cache_addb(0x5+(gr1->index<<3));
|
||||
cache_addd((Bit32u)data);
|
||||
dr1->flags|=DYNFLG_CHANGED;
|
||||
}
|
||||
|
||||
static void gen_return(BlockReturn retcode) {
|
||||
cache_addb(0xb8);
|
||||
cache_addd(retcode);
|
||||
cache_addb(0xc3);
|
||||
}
|
||||
|
||||
static void gen_init(void) {
|
||||
x86gen.regs[X86_REG_EAX]=new GenReg(0,false);
|
||||
x86gen.regs[X86_REG_ECX]=new GenReg(1,false);
|
||||
x86gen.regs[X86_REG_EDX]=new GenReg(2,false);
|
||||
x86gen.regs[X86_REG_EBX]=new GenReg(3,true);
|
||||
x86gen.regs[X86_REG_EBP]=new GenReg(5,true);
|
||||
x86gen.regs[X86_REG_ESI]=new GenReg(6,true);
|
||||
x86gen.regs[X86_REG_EDI]=new GenReg(7,true);
|
||||
}
|
||||
|
||||
|
0
src/cpu/core_dyn_x86/string.h
Normal file
0
src/cpu/core_dyn_x86/string.h
Normal file
Loading…
Add table
Reference in a new issue