mirror of
https://github.com/retro100/dosbox-wii.git
synced 2024-11-05 01:45:11 +01:00
476 lines
11 KiB
C++
476 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2002-2007 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_DYNAMIC_X86)
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
#if defined (WIN32)
|
|
#include <windows.h>
|
|
#include <winbase.h>
|
|
#endif
|
|
|
|
#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"
|
|
#include "fpu.h"
|
|
|
|
#define CACHE_MAXSIZE (4096*3)
|
|
#define CACHE_TOTAL (1024*1024*8)
|
|
#define CACHE_PAGES (512)
|
|
#define CACHE_BLOCKS (64*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
|
|
|
|
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_NEWESP,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_TEST,
|
|
DOP_MOV,
|
|
DOP_XCHG,
|
|
};
|
|
|
|
enum ShiftOps {
|
|
SHIFT_ROL,SHIFT_ROR,
|
|
SHIFT_RCL,SHIFT_RCR,
|
|
SHIFT_SHL,SHIFT_SHR,
|
|
SHIFT_SAL,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 BlockReturn {
|
|
BR_Normal=0,
|
|
BR_Cycles,
|
|
BR_Link1,BR_Link2,
|
|
BR_Opcode,
|
|
#if (C_DEBUG)
|
|
BR_OpcodeFull,
|
|
#endif
|
|
BR_Iret,
|
|
BR_CallBack,
|
|
BR_SMCBlock
|
|
};
|
|
|
|
#define SMC_CURRENT_BLOCK 0xffff
|
|
|
|
|
|
#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 //Value is in a register and changed from load
|
|
#define DYNFLG_ACTIVE 0x20 //Register has an active value
|
|
|
|
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,newesp;
|
|
} extra_regs;
|
|
|
|
static void IllegalOption(const char* msg) {
|
|
E_Exit("DynCore: illegal option in %s",msg);
|
|
}
|
|
|
|
#include "core_dyn_x86/cache.h"
|
|
|
|
static struct {
|
|
Bitu callback;
|
|
Bit32u readdata;
|
|
} core_dyn;
|
|
|
|
static struct {
|
|
Bit32u state[32];
|
|
FPU_P_Reg temp,temp2;
|
|
Bit32u dh_fpu_enabled;
|
|
Bit32u state_used;
|
|
Bit32u cw,host_cw;
|
|
Bit8u temp_state[128];
|
|
} dyn_dh_fpu;
|
|
|
|
|
|
#include "core_dyn_x86/risc_x86.h"
|
|
|
|
struct DynState {
|
|
DynReg regs[G_MAX];
|
|
};
|
|
|
|
static void dyn_flags_host_to_gen(void) {
|
|
gen_dop_word(DOP_MOV,true,DREG(EXIT),DREG(FLAGS));
|
|
gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
|
|
gen_load_flags(DREG(EXIT));
|
|
gen_releasereg(DREG(EXIT));
|
|
gen_releasereg(DREG(FLAGS));
|
|
}
|
|
|
|
static void dyn_flags_gen_to_host(void) {
|
|
gen_save_flags(DREG(EXIT));
|
|
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));
|
|
gen_releasereg(DREG(FLAGS));
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
|
|
static void dyn_saveregister(DynReg * src_reg, DynReg * dst_reg) {
|
|
dst_reg->flags=src_reg->flags;
|
|
dst_reg->genreg=src_reg->genreg;
|
|
}
|
|
|
|
static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) {
|
|
dst_reg->flags=src_reg->flags;
|
|
dst_reg->genreg=src_reg->genreg;
|
|
dst_reg->genreg->dynreg=dst_reg; // necessary when register has been released
|
|
}
|
|
|
|
#include "core_dyn_x86/decoder.h"
|
|
|
|
#if defined (_MSC_VER)
|
|
#define DH_FPU_SAVE_REINIT \
|
|
{ \
|
|
__asm { \
|
|
__asm fnsave dyn_dh_fpu.state[0] \
|
|
} \
|
|
dyn_dh_fpu.state_used=false; \
|
|
dyn_dh_fpu.state[0]|=0x3f; \
|
|
}
|
|
#else
|
|
#define DH_FPU_SAVE_REINIT \
|
|
{ \
|
|
__asm__ volatile ( \
|
|
"fnsave %0 \n" \
|
|
: \
|
|
: "m" (dyn_dh_fpu.state[0]) \
|
|
: "memory" \
|
|
); \
|
|
dyn_dh_fpu.state_used=false; \
|
|
dyn_dh_fpu.state[0]|=0x3f; \
|
|
}
|
|
#endif
|
|
|
|
|
|
Bits CPU_Core_Dyn_X86_Run(void) {
|
|
/* Determine the linear address of CS:EIP */
|
|
restart_core:
|
|
PhysPt ip_point=SegPhys(cs)+reg_eip;
|
|
#if C_HEAVY_DEBUG
|
|
if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
|
|
#endif
|
|
CodePageHandler * chandler=0;
|
|
if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
|
|
CPU_Exception(cpu.exception.which,cpu.exception.error);
|
|
goto restart_core;
|
|
}
|
|
if (!chandler) {
|
|
if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
|
|
return CPU_Core_Normal_Run();
|
|
}
|
|
/* Find correct Dynamic Block to run */
|
|
CacheBlock * block=chandler->FindCacheBlock(ip_point&4095);
|
|
if (!block) {
|
|
if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
|
|
block=CreateCacheBlock(chandler,ip_point,32);
|
|
} else {
|
|
Bitu old_cycles=CPU_Cycles;
|
|
CPU_Cycles=1;
|
|
Bits nc_retcode=CPU_Core_Normal_Run();
|
|
if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
|
|
if (!nc_retcode) {
|
|
CPU_Cycles=old_cycles-1;
|
|
goto restart_core;
|
|
}
|
|
CPU_CycleLeft+=old_cycles;
|
|
return nc_retcode;
|
|
}
|
|
}
|
|
run_block:
|
|
cache.block.running=0;
|
|
BlockReturn ret=gen_runcode(block->cache.start);
|
|
switch (ret) {
|
|
case BR_Iret:
|
|
#if C_HEAVY_DEBUG
|
|
if (DEBUG_HeavyIsBreakpoint()) {
|
|
if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
|
|
return debugCallback;
|
|
}
|
|
#endif
|
|
if (!GETFLAG(TF)) goto restart_core;
|
|
cpudecoder=CPU_Core_Dyn_X86_Trap_Run;
|
|
if (!dyn_dh_fpu.state_used) return CBRET_NONE;
|
|
DH_FPU_SAVE_REINIT
|
|
return CBRET_NONE;
|
|
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
|
|
if (!dyn_dh_fpu.state_used) return CBRET_NONE;
|
|
DH_FPU_SAVE_REINIT
|
|
return CBRET_NONE;
|
|
case BR_CallBack:
|
|
if (!dyn_dh_fpu.state_used) return core_dyn.callback;
|
|
DH_FPU_SAVE_REINIT
|
|
return core_dyn.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:
|
|
CPU_CycleLeft+=CPU_Cycles;
|
|
CPU_Cycles=1;
|
|
if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
|
|
return CPU_Core_Normal_Run();
|
|
#if (C_DEBUG)
|
|
case BR_OpcodeFull:
|
|
CPU_CycleLeft+=CPU_Cycles;
|
|
CPU_Cycles=1;
|
|
if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
|
|
return CPU_Core_Full_Run();
|
|
#endif
|
|
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.block.running->LinkTo(ret==BR_Link2,block);
|
|
goto run_block;
|
|
}
|
|
}
|
|
goto restart_core;
|
|
}
|
|
if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
|
|
return CBRET_NONE;
|
|
}
|
|
|
|
Bits CPU_Core_Dyn_X86_Trap_Run(void) {
|
|
Bits oldCycles = CPU_Cycles;
|
|
CPU_Cycles = 1;
|
|
cpu.trap_skip = false;
|
|
|
|
Bits ret=CPU_Core_Normal_Run();
|
|
if (!cpu.trap_skip) CPU_HW_Interrupt(1);
|
|
CPU_Cycles = oldCycles-1;
|
|
cpudecoder = &CPU_Core_Dyn_X86_Run;
|
|
|
|
return ret;
|
|
}
|
|
|
|
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_NEWESP].data=&extra_regs.newesp;
|
|
DynRegs[G_NEWESP].flags=0;
|
|
|
|
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;
|
|
/* Init the generator */
|
|
gen_init();
|
|
|
|
/* Init the fpu state */
|
|
dyn_dh_fpu.dh_fpu_enabled=true;
|
|
dyn_dh_fpu.state_used=false;
|
|
dyn_dh_fpu.cw=0x37f;
|
|
#if defined (_MSC_VER)
|
|
__asm {
|
|
__asm finit
|
|
__asm fsave dyn_dh_fpu.state[0]
|
|
__asm fstcw dyn_dh_fpu.host_cw
|
|
}
|
|
#else
|
|
__asm__ volatile (
|
|
"finit \n"
|
|
"fsave %0 \n"
|
|
"fstcw %1 \n"
|
|
:
|
|
: "m" (dyn_dh_fpu.state[0]), "m" (dyn_dh_fpu.host_cw)
|
|
: "memory"
|
|
);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache) {
|
|
/* Initialize code cache and dynamic blocks */
|
|
cache_init(enable_cache);
|
|
}
|
|
|
|
void CPU_Core_Dyn_X86_Cache_Close(void) {
|
|
cache_close();
|
|
}
|
|
|
|
void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu) {
|
|
dyn_dh_fpu.dh_fpu_enabled=dh_fpu;
|
|
}
|
|
|
|
#endif
|