mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-08 19:32:39 +08:00
remove C_DYNREC dynamic core
This commit is contained in:
parent
a77a5e33c9
commit
d6f3d82f8b
@ -51,10 +51,6 @@
|
||||
/* Define to 1 to use x86 dynamic cpu core */
|
||||
#undef C_DYNAMIC_X86
|
||||
|
||||
/* Define to 1 to use recompiling cpu core. Can not be used together with the
|
||||
dynamic-x86 core */
|
||||
#undef C_DYNREC
|
||||
|
||||
/* Define to 1 to enable fluidsynth MIDI synthesis */
|
||||
#undef C_FLUIDSYNTH
|
||||
|
||||
|
42
configure
vendored
42
configure
vendored
@ -748,7 +748,6 @@ enable_debug
|
||||
enable_core_inline
|
||||
enable_dynamic_core
|
||||
enable_dynamic_x86
|
||||
enable_dynrec
|
||||
enable_fpu
|
||||
enable_fpu_x86
|
||||
enable_unaligned_memory
|
||||
@ -1400,7 +1399,6 @@ Optional Features:
|
||||
--enable-core-inline Enable inlined memory handling in CPU Core
|
||||
--disable-dynamic-core Disable all dynamic cores
|
||||
--disable-dynamic-x86 Disable x86 dynamic cpu core
|
||||
--disable-dynrec Disable recompiling cpu core
|
||||
--disable-fpu Disable fpu support
|
||||
--disable-fpu-x86 Disable x86 assembly fpu core
|
||||
--disable-unaligned-memory
|
||||
@ -6829,43 +6827,6 @@ $as_echo "no" >&6; }
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-dynrec was given.
|
||||
if test "${enable_dynrec+set}" = set; then :
|
||||
enableval=$enable_dynrec;
|
||||
else
|
||||
enable_dynrec=yes
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether recompiling cpu core will be enabled" >&5
|
||||
$as_echo_n "checking whether recompiling cpu core will be enabled... " >&6; }
|
||||
if test x$enable_dynrec = xno -o x$enable_dynamic_core = xno; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
else
|
||||
if test x$c_targetcpu = xx86 ; then
|
||||
if test x$enable_dynamic_x86 = xno ; then
|
||||
$as_echo "#define C_DYNREC 1" >>confdefs.h
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using dynamic-x86" >&5
|
||||
$as_echo "no, using dynamic-x86" >&6; }
|
||||
fi
|
||||
else
|
||||
if test x$c_targetcpu = xx86_64 ; then
|
||||
$as_echo "#define C_DYNREC 1" >>confdefs.h
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-fpu was given.
|
||||
if test "${enable_fpu+set}" = set; then :
|
||||
enableval=$enable_fpu;
|
||||
@ -7693,7 +7654,7 @@ fi
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile src/Makefile 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 src/gui/Makefile src/hardware/Makefile src/hardware/serialport/Makefile src/hardware/reSID/Makefile src/hardware/parport/Makefile src/ints/Makefile src/libs/Makefile src/libs/zmbv/Makefile src/libs/gui_tk/Makefile src/libs/porttalk/Makefile src/misc/Makefile src/shell/Makefile src/platform/Makefile include/Makefile"
|
||||
ac_config_files="$ac_config_files Makefile src/Makefile src/cpu/Makefile src/cpu/core_full/Makefile src/cpu/core_normal/Makefile src/cpu/core_dyn_x86/Makefile src/debug/Makefile src/dos/Makefile src/fpu/Makefile src/gui/Makefile src/hardware/Makefile src/hardware/serialport/Makefile src/hardware/reSID/Makefile src/hardware/parport/Makefile src/ints/Makefile src/libs/Makefile src/libs/zmbv/Makefile src/libs/gui_tk/Makefile src/libs/porttalk/Makefile src/misc/Makefile src/shell/Makefile src/platform/Makefile include/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@ -8437,7 +8398,6 @@ do
|
||||
"src/cpu/core_full/Makefile") CONFIG_FILES="$CONFIG_FILES src/cpu/core_full/Makefile" ;;
|
||||
"src/cpu/core_normal/Makefile") CONFIG_FILES="$CONFIG_FILES src/cpu/core_normal/Makefile" ;;
|
||||
"src/cpu/core_dyn_x86/Makefile") CONFIG_FILES="$CONFIG_FILES src/cpu/core_dyn_x86/Makefile" ;;
|
||||
"src/cpu/core_dynrec/Makefile") CONFIG_FILES="$CONFIG_FILES src/cpu/core_dynrec/Makefile" ;;
|
||||
"src/debug/Makefile") CONFIG_FILES="$CONFIG_FILES src/debug/Makefile" ;;
|
||||
"src/dos/Makefile") CONFIG_FILES="$CONFIG_FILES src/dos/Makefile" ;;
|
||||
"src/fpu/Makefile") CONFIG_FILES="$CONFIG_FILES src/fpu/Makefile" ;;
|
||||
|
25
configure.ac
25
configure.ac
@ -285,30 +285,6 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
AH_TEMPLATE(C_DYNREC,[Define to 1 to use recompiling cpu core. Can not be used together with the dynamic-x86 core])
|
||||
AC_ARG_ENABLE(dynrec,AC_HELP_STRING([--disable-dynrec],[Disable recompiling cpu core]),,enable_dynrec=yes)
|
||||
AC_MSG_CHECKING(whether recompiling cpu core will be enabled)
|
||||
if test x$enable_dynrec = xno -o x$enable_dynamic_core = xno; then
|
||||
AC_MSG_RESULT(no)
|
||||
else
|
||||
dnl x86 only enable it if dynamic-x86 is disabled.
|
||||
if test x$c_targetcpu = xx86 ; then
|
||||
if test x$enable_dynamic_x86 = xno ; then
|
||||
AC_DEFINE(C_DYNREC,1)
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT([no, using dynamic-x86])
|
||||
fi
|
||||
else
|
||||
if test x$c_targetcpu = xx86_64 ; then
|
||||
AC_DEFINE(C_DYNREC,1)
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
AH_TEMPLATE(C_FPU,[Define to 1 to enable floating point emulation])
|
||||
AC_ARG_ENABLE(fpu,AC_HELP_STRING([--disable-fpu],[Disable fpu support]),,enable_fpu=yes)
|
||||
AC_MSG_CHECKING(whether fpu emulation will be enabled)
|
||||
@ -528,7 +504,6 @@ 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
|
||||
|
@ -1,7 +1,7 @@
|
||||
SUBDIRS = core_full core_normal core_dyn_x86 core_dynrec
|
||||
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 core_simple.cpp core_prefetch.cpp \
|
||||
core_dyn_x86.cpp core_dynrec.cpp mmx.cpp
|
||||
core_dyn_x86.cpp mmx.cpp
|
||||
|
@ -74,8 +74,7 @@ libcpu_a_LIBADD =
|
||||
am_libcpu_a_OBJECTS = callback.$(OBJEXT) cpu.$(OBJEXT) flags.$(OBJEXT) \
|
||||
modrm.$(OBJEXT) core_full.$(OBJEXT) paging.$(OBJEXT) \
|
||||
core_normal.$(OBJEXT) core_simple.$(OBJEXT) \
|
||||
core_prefetch.$(OBJEXT) core_dyn_x86.$(OBJEXT) \
|
||||
core_dynrec.$(OBJEXT) mmx.$(OBJEXT)
|
||||
core_prefetch.$(OBJEXT) core_dyn_x86.$(OBJEXT) mmx.$(OBJEXT)
|
||||
libcpu_a_OBJECTS = $(am_libcpu_a_OBJECTS)
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
@ -294,12 +293,12 @@ target_alias = @target_alias@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
SUBDIRS = core_full core_normal core_dyn_x86 core_dynrec
|
||||
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 core_simple.cpp core_prefetch.cpp \
|
||||
core_dyn_x86.cpp core_dynrec.cpp mmx.cpp
|
||||
core_dyn_x86.cpp mmx.cpp
|
||||
|
||||
all: all-recursive
|
||||
|
||||
@ -351,7 +350,6 @@ distclean-compile:
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_dyn_x86.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_dynrec.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_full.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_normal.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_prefetch.Po@am__quote@
|
||||
|
@ -1,334 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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 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 "lazyflags.h"
|
||||
#include "pic.h"
|
||||
|
||||
#define CACHE_MAXSIZE (4096*2)
|
||||
#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)
|
||||
|
||||
|
||||
//#define DYN_LOG 1 //Turn Logging on.
|
||||
|
||||
|
||||
#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_VAL(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?BH_INDEX:BL_INDEX])
|
||||
// access to 16/32bit general registers
|
||||
#define DRCD_REG_WORD(reg,dwrd) ((dwrd)?((void*)(&cpu_regs.regs[reg].dword[DW_INDEX])):((void*)(&cpu_regs.regs[reg].word[W_INDEX])))
|
||||
|
||||
|
||||
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
|
||||
#define MIPSEL 0x03
|
||||
#define ARMV4LE 0x04
|
||||
#define ARMV7LE 0x05
|
||||
#define POWERPC 0x04
|
||||
|
||||
#if C_TARGETCPU == X86_64
|
||||
#include "core_dynrec/risc_x64.h"
|
||||
#elif C_TARGETCPU == X86
|
||||
#include "core_dynrec/risc_x86.h"
|
||||
#elif C_TARGETCPU == MIPSEL
|
||||
#include "core_dynrec/risc_mipsel32.h"
|
||||
#elif (C_TARGETCPU == ARMV4LE) || (C_TARGETCPU == ARMV7LE)
|
||||
#include "core_dynrec/risc_armv4le.h"
|
||||
#elif C_TARGETCPU == POWERPC
|
||||
#include "core_dynrec/risc_ppc.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;
|
||||
CodePageHandlerDynRec * temp_handler=(CodePageHandlerDynRec *)get_tlb_readhandler(temp_ip);
|
||||
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;
|
||||
Bitu CPU_CyclesOld = CPU_Cycles;
|
||||
// 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);
|
||||
cycle_count += CPU_CyclesOld - CPU_Cycles;
|
||||
|
||||
switch (ret) {
|
||||
case BR_Iret:
|
||||
#if C_HEAVY_DEBUG
|
||||
if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
|
||||
#endif
|
||||
if (!GETFLAG(TF)) {
|
||||
if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE;
|
||||
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 maximum 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
|
@ -1,5 +0,0 @@
|
||||
noinst_HEADERS = cache.h decoder.h decoder_basic.h decoder_opcodes.h \
|
||||
dyn_fpu.h operators.h risc_x64.h risc_x86.h risc_mipsel32.h \
|
||||
risc_armv4le.h risc_armv4le-common.h \
|
||||
risc_armv4le-o3.h risc_armv4le-thumb.h \
|
||||
risc_armv4le-thumb-iw.h risc_armv4le-thumb-niw.h
|
@ -1,451 +0,0 @@
|
||||
# Makefile.in generated by automake 1.13.1 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
|
||||
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
VPATH = @srcdir@
|
||||
am__make_dryrun = \
|
||||
{ \
|
||||
am__dry=no; \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
|
||||
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
|
||||
*) \
|
||||
for am__flg in $$MAKEFLAGS; do \
|
||||
case $$am__flg in \
|
||||
*=*|--*) ;; \
|
||||
*n*) am__dry=yes; break;; \
|
||||
esac; \
|
||||
done;; \
|
||||
esac; \
|
||||
test $$am__dry = yes; \
|
||||
}
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
subdir = src/cpu/core_dynrec
|
||||
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
|
||||
$(noinst_HEADERS)
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
|
||||
$(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
am__v_P_0 = false
|
||||
am__v_P_1 = :
|
||||
AM_V_GEN = $(am__v_GEN_@AM_V@)
|
||||
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
am__v_GEN_1 =
|
||||
AM_V_at = $(am__v_at_@AM_V@)
|
||||
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
|
||||
am__v_at_0 = @
|
||||
am__v_at_1 =
|
||||
SOURCES =
|
||||
DIST_SOURCES =
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
HEADERS = $(noinst_HEADERS)
|
||||
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
|
||||
# Read a list of newline-separated strings from the standard input,
|
||||
# and print each of them once, without duplicates. Input order is
|
||||
# *not* preserved.
|
||||
am__uniquify_input = $(AWK) '\
|
||||
BEGIN { nonempty = 0; } \
|
||||
{ items[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in items) print i; }; } \
|
||||
'
|
||||
# Make sure the list of sources is unique. This is necessary because,
|
||||
# e.g., the same source file might be shared among _SOURCES variables
|
||||
# for different programs/libraries.
|
||||
am__define_uniq_tagged_files = \
|
||||
list='$(am__tagged_files)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | $(am__uniquify_input)`
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ALSA_CFLAGS = @ALSA_CFLAGS@
|
||||
ALSA_LIBS = @ALSA_LIBS@
|
||||
AMTAR = @AMTAR@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
GREP = @GREP@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
RANLIB = @RANLIB@
|
||||
SDL_CFLAGS = @SDL_CFLAGS@
|
||||
SDL_CONFIG = @SDL_CONFIG@
|
||||
SDL_LIBS = @SDL_LIBS@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
WINDRES = @WINDRES@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
noinst_HEADERS = cache.h decoder.h decoder_basic.h decoder_opcodes.h \
|
||||
dyn_fpu.h operators.h risc_x64.h risc_x86.h risc_mipsel32.h \
|
||||
risc_armv4le.h risc_armv4le-common.h \
|
||||
risc_armv4le-o3.h risc_armv4le-thumb.h \
|
||||
risc_armv4le-thumb-iw.h risc_armv4le-thumb-niw.h
|
||||
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/cpu/core_dynrec/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu src/cpu/core_dynrec/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
ID: $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); mkid -fID $$unique
|
||||
tags: tags-am
|
||||
TAGS: tags
|
||||
|
||||
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
$(am__define_uniq_tagged_files); \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: ctags-am
|
||||
|
||||
CTAGS: ctags
|
||||
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
cscopelist: cscopelist-am
|
||||
|
||||
cscopelist-am: $(am__tagged_files)
|
||||
list='$(am__tagged_files)'; \
|
||||
case "$(srcdir)" in \
|
||||
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
|
||||
*) sdir=$(subdir)/$(srcdir) ;; \
|
||||
esac; \
|
||||
for i in $$list; do \
|
||||
if test -f "$$i"; then \
|
||||
echo "$(subdir)/$$i"; \
|
||||
else \
|
||||
echo "$$sdir/$$i"; \
|
||||
fi; \
|
||||
done >> $(top_builddir)/cscope.files
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(HEADERS)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-generic distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-generic
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
|
||||
cscopelist-am ctags ctags-am distclean distclean-generic \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
|
||||
uninstall-am
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
@ -1,665 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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]; // maximum 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;
|
||||
|
||||
|
||||
// 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;
|
||||
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_GetPhysicalPage(ip_point)-(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 (!host_readb(&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 (!host_readw(&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);
|
||||
}
|
||||
#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
|
||||
host_writew(&invalidation_map[addr],
|
||||
host_readw(&invalidation_map[addr])+0x101);
|
||||
#else
|
||||
(*(Bit16u*)&invalidation_map[addr])+=0x101;
|
||||
#endif
|
||||
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 (!host_readd(&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);
|
||||
}
|
||||
#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
|
||||
host_writed(&invalidation_map[addr],
|
||||
host_readd(&invalidation_map[addr])+0x1010101);
|
||||
#else
|
||||
(*(Bit32u*)&invalidation_map[addr])+=0x1010101;
|
||||
#endif
|
||||
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 (!host_readb(&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 (!host_readw(&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);
|
||||
}
|
||||
#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
|
||||
host_writew(&invalidation_map[addr],
|
||||
host_readw(&invalidation_map[addr])+0x101);
|
||||
#else
|
||||
(*(Bit16u*)&invalidation_map[addr])+=0x101;
|
||||
#endif
|
||||
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 (!host_readd(&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);
|
||||
}
|
||||
#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
|
||||
host_writed(&invalidation_map[addr],
|
||||
host_readd(&invalidation_map[addr])+0x1010101);
|
||||
#else
|
||||
(*(Bit32u*)&invalidation_map[addr])+=0x1010101;
|
||||
#endif
|
||||
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 || (block->cache.next->cache.start>(cache_code_start_ptr + CACHE_TOTAL - CACHE_MAXSIZE))) {
|
||||
// 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);
|
||||
|
||||
|
||||
/* 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
|
||||
#if defined (WIN32)
|
||||
cache_code_start_ptr=(Bit8u*)VirtualAlloc(0,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP,
|
||||
MEM_COMMIT,PAGE_EXECUTE_READWRITE);
|
||||
if (!cache_code_start_ptr)
|
||||
cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
|
||||
#else
|
||||
cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
|
||||
#endif
|
||||
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) {
|
||||
### care: under windows VirtualFree() has to be used if
|
||||
### VirtualAlloc was used for memory allocation
|
||||
free(cache_code_start_ptr);
|
||||
cache_code_start_ptr = NULL;
|
||||
}
|
||||
cache_code = NULL;
|
||||
cache_code_link_blocks = NULL;
|
||||
cache_initialized = false; */
|
||||
}
|
@ -1,615 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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 maximum
|
||||
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;
|
||||
|
||||
// lfs
|
||||
case 0xb4:
|
||||
dyn_get_modrm();
|
||||
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
|
||||
dyn_load_seg_off_ea(DRC_SEG_FS);
|
||||
break;
|
||||
// lgs
|
||||
case 0xb5:
|
||||
dyn_get_modrm();
|
||||
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
|
||||
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:
|
||||
#if DYN_LOG
|
||||
// LOG_MSG("Unhandled dual opcode 0F%02X",dual_code);
|
||||
#endif
|
||||
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_lea();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;
|
||||
|
||||
// les
|
||||
case 0xc4:
|
||||
dyn_get_modrm();
|
||||
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
|
||||
dyn_load_seg_off_ea(DRC_SEG_ES);
|
||||
break;
|
||||
// lds
|
||||
case 0xc5:
|
||||
dyn_get_modrm();
|
||||
if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
|
||||
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);
|
||||
dyn_check_exception(FC_RETOP);
|
||||
break;
|
||||
case 0xfb: //STI
|
||||
gen_call_function_raw((void *)&CPU_STI);
|
||||
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:
|
||||
switch (dyn_grp4_ev()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
goto core_close_block;
|
||||
case 2:
|
||||
goto illegalopcode;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#if DYN_LOG
|
||||
// LOG_MSG("Dynrec unhandled opcode %X",opcode);
|
||||
#endif
|
||||
goto illegalopcode;
|
||||
}
|
||||
}
|
||||
// link to next block because the maximum number of opcodes has been reached
|
||||
dyn_set_eip_end();
|
||||
dyn_reduce_cycles();
|
||||
gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
|
||||
dyn_closeblock();
|
||||
goto finish_block;
|
||||
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
@ -1,687 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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_protect_reg(FC_OP1);
|
||||
gen_call_function_raw((void*)&FPU_PREP_PUSH);
|
||||
gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
|
||||
gen_restore_reg(FC_OP1);
|
||||
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);
|
||||
dyn_fill_ea(FC_OP1);
|
||||
gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
|
||||
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);
|
||||
dyn_fill_ea(FC_OP1);
|
||||
gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
|
||||
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);
|
||||
dyn_fill_ea(FC_OP1);
|
||||
gen_mov_word_to_reg(FC_OP2,(void*)(&fpu.sw),true);
|
||||
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 0x00: /* FFREEP STi */
|
||||
dyn_fpu_top();
|
||||
gen_call_function_R((void*)&FPU_FFREE,FC_OP2);
|
||||
gen_call_function_raw((void*)&FPU_FPOP);
|
||||
break;
|
||||
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);
|
||||
MOV_REG_WORD16_FROM_HOST_REG(FC_OP1,DRC_REG_EAX);
|
||||
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);
|
||||
dyn_fill_ea(FC_OP1);
|
||||
gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
|
||||
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);
|
||||
dyn_fill_ea(FC_OP1);
|
||||
gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
|
||||
gen_call_function_RR((void*)&FPU_FBLD,FC_OP1,FC_OP2);
|
||||
break;
|
||||
case 0x05: /* FILD Bit64s */
|
||||
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_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
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* ARMv4 (little endian) backend by M-HT (common data/functions) */
|
||||
|
||||
|
||||
// some configuring defines that specify the capabilities of this architecture
|
||||
// or aspects of the recompiling
|
||||
|
||||
// protect FC_ADDR over function calls if necessaray
|
||||
// #define DRC_PROTECT_ADDR_REG
|
||||
|
||||
// try to use non-flags generating functions if possible
|
||||
#define DRC_FLAGS_INVALIDATION
|
||||
// try to replace _simple functions by code
|
||||
#define DRC_FLAGS_INVALIDATION_DCODE
|
||||
|
||||
// type with the same size as a pointer
|
||||
#define DRC_PTR_SIZE_IM Bit32u
|
||||
|
||||
// calling convention modifier
|
||||
#define DRC_CALL_CONV /* nothing */
|
||||
#define DRC_FC /* nothing */
|
||||
|
||||
// use FC_REGS_ADDR to hold the address of "cpu_regs" and to access it using FC_REGS_ADDR
|
||||
#define DRC_USE_REGS_ADDR
|
||||
// use FC_SEGS_ADDR to hold the address of "Segs" and to access it using FC_SEGS_ADDR
|
||||
#define DRC_USE_SEGS_ADDR
|
||||
|
||||
// register mapping
|
||||
typedef Bit8u HostReg;
|
||||
|
||||
// "lo" registers
|
||||
#define HOST_r0 0
|
||||
#define HOST_r1 1
|
||||
#define HOST_r2 2
|
||||
#define HOST_r3 3
|
||||
#define HOST_r4 4
|
||||
#define HOST_r5 5
|
||||
#define HOST_r6 6
|
||||
#define HOST_r7 7
|
||||
// "hi" registers
|
||||
#define HOST_r8 8
|
||||
#define HOST_r9 9
|
||||
#define HOST_r10 10
|
||||
#define HOST_r11 11
|
||||
#define HOST_r12 12
|
||||
#define HOST_r13 13
|
||||
#define HOST_r14 14
|
||||
#define HOST_r15 15
|
||||
|
||||
// register aliases
|
||||
// "lo" registers
|
||||
#define HOST_a1 HOST_r0
|
||||
#define HOST_a2 HOST_r1
|
||||
#define HOST_a3 HOST_r2
|
||||
#define HOST_a4 HOST_r3
|
||||
#define HOST_v1 HOST_r4
|
||||
#define HOST_v2 HOST_r5
|
||||
#define HOST_v3 HOST_r6
|
||||
#define HOST_v4 HOST_r7
|
||||
// "hi" registers
|
||||
#define HOST_v5 HOST_r8
|
||||
#define HOST_v6 HOST_r9
|
||||
#define HOST_v7 HOST_r10
|
||||
#define HOST_v8 HOST_r11
|
||||
#define HOST_ip HOST_r12
|
||||
#define HOST_sp HOST_r13
|
||||
#define HOST_lr HOST_r14
|
||||
#define HOST_pc HOST_r15
|
||||
|
||||
|
||||
static void cache_block_closing(Bit8u* block_start,Bitu block_size) {
|
||||
#if (__ARM_EABI__)
|
||||
//flush cache - eabi
|
||||
register unsigned long _beg __asm ("a1") = (unsigned long)(block_start); // block start
|
||||
register unsigned long _end __asm ("a2") = (unsigned long)(block_start+block_size); // block end
|
||||
register unsigned long _flg __asm ("a3") = 0;
|
||||
register unsigned long _par __asm ("r7") = 0xf0002; // sys_cacheflush
|
||||
__asm __volatile ("swi 0x0"
|
||||
: // no outputs
|
||||
: "r" (_beg), "r" (_end), "r" (_flg), "r" (_par)
|
||||
);
|
||||
#else
|
||||
// GP2X BEGIN
|
||||
//flush cache - old abi
|
||||
register unsigned long _beg __asm ("a1") = (unsigned long)(block_start); // block start
|
||||
register unsigned long _end __asm ("a2") = (unsigned long)(block_start+block_size); // block end
|
||||
register unsigned long _flg __asm ("a3") = 0;
|
||||
__asm __volatile ("swi 0x9f0002 @ sys_cacheflush"
|
||||
: // no outputs
|
||||
: "r" (_beg), "r" (_end), "r" (_flg)
|
||||
);
|
||||
// GP2X END
|
||||
#endif
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,918 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* ARMv4 (little endian) backend by M-HT (speed-tweaked arm version) */
|
||||
|
||||
|
||||
// temporary registers
|
||||
#define temp1 HOST_ip
|
||||
#define temp2 HOST_v3
|
||||
#define temp3 HOST_v4
|
||||
|
||||
// register that holds function return values
|
||||
#define FC_RETOP HOST_a1
|
||||
|
||||
// register used for address calculations,
|
||||
#define FC_ADDR HOST_v1 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
|
||||
|
||||
// register that holds the first parameter
|
||||
#define FC_OP1 HOST_a1
|
||||
|
||||
// register that holds the second parameter
|
||||
#define FC_OP2 HOST_a2
|
||||
|
||||
// special register that holds the third parameter for _R3 calls (byte accessible)
|
||||
#define FC_OP3 HOST_v2
|
||||
|
||||
// register that holds byte-accessible temporary values
|
||||
#define FC_TMP_BA1 HOST_a1
|
||||
|
||||
// register that holds byte-accessible temporary values
|
||||
#define FC_TMP_BA2 HOST_a2
|
||||
|
||||
// temporary register for LEA
|
||||
#define TEMP_REG_DRC HOST_v2
|
||||
|
||||
#ifdef DRC_USE_REGS_ADDR
|
||||
// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
|
||||
#define FC_REGS_ADDR HOST_v7
|
||||
#endif
|
||||
|
||||
#ifdef DRC_USE_SEGS_ADDR
|
||||
// used to hold the address of "Segs" - preferably filled in function gen_run_code
|
||||
#define FC_SEGS_ADDR HOST_v8
|
||||
#endif
|
||||
|
||||
|
||||
// helper macro
|
||||
#define ROTATE_SCALE(x) ( (x)?(32 - x):(0) )
|
||||
|
||||
|
||||
// instruction encodings
|
||||
|
||||
// move
|
||||
// mov dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define MOV_IMM(dst, imm, rimm) (0xe3a00000 + ((dst) << 12) + (imm) + ((rimm) << 7) )
|
||||
// mov dst, src, lsl #imm
|
||||
#define MOV_REG_LSL_IMM(dst, src, imm) (0xe1a00000 + ((dst) << 12) + (src) + ((imm) << 7) )
|
||||
// movs dst, src, lsl #imm
|
||||
#define MOVS_REG_LSL_IMM(dst, src, imm) (0xe1b00000 + ((dst) << 12) + (src) + ((imm) << 7) )
|
||||
// mov dst, src, lsr #imm
|
||||
#define MOV_REG_LSR_IMM(dst, src, imm) (0xe1a00020 + ((dst) << 12) + (src) + ((imm) << 7) )
|
||||
// mov dst, src, asr #imm
|
||||
#define MOV_REG_ASR_IMM(dst, src, imm) (0xe1a00040 + ((dst) << 12) + (src) + ((imm) << 7) )
|
||||
// mov dst, src, lsl rreg
|
||||
#define MOV_REG_LSL_REG(dst, src, rreg) (0xe1a00010 + ((dst) << 12) + (src) + ((rreg) << 8) )
|
||||
// mov dst, src, lsr rreg
|
||||
#define MOV_REG_LSR_REG(dst, src, rreg) (0xe1a00030 + ((dst) << 12) + (src) + ((rreg) << 8) )
|
||||
// mov dst, src, asr rreg
|
||||
#define MOV_REG_ASR_REG(dst, src, rreg) (0xe1a00050 + ((dst) << 12) + (src) + ((rreg) << 8) )
|
||||
// mov dst, src, ror rreg
|
||||
#define MOV_REG_ROR_REG(dst, src, rreg) (0xe1a00070 + ((dst) << 12) + (src) + ((rreg) << 8) )
|
||||
// mvn dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define MVN_IMM(dst, imm, rimm) (0xe3e00000 + ((dst) << 12) + (imm) + ((rimm) << 7) )
|
||||
|
||||
// arithmetic
|
||||
// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// add dst, src1, src2, lsl #imm
|
||||
#define ADD_REG_LSL_IMM(dst, src1, src2, imm) (0xe0800000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
|
||||
// sub dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define SUB_IMM(dst, src, imm, rimm) (0xe2400000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// sub dst, src1, src2, lsl #imm
|
||||
#define SUB_REG_LSL_IMM(dst, src1, src2, imm) (0xe0400000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
|
||||
// rsb dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define RSB_IMM(dst, src, imm, rimm) (0xe2600000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// cmp src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define CMP_IMM(src, imm, rimm) (0xe3500000 + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// nop
|
||||
#define NOP MOV_REG_LSL_IMM(HOST_r0, HOST_r0, 0)
|
||||
|
||||
// logical
|
||||
// tst src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define TST_IMM(src, imm, rimm) (0xe3100000 + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// and dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define AND_IMM(dst, src, imm, rimm) (0xe2000000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// and dst, src1, src2, lsl #imm
|
||||
#define AND_REG_LSL_IMM(dst, src1, src2, imm) (0xe0000000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
|
||||
// orr dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define ORR_IMM(dst, src, imm, rimm) (0xe3800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
// orr dst, src1, src2, lsl #imm
|
||||
#define ORR_REG_LSL_IMM(dst, src1, src2, imm) (0xe1800000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
|
||||
// orr dst, src1, src2, lsr #imm
|
||||
#define ORR_REG_LSR_IMM(dst, src1, src2, imm) (0xe1800020 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
|
||||
// eor dst, src1, src2, lsl #imm
|
||||
#define EOR_REG_LSL_IMM(dst, src1, src2, imm) (0xe0200000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
|
||||
// bic dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
|
||||
#define BIC_IMM(dst, src, imm, rimm) (0xe3c00000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
|
||||
|
||||
// load
|
||||
// ldr reg, [addr, #imm] @ 0 <= imm < 4096
|
||||
#define LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) )
|
||||
// ldrh reg, [addr, #imm] @ 0 <= imm < 256
|
||||
#define LDRH_IMM(reg, addr, imm) (0xe1d000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) )
|
||||
// ldrb reg, [addr, #imm] @ 0 <= imm < 4096
|
||||
#define LDRB_IMM(reg, addr, imm) (0xe5d00000 + ((reg) << 12) + ((addr) << 16) + (imm) )
|
||||
|
||||
// store
|
||||
// str reg, [addr, #imm] @ 0 <= imm < 4096
|
||||
#define STR_IMM(reg, addr, imm) (0xe5800000 + ((reg) << 12) + ((addr) << 16) + (imm) )
|
||||
// strh reg, [addr, #imm] @ 0 <= imm < 256
|
||||
#define STRH_IMM(reg, addr, imm) (0xe1c000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) )
|
||||
// strb reg, [addr, #imm] @ 0 <= imm < 4096
|
||||
#define STRB_IMM(reg, addr, imm) (0xe5c00000 + ((reg) << 12) + ((addr) << 16) + (imm) )
|
||||
|
||||
// branch
|
||||
// beq pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
|
||||
#define BEQ_FWD(imm) (0x0a000000 + ((imm) >> 2) )
|
||||
// bne pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
|
||||
#define BNE_FWD(imm) (0x1a000000 + ((imm) >> 2) )
|
||||
// bgt pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
|
||||
#define BGT_FWD(imm) (0xca000000 + ((imm) >> 2) )
|
||||
// b pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
|
||||
#define B_FWD(imm) (0xea000000 + ((imm) >> 2) )
|
||||
// bx reg
|
||||
#define BX(reg) (0xe12fff10 + (reg) )
|
||||
|
||||
|
||||
// move a full register from reg_src to reg_dst
|
||||
static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
|
||||
if(reg_src == reg_dst) return;
|
||||
cache_addd( MOV_REG_LSL_IMM(reg_dst, reg_src, 0) ); // mov reg_dst, reg_src
|
||||
}
|
||||
|
||||
// move a 32bit constant value into dest_reg
|
||||
static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
|
||||
Bits first, scale;
|
||||
if (imm == 0) {
|
||||
cache_addd( MOV_IMM(dest_reg, 0, 0) ); // mov dest_reg, #0
|
||||
} else {
|
||||
scale = 0;
|
||||
first = 1;
|
||||
while (imm) {
|
||||
while ((imm & 3) == 0) {
|
||||
imm>>=2;
|
||||
scale+=2;
|
||||
}
|
||||
if (first) {
|
||||
cache_addd( MOV_IMM(dest_reg, imm & 0xff, ROTATE_SCALE(scale)) ); // mov dest_reg, #((imm & 0xff) << scale)
|
||||
first = 0;
|
||||
} else {
|
||||
cache_addd( ORR_IMM(dest_reg, dest_reg, imm & 0xff, ROTATE_SCALE(scale)) ); // orr dest_reg, dest_reg, #((imm & 0xff) << scale)
|
||||
}
|
||||
imm>>=8;
|
||||
scale+=8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helper function for gen_mov_word_to_reg
|
||||
static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) {
|
||||
// alignment....
|
||||
if (dword) {
|
||||
if ((Bit32u)data & 3) {
|
||||
if ( ((Bit32u)data & 3) == 2 ) {
|
||||
cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
|
||||
cache_addd( LDRH_IMM(temp2, data_reg, 2) ); // ldrh temp2, [data_reg, #2]
|
||||
cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 16) ); // orr dest_reg, dest_reg, temp2, lsl #16
|
||||
} else {
|
||||
cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
|
||||
cache_addd( LDRH_IMM(temp2, data_reg, 1) ); // ldrh temp2, [data_reg, #1]
|
||||
cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8
|
||||
cache_addd( LDRB_IMM(temp2, data_reg, 3) ); // ldrb temp2, [data_reg, #3]
|
||||
cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 24) ); // orr dest_reg, dest_reg, temp2, lsl #24
|
||||
}
|
||||
} else {
|
||||
cache_addd( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg]
|
||||
}
|
||||
} else {
|
||||
if ((Bit32u)data & 1) {
|
||||
cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
|
||||
cache_addd( LDRB_IMM(temp2, data_reg, 1) ); // ldrb temp2, [data_reg, #1]
|
||||
cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8
|
||||
} else {
|
||||
cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)data);
|
||||
gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1);
|
||||
}
|
||||
|
||||
// move a 16bit constant value into dest_reg
|
||||
// the upper 16bit of the destination register may be destroyed
|
||||
static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
|
||||
gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm);
|
||||
}
|
||||
|
||||
// helper function for gen_mov_word_from_reg
|
||||
static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) {
|
||||
// alignment....
|
||||
if (dword) {
|
||||
if ((Bit32u)dest & 3) {
|
||||
if ( ((Bit32u)dest & 3) == 2 ) {
|
||||
cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
|
||||
cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 16) ); // mov temp2, src_reg, lsr #16
|
||||
cache_addd( STRH_IMM(temp2, data_reg, 2) ); // strh temp2, [data_reg, #2]
|
||||
} else {
|
||||
cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
|
||||
cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8
|
||||
cache_addd( STRH_IMM(temp2, data_reg, 1) ); // strh temp2, [data_reg, #1]
|
||||
cache_addd( MOV_REG_LSR_IMM(temp2, temp2, 16) ); // mov temp2, temp2, lsr #16
|
||||
cache_addd( STRB_IMM(temp2, data_reg, 3) ); // strb temp2, [data_reg, #3]
|
||||
}
|
||||
} else {
|
||||
cache_addd( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg]
|
||||
}
|
||||
} else {
|
||||
if ((Bit32u)dest & 1) {
|
||||
cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
|
||||
cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8
|
||||
cache_addd( STRB_IMM(temp2, data_reg, 1) ); // strb temp2, [data_reg, #1]
|
||||
} else {
|
||||
cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
|
||||
gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1);
|
||||
}
|
||||
|
||||
// move an 8bit value from memory into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)data);
|
||||
cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1]
|
||||
}
|
||||
|
||||
// move an 8bit value from memory into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
|
||||
gen_mov_byte_to_reg_low(dest_reg, data);
|
||||
}
|
||||
|
||||
// move an 8bit constant value into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
|
||||
cache_addd( MOV_IMM(dest_reg, imm, 0) ); // mov dest_reg, #(imm)
|
||||
}
|
||||
|
||||
// move an 8bit constant value into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
|
||||
gen_mov_byte_to_reg_low_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
// move the lowest 8bit of a register into memory
|
||||
static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
|
||||
cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1]
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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) {
|
||||
if (sign) {
|
||||
cache_addd( MOV_REG_LSL_IMM(reg, reg, 24) ); // mov reg, reg, lsl #24
|
||||
cache_addd( MOV_REG_ASR_IMM(reg, reg, 24) ); // mov reg, reg, asr #24
|
||||
} else {
|
||||
cache_addd( AND_IMM(reg, reg, 0xff, 0) ); // and reg, reg, #0xff
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (sign) {
|
||||
cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16
|
||||
cache_addd( MOV_REG_ASR_IMM(reg, reg, 16) ); // mov reg, reg, asr #16
|
||||
} else {
|
||||
cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16
|
||||
cache_addd( MOV_REG_LSR_IMM(reg, reg, 16) ); // mov reg, reg, lsr #16
|
||||
}
|
||||
}
|
||||
|
||||
// add a 32bit value from memory to a full register
|
||||
static void gen_add(HostReg reg,void* op) {
|
||||
gen_mov_word_to_reg(temp3, op, 1);
|
||||
cache_addd( ADD_REG_LSL_IMM(reg, reg, temp3, 0) ); // add reg, reg, temp3
|
||||
}
|
||||
|
||||
// add a 32bit constant value to a full register
|
||||
static void gen_add_imm(HostReg reg,Bit32u imm) {
|
||||
Bits scale;
|
||||
if(!imm) return;
|
||||
if (imm == 0xffffffff) {
|
||||
cache_addd( SUB_IMM(reg, reg, 1, 0) ); // sub reg, reg, #1
|
||||
} else {
|
||||
scale = 0;
|
||||
while (imm) {
|
||||
while ((imm & 3) == 0) {
|
||||
imm>>=2;
|
||||
scale+=2;
|
||||
}
|
||||
cache_addd( ADD_IMM(reg, reg, imm & 0xff, ROTATE_SCALE(scale)) ); // add reg, reg, #((imm & 0xff) << scale)
|
||||
imm>>=8;
|
||||
scale+=8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// and a 32bit constant value with a full register
|
||||
static void gen_and_imm(HostReg reg,Bit32u imm) {
|
||||
Bits scale;
|
||||
Bit32u imm2;
|
||||
imm2 = ~imm;
|
||||
if(!imm2) return;
|
||||
if (!imm) {
|
||||
cache_addd( MOV_IMM(reg, 0, 0) ); // mov reg, #0
|
||||
} else {
|
||||
scale = 0;
|
||||
while (imm2) {
|
||||
while ((imm2 & 3) == 0) {
|
||||
imm2>>=2;
|
||||
scale+=2;
|
||||
}
|
||||
cache_addd( BIC_IMM(reg, reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // bic reg, reg, #((imm2 & 0xff) << scale)
|
||||
imm2>>=8;
|
||||
scale+=8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// move a 32bit constant value into memory
|
||||
static void gen_mov_direct_dword(void* dest,Bit32u imm) {
|
||||
gen_mov_dword_to_reg_imm(temp3, imm);
|
||||
gen_mov_word_from_reg(temp3, dest, 1);
|
||||
}
|
||||
|
||||
// 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 dword memory value
|
||||
static void gen_add_direct_byte(void* dest,Bit8s imm) {
|
||||
if(!imm) return;
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
|
||||
gen_mov_word_to_reg_helper(temp3, dest, 1, temp1);
|
||||
if (imm >= 0) {
|
||||
cache_addd( ADD_IMM(temp3, temp3, (Bit32s)imm, 0) ); // add temp3, temp3, #(imm)
|
||||
} else {
|
||||
cache_addd( SUB_IMM(temp3, temp3, -((Bit32s)imm), 0) ); // sub temp3, temp3, #(-imm)
|
||||
}
|
||||
gen_mov_word_from_reg_helper(temp3, dest, 1, temp1);
|
||||
}
|
||||
|
||||
// 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) return;
|
||||
if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) {
|
||||
gen_add_direct_byte(dest,(Bit8s)imm);
|
||||
return;
|
||||
}
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
|
||||
gen_mov_word_to_reg_helper(temp3, dest, dword, temp1);
|
||||
// maybe use function gen_add_imm
|
||||
if (dword) {
|
||||
gen_mov_dword_to_reg_imm(temp2, imm);
|
||||
} else {
|
||||
gen_mov_word_to_reg_imm(temp2, (Bit16u)imm);
|
||||
}
|
||||
cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2
|
||||
gen_mov_word_from_reg_helper(temp3, dest, dword, temp1);
|
||||
}
|
||||
|
||||
// subtract an 8bit constant value from a dword memory value
|
||||
static void gen_sub_direct_byte(void* dest,Bit8s imm) {
|
||||
if(!imm) return;
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
|
||||
gen_mov_word_to_reg_helper(temp3, dest, 1, temp1);
|
||||
if (imm >= 0) {
|
||||
cache_addd( SUB_IMM(temp3, temp3, (Bit32s)imm, 0) ); // sub temp3, temp3, #(imm)
|
||||
} else {
|
||||
cache_addd( ADD_IMM(temp3, temp3, -((Bit32s)imm), 0) ); // add temp3, temp3, #(-imm)
|
||||
}
|
||||
gen_mov_word_from_reg_helper(temp3, dest, 1, temp1);
|
||||
}
|
||||
|
||||
// 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) return;
|
||||
if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) {
|
||||
gen_sub_direct_byte(dest,(Bit8s)imm);
|
||||
return;
|
||||
}
|
||||
gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
|
||||
gen_mov_word_to_reg_helper(temp3, dest, dword, temp1);
|
||||
// maybe use function gen_add_imm/gen_sub_imm
|
||||
if (dword) {
|
||||
gen_mov_dword_to_reg_imm(temp2, imm);
|
||||
} else {
|
||||
gen_mov_word_to_reg_imm(temp2, (Bit16u)imm);
|
||||
}
|
||||
cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2
|
||||
gen_mov_word_from_reg_helper(temp3, dest, dword, temp1);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
cache_addd( ADD_REG_LSL_IMM(dest_reg, dest_reg, scale_reg, scale) ); // add dest_reg, dest_reg, scale_reg, lsl #(scale)
|
||||
gen_add_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (scale) {
|
||||
cache_addd( MOV_REG_LSL_IMM(dest_reg, dest_reg, scale) ); // mov dest_reg, dest_reg, lsl #(scale)
|
||||
}
|
||||
gen_add_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
// generate a call to a parameterless function
|
||||
static void INLINE gen_call_function_raw(void * func) {
|
||||
cache_addd( LDR_IMM(temp1, HOST_pc, 4) ); // ldr temp1, [pc, #4]
|
||||
cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4
|
||||
cache_addd( BX(temp1) ); // bx temp1
|
||||
cache_addd((Bit32u)func); // .int func
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Bit32u proc_addr = (Bit32u)cache.pos;
|
||||
gen_call_function_raw(func);
|
||||
return proc_addr;
|
||||
}
|
||||
|
||||
#if (1)
|
||||
// max of 4 parameters in a1-a4
|
||||
|
||||
// load an immediate value as param'th function parameter
|
||||
static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
|
||||
gen_mov_dword_to_reg_imm(param, imm);
|
||||
}
|
||||
|
||||
// load an address as param'th function parameter
|
||||
static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
|
||||
gen_mov_dword_to_reg_imm(param, addr);
|
||||
}
|
||||
|
||||
// load a host-register as param'th function parameter
|
||||
static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
|
||||
gen_mov_regs(param, reg);
|
||||
}
|
||||
|
||||
// load a value from memory as param'th function parameter
|
||||
static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
|
||||
gen_mov_word_to_reg(param, (void *)mem, 1);
|
||||
}
|
||||
#else
|
||||
other arm abis
|
||||
#endif
|
||||
|
||||
// jump to an address pointed at by ptr, offset is in imm
|
||||
static void gen_jmp_ptr(void * ptr,Bits imm=0) {
|
||||
Bits scale;
|
||||
Bitu imm2;
|
||||
gen_mov_word_to_reg(temp3, ptr, 1);
|
||||
|
||||
if (imm) {
|
||||
scale = 0;
|
||||
imm2 = (Bitu)imm;
|
||||
while (imm2) {
|
||||
while ((imm2 & 3) == 0) {
|
||||
imm2>>=2;
|
||||
scale+=2;
|
||||
}
|
||||
cache_addd( ADD_IMM(temp3, temp3, imm2 & 0xff, ROTATE_SCALE(scale)) ); // add temp3, temp3, #((imm2 & 0xff) << scale)
|
||||
imm2>>=8;
|
||||
scale+=8;
|
||||
}
|
||||
}
|
||||
|
||||
#if (1)
|
||||
// (*ptr) should be word aligned
|
||||
if ((imm & 0x03) == 0) {
|
||||
cache_addd( LDR_IMM(temp1, temp3, 0) ); // ldr temp1, [temp3]
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
cache_addd( LDRB_IMM(temp1, temp3, 0) ); // ldrb temp1, [temp3]
|
||||
cache_addd( LDRB_IMM(temp2, temp3, 1) ); // ldrb temp2, [temp3, #1]
|
||||
cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 8) ); // orr temp1, temp1, temp2, lsl #8
|
||||
cache_addd( LDRB_IMM(temp2, temp3, 2) ); // ldrb temp2, [temp3, #2]
|
||||
cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 16) ); // orr temp1, temp1, temp2, lsl #16
|
||||
cache_addd( LDRB_IMM(temp2, temp3, 3) ); // ldrb temp2, [temp3, #3]
|
||||
cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 24) ); // orr temp1, temp1, temp2, lsl #24
|
||||
}
|
||||
|
||||
cache_addd( BX(temp1) ); // bx temp1
|
||||
}
|
||||
|
||||
// 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_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
|
||||
} else {
|
||||
cache_addd( MOVS_REG_LSL_IMM(temp1, reg, 16) ); // movs temp1, reg, lsl #16
|
||||
}
|
||||
cache_addd( BEQ_FWD(0) ); // beq j
|
||||
return ((Bit32u)cache.pos-4);
|
||||
}
|
||||
|
||||
// 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_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
|
||||
} else {
|
||||
cache_addd( MOVS_REG_LSL_IMM(temp1, reg, 16) ); // movs temp1, reg, lsl #16
|
||||
}
|
||||
cache_addd( BNE_FWD(0) ); // bne j
|
||||
return ((Bit32u)cache.pos-4);
|
||||
}
|
||||
|
||||
// calculate relative offset and fill it into the location pointed to by data
|
||||
static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
|
||||
#if C_DEBUG
|
||||
Bits len=(Bit32u)cache.pos-(data+8);
|
||||
if (len<0) len=-len;
|
||||
if (len>0x02000000) LOG_MSG("Big jump %d",len);
|
||||
#endif
|
||||
*(Bit32u*)data=( (*(Bit32u*)data) & 0xff000000 ) | ( ( ((Bit32u)cache.pos - (data+8)) >> 2 ) & 0x00ffffff );
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (isdword) {
|
||||
cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
|
||||
} else {
|
||||
cache_addd( TST_IMM(reg, 0xff, 0) ); // tst reg, #0xff
|
||||
}
|
||||
cache_addd( BEQ_FWD(8) ); // beq nobranch (pc +8)
|
||||
cache_addd( LDR_IMM(temp1, HOST_pc, 0) ); // ldr temp1, [pc, #0]
|
||||
cache_addd( BX(temp1) ); // bx temp1
|
||||
cache_addd(0); // fill j
|
||||
// nobranch:
|
||||
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_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
|
||||
cache_addd( BGT_FWD(8) ); // bgt nobranch (pc+8)
|
||||
cache_addd( LDR_IMM(temp1, HOST_pc, 0) ); // ldr temp1, [pc, #0]
|
||||
cache_addd( BX(temp1) ); // bx temp1
|
||||
cache_addd(0); // fill j
|
||||
// nobranch:
|
||||
return ((Bit32u)cache.pos-4);
|
||||
}
|
||||
|
||||
// calculate long relative offset and fill it into the location pointed to by data
|
||||
static void INLINE gen_fill_branch_long(Bit32u data) {
|
||||
// this is an absolute branch
|
||||
*(Bit32u*)data=(Bit32u)cache.pos;
|
||||
}
|
||||
|
||||
static void gen_run_code(void) {
|
||||
cache_addd(0xe92d4000); // stmfd sp!, {lr}
|
||||
cache_addd(0xe92d0cf0); // stmfd sp!, {v1-v4,v7,v8}
|
||||
|
||||
// adr: 8
|
||||
cache_addd( LDR_IMM(FC_SEGS_ADDR, HOST_pc, 64 - (8 + 8)) ); // ldr FC_SEGS_ADDR, [pc, #(&Segs)]
|
||||
// adr: 12
|
||||
cache_addd( LDR_IMM(FC_REGS_ADDR, HOST_pc, 68 - (12 + 8)) ); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)]
|
||||
|
||||
cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4
|
||||
cache_addd(0xe92d4000); // stmfd sp!, {lr}
|
||||
cache_addd( BX(HOST_r0) ); // bx r0
|
||||
|
||||
cache_addd(0xe8bd0cf0); // ldmfd sp!, {v1-v4,v7,v8}
|
||||
|
||||
cache_addd(0xe8bd4000); // ldmfd sp!, {lr}
|
||||
cache_addd( BX(HOST_lr) ); // bx lr
|
||||
|
||||
// fill up to 64 bytes
|
||||
cache_addd( NOP ); // nop
|
||||
cache_addd( NOP ); // nop
|
||||
cache_addd( NOP ); // nop
|
||||
cache_addd( NOP ); // nop
|
||||
cache_addd( NOP ); // nop
|
||||
cache_addd( NOP ); // nop
|
||||
|
||||
// adr: 64
|
||||
cache_addd((Bit32u)&Segs); // address of "Segs"
|
||||
// adr: 68
|
||||
cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs"
|
||||
}
|
||||
|
||||
// return from a function
|
||||
static void gen_return_function(void) {
|
||||
cache_addd(0xe8bd4000); // ldmfd sp!, {lr}
|
||||
cache_addd( BX(HOST_lr) ); // bx lr
|
||||
}
|
||||
|
||||
#ifdef DRC_FLAGS_INVALIDATION
|
||||
|
||||
// called when a call to a function can be replaced by a
|
||||
// call to a simpler function
|
||||
static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
|
||||
#ifdef DRC_FLAGS_INVALIDATION_DCODE
|
||||
// try to avoid function calls but rather directly fill in code
|
||||
switch (flags_type) {
|
||||
case t_ADDb:
|
||||
case t_ADDw:
|
||||
case t_ADDd:
|
||||
*(Bit32u*)pos=ADD_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // add FC_RETOP, a1, a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_ORb:
|
||||
case t_ORw:
|
||||
case t_ORd:
|
||||
*(Bit32u*)pos=ORR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // orr FC_RETOP, a1, a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_ANDb:
|
||||
case t_ANDw:
|
||||
case t_ANDd:
|
||||
*(Bit32u*)pos=AND_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // and FC_RETOP, a1, a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SUBb:
|
||||
case t_SUBw:
|
||||
case t_SUBd:
|
||||
*(Bit32u*)pos=SUB_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // sub FC_RETOP, a1, a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_XORb:
|
||||
case t_XORw:
|
||||
case t_XORd:
|
||||
*(Bit32u*)pos=EOR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // eor FC_RETOP, a1, a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_CMPb:
|
||||
case t_CMPw:
|
||||
case t_CMPd:
|
||||
case t_TESTb:
|
||||
case t_TESTw:
|
||||
case t_TESTd:
|
||||
*(Bit32u*)pos=B_FWD(8); // b (pc+2*4)
|
||||
break;
|
||||
case t_INCb:
|
||||
case t_INCw:
|
||||
case t_INCd:
|
||||
*(Bit32u*)pos=ADD_IMM(FC_RETOP, HOST_a1, 1, 0); // add FC_RETOP, a1, #1
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_DECb:
|
||||
case t_DECw:
|
||||
case t_DECd:
|
||||
*(Bit32u*)pos=SUB_IMM(FC_RETOP, HOST_a1, 1, 0); // sub FC_RETOP, a1, #1
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SHLb:
|
||||
case t_SHLw:
|
||||
case t_SHLd:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsl a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SHRb:
|
||||
*(Bit32u*)pos=AND_IMM(FC_RETOP, HOST_a1, 0xff, 0); // and FC_RETOP, a1, #0xff
|
||||
*(Bit32u*)(pos+4)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SHRw:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
|
||||
*(Bit32u*)(pos+4)=MOV_REG_LSR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, lsr #16
|
||||
*(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SHRd:
|
||||
*(Bit32u*)pos=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SARb:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24
|
||||
*(Bit32u*)(pos+4)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 24); // mov FC_RETOP, FC_RETOP, asr #24
|
||||
*(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SARw:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
|
||||
*(Bit32u*)(pos+4)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, asr #16
|
||||
*(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_SARd:
|
||||
*(Bit32u*)pos=MOV_REG_ASR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, asr a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_RORb:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24
|
||||
*(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 8); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #8
|
||||
*(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16
|
||||
*(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2
|
||||
break;
|
||||
case t_RORw:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
|
||||
*(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16
|
||||
*(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_RORd:
|
||||
*(Bit32u*)pos=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_ROLw:
|
||||
*(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
|
||||
*(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32
|
||||
*(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16
|
||||
*(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2
|
||||
break;
|
||||
case t_ROLd:
|
||||
*(Bit32u*)pos=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32
|
||||
*(Bit32u*)(pos+4)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
case t_NEGb:
|
||||
case t_NEGw:
|
||||
case t_NEGd:
|
||||
*(Bit32u*)pos=RSB_IMM(FC_RETOP, HOST_a1, 0, 0); // rsb FC_RETOP, a1, #0
|
||||
*(Bit32u*)(pos+4)=NOP; // nop
|
||||
*(Bit32u*)(pos+8)=NOP; // nop
|
||||
*(Bit32u*)(pos+12)=NOP; // nop
|
||||
break;
|
||||
default:
|
||||
*(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func
|
||||
break;
|
||||
|
||||
}
|
||||
#else
|
||||
*(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cache_block_before_close(void) { }
|
||||
|
||||
#ifdef DRC_USE_SEGS_ADDR
|
||||
|
||||
// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
|
||||
cache_addd( LDRH_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldrh dest_reg, [FC_SEGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
|
||||
cache_addd( LDR_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldr dest_reg, [FC_SEGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
|
||||
cache_addd( LDR_IMM(temp1, FC_SEGS_ADDR, index) ); // ldr temp1, [FC_SEGS_ADDR, #index]
|
||||
cache_addd( ADD_REG_LSL_IMM(reg, reg, temp1, 0) ); // add reg, reg, temp1
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DRC_USE_REGS_ADDR
|
||||
|
||||
// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
|
||||
cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
|
||||
cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
|
||||
if (dword) {
|
||||
cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index]
|
||||
} else {
|
||||
cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
}
|
||||
|
||||
// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
|
||||
cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
|
||||
cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
|
||||
// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
|
||||
cache_addd( LDR_IMM(temp2, FC_REGS_ADDR, index) ); // ldr temp2, [FC_REGS_ADDR, #index]
|
||||
cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2
|
||||
}
|
||||
|
||||
|
||||
// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
|
||||
static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
|
||||
cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
|
||||
cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
|
||||
static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
|
||||
if (dword) {
|
||||
cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index]
|
||||
} else {
|
||||
cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
}
|
||||
|
||||
// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
|
||||
static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
|
||||
cache_addd( STRB_IMM(src_reg, FC_REGS_ADDR, index) ); // strb src_reg, [FC_REGS_ADDR, #index]
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* ARMv4/ARMv7 (little endian) backend (switcher) by M-HT */
|
||||
|
||||
#include "risc_armv4le-common.h"
|
||||
|
||||
// choose your destiny:
|
||||
#if C_TARGETCPU == ARMV7LE
|
||||
#include "risc_armv4le-o3.h"
|
||||
#else
|
||||
#if defined(__THUMB_INTERWORK__)
|
||||
#include "risc_armv4le-thumb-iw.h"
|
||||
#else
|
||||
#include "risc_armv4le-o3.h"
|
||||
// #include "risc_armv4le-thumb-niw.h"
|
||||
// #include "risc_armv4le-thumb.h"
|
||||
#endif
|
||||
#endif
|
@ -1,750 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* MIPS32 (little endian) backend by crazyc */
|
||||
|
||||
|
||||
// some configuring defines that specify the capabilities of this architecture
|
||||
// or aspects of the recompiling
|
||||
|
||||
// protect FC_ADDR over function calls if necessaray
|
||||
// #define DRC_PROTECT_ADDR_REG
|
||||
|
||||
// try to use non-flags generating functions if possible
|
||||
#define DRC_FLAGS_INVALIDATION
|
||||
// try to replace _simple functions by code
|
||||
#define DRC_FLAGS_INVALIDATION_DCODE
|
||||
|
||||
// type with the same size as a pointer
|
||||
#define DRC_PTR_SIZE_IM Bit32u
|
||||
|
||||
// calling convention modifier
|
||||
#define DRC_CALL_CONV /* nothing */
|
||||
#define DRC_FC /* nothing */
|
||||
|
||||
// use FC_REGS_ADDR to hold the address of "cpu_regs" and to access it using FC_REGS_ADDR
|
||||
//#define DRC_USE_REGS_ADDR
|
||||
// use FC_SEGS_ADDR to hold the address of "Segs" and to access it using FC_SEGS_ADDR
|
||||
//#define DRC_USE_SEGS_ADDR
|
||||
|
||||
// register mapping
|
||||
typedef Bit8u HostReg;
|
||||
|
||||
#define HOST_v0 2
|
||||
#define HOST_v1 3
|
||||
#define HOST_a0 4
|
||||
#define HOST_a1 5
|
||||
#define HOST_t4 12
|
||||
#define HOST_t5 13
|
||||
#define HOST_t6 14
|
||||
#define HOST_t7 15
|
||||
#define HOST_s0 16
|
||||
#define HOST_t8 24
|
||||
#define HOST_t9 25
|
||||
#define temp1 HOST_v1
|
||||
#define temp2 HOST_t9
|
||||
|
||||
// register that holds function return values
|
||||
#define FC_RETOP HOST_v0
|
||||
|
||||
// register used for address calculations,
|
||||
#define FC_ADDR HOST_s0 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
|
||||
|
||||
// register that holds the first parameter
|
||||
#define FC_OP1 HOST_a0
|
||||
|
||||
// register that holds the second parameter
|
||||
#define FC_OP2 HOST_a1
|
||||
|
||||
// special register that holds the third parameter for _R3 calls (byte accessible)
|
||||
#define FC_OP3 HOST_???
|
||||
|
||||
// register that holds byte-accessible temporary values
|
||||
#define FC_TMP_BA1 HOST_t5
|
||||
|
||||
// register that holds byte-accessible temporary values
|
||||
#define FC_TMP_BA2 HOST_t6
|
||||
|
||||
// temporary register for LEA
|
||||
#define TEMP_REG_DRC HOST_t7
|
||||
|
||||
#ifdef DRC_USE_REGS_ADDR
|
||||
// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
|
||||
#define FC_REGS_ADDR HOST_???
|
||||
#endif
|
||||
|
||||
#ifdef DRC_USE_SEGS_ADDR
|
||||
// used to hold the address of "Segs" - preferably filled in function gen_run_code
|
||||
#define FC_SEGS_ADDR HOST_???
|
||||
#endif
|
||||
|
||||
// save some state to improve code gen
|
||||
static bool temp1_valid = false;
|
||||
static Bit32u temp1_value;
|
||||
|
||||
// move a full register from reg_src to reg_dst
|
||||
static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
|
||||
if(reg_src == reg_dst) return;
|
||||
cache_addw((reg_dst<<11)+0x21); // addu reg_dst, $0, reg_src
|
||||
cache_addw(reg_src);
|
||||
}
|
||||
|
||||
// move a 32bit constant value into dest_reg
|
||||
static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
|
||||
if(imm < 65536) {
|
||||
cache_addw((Bit16u)imm); // ori dest_reg, $0, imm
|
||||
cache_addw(0x3400+dest_reg);
|
||||
} else if(((Bit32s)imm < 0) && ((Bit32s)imm >= -32768)) {
|
||||
cache_addw((Bit16u)imm); // addiu dest_reg, $0, imm
|
||||
cache_addw(0x2400+dest_reg);
|
||||
} else if(!(imm & 0xffff)) {
|
||||
cache_addw((Bit16u)(imm >> 16)); // lui dest_reg, %hi(imm)
|
||||
cache_addw(0x3c00+dest_reg);
|
||||
} else {
|
||||
cache_addw((Bit16u)(imm >> 16)); // lui dest_reg, %hi(imm)
|
||||
cache_addw(0x3c00+dest_reg);
|
||||
cache_addw((Bit16u)imm); // ori dest_reg, dest_reg, %lo(imm)
|
||||
cache_addw(0x3400+(dest_reg<<5)+dest_reg);
|
||||
}
|
||||
}
|
||||
|
||||
// this is the only place temp1 should be modified
|
||||
static void INLINE mov_imm_to_temp1(Bit32u imm) {
|
||||
if (temp1_valid && (temp1_value == imm)) return;
|
||||
gen_mov_dword_to_reg_imm(temp1, imm);
|
||||
temp1_valid = true;
|
||||
temp1_value = imm;
|
||||
}
|
||||
|
||||
static Bit16s gen_addr_temp1(Bit32u addr) {
|
||||
Bit32u hihalf = addr & 0xffff0000;
|
||||
Bit16s lohalf = addr & 0xffff;
|
||||
if (lohalf > 32764) { // [l,s]wl will overflow
|
||||
hihalf = addr;
|
||||
lohalf = 0;
|
||||
} else if(lohalf < 0) hihalf += 0x10000;
|
||||
mov_imm_to_temp1(hihalf);
|
||||
return lohalf;
|
||||
}
|
||||
|
||||
// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
|
||||
Bit16s lohalf = gen_addr_temp1((Bit32u)data);
|
||||
// alignment....
|
||||
if (dword) {
|
||||
if ((Bit32u)data & 3) {
|
||||
cache_addw(lohalf+3); // lwl dest_reg, 3(temp1)
|
||||
cache_addw(0x8800+(temp1<<5)+dest_reg);
|
||||
cache_addw(lohalf); // lwr dest_reg, 0(temp1)
|
||||
cache_addw(0x9800+(temp1<<5)+dest_reg);
|
||||
} else {
|
||||
cache_addw(lohalf); // lw dest_reg, 0(temp1)
|
||||
cache_addw(0x8C00+(temp1<<5)+dest_reg);
|
||||
}
|
||||
} else {
|
||||
if ((Bit32u)data & 1) {
|
||||
cache_addw(lohalf); // lbu dest_reg, 0(temp1)
|
||||
cache_addw(0x9000+(temp1<<5)+dest_reg);
|
||||
cache_addw(lohalf+1); // lbu temp2, 1(temp1)
|
||||
cache_addw(0x9000+(temp1<<5)+temp2);
|
||||
#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
|
||||
cache_addw(0x7a04); // ins dest_reg, temp2, 8, 8
|
||||
cache_addw(0x7c00+(temp2<<5)+dest_reg);
|
||||
#else
|
||||
cache_addw((temp2<<11)+0x200); // sll temp2, temp2, 8
|
||||
cache_addw(temp2);
|
||||
cache_addw((dest_reg<<11)+0x25); // or dest_reg, temp2, dest_reg
|
||||
cache_addw((temp2<<5)+dest_reg);
|
||||
#endif
|
||||
} else {
|
||||
cache_addw(lohalf); // lhu dest_reg, 0(temp1);
|
||||
cache_addw(0x9400+(temp1<<5)+dest_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move a 16bit constant value into dest_reg
|
||||
// the upper 16bit of the destination register may be destroyed
|
||||
static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
|
||||
cache_addw(imm); // ori dest_reg, $0, imm
|
||||
cache_addw(0x3400+dest_reg);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Bit16s lohalf = gen_addr_temp1((Bit32u)dest);
|
||||
// alignment....
|
||||
if (dword) {
|
||||
if ((Bit32u)dest & 3) {
|
||||
cache_addw(lohalf+3); // swl src_reg, 3(temp1)
|
||||
cache_addw(0xA800+(temp1<<5)+src_reg);
|
||||
cache_addw(lohalf); // swr src_reg, 0(temp1)
|
||||
cache_addw(0xB800+(temp1<<5)+src_reg);
|
||||
} else {
|
||||
cache_addw(lohalf); // sw src_reg, 0(temp1)
|
||||
cache_addw(0xAC00+(temp1<<5)+src_reg);
|
||||
}
|
||||
} else {
|
||||
if((Bit32u)dest & 1) {
|
||||
cache_addw(lohalf); // sb src_reg, 0(temp1)
|
||||
cache_addw(0xA000+(temp1<<5)+src_reg);
|
||||
cache_addw((temp2<<11)+0x202); // srl temp2, src_reg, 8
|
||||
cache_addw(src_reg);
|
||||
cache_addw(lohalf+1); // sb temp2, 1(temp1)
|
||||
cache_addw(0xA000+(temp1<<5)+temp2);
|
||||
} else {
|
||||
cache_addw(lohalf); // sh src_reg, 0(temp1);
|
||||
cache_addw(0xA400+(temp1<<5)+src_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move an 8bit value from memory into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
|
||||
Bit16s lohalf = gen_addr_temp1((Bit32u)data);
|
||||
cache_addw(lohalf); // lbu dest_reg, 0(temp1)
|
||||
cache_addw(0x9000+(temp1<<5)+dest_reg);
|
||||
}
|
||||
|
||||
// move an 8bit value from memory into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
|
||||
gen_mov_byte_to_reg_low(dest_reg, data);
|
||||
}
|
||||
|
||||
// move an 8bit constant value into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
|
||||
gen_mov_word_to_reg_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
// move an 8bit constant value into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
|
||||
gen_mov_byte_to_reg_low_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
// move the lowest 8bit of a register into memory
|
||||
static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
|
||||
Bit16s lohalf = gen_addr_temp1((Bit32u)dest);
|
||||
cache_addw(lohalf); // sb src_reg, 0(temp1)
|
||||
cache_addw(0xA000+(temp1<<5)+src_reg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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) {
|
||||
if (sign) {
|
||||
#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
|
||||
cache_addw((reg<<11)+0x420); // seb reg, reg
|
||||
cache_addw(0x7c00+reg);
|
||||
#else
|
||||
arch that lacks seb
|
||||
#endif
|
||||
} else {
|
||||
cache_addw(0xff); // andi reg, reg, 0xff
|
||||
cache_addw(0x3000+(reg<<5)+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) {
|
||||
if (sign) {
|
||||
#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
|
||||
cache_addw((reg<<11)+0x620); // seh reg, reg
|
||||
cache_addw(0x7c00+reg);
|
||||
#else
|
||||
arch that lacks seh
|
||||
#endif
|
||||
} else {
|
||||
cache_addw(0xffff); // andi reg, reg, 0xffff
|
||||
cache_addw(0x3000+(reg<<5)+reg);
|
||||
}
|
||||
}
|
||||
|
||||
// add a 32bit value from memory to a full register
|
||||
static void gen_add(HostReg reg,void* op) {
|
||||
gen_mov_word_to_reg(temp2, op, 1);
|
||||
cache_addw((reg<<11)+0x21); // addu reg, reg, temp2
|
||||
cache_addw((reg<<5)+temp2);
|
||||
}
|
||||
|
||||
// add a 32bit constant value to a full register
|
||||
static void gen_add_imm(HostReg reg,Bit32u imm) {
|
||||
if(!imm) return;
|
||||
if(((Bit32s)imm >= -32768) && ((Bit32s)imm < 32768)) {
|
||||
cache_addw((Bit16u)imm); // addiu reg, reg, imm
|
||||
cache_addw(0x2400+(reg<<5)+reg);
|
||||
} else {
|
||||
mov_imm_to_temp1(imm);
|
||||
cache_addw((reg<<11)+0x21); // addu reg, reg, temp1
|
||||
cache_addw((reg<<5)+temp1);
|
||||
}
|
||||
}
|
||||
|
||||
// and a 32bit constant value with a full register
|
||||
static void gen_and_imm(HostReg reg,Bit32u imm) {
|
||||
if(imm < 65536) {
|
||||
cache_addw((Bit16u)imm); // andi reg, reg, imm
|
||||
cache_addw(0x3000+(reg<<5)+reg);
|
||||
} else {
|
||||
mov_imm_to_temp1((Bit32u)imm);
|
||||
cache_addw((reg<<11)+0x24); // and reg, temp1, reg
|
||||
cache_addw((temp1<<5)+reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// move a 32bit constant value into memory
|
||||
static void INLINE gen_mov_direct_dword(void* dest,Bit32u imm) {
|
||||
gen_mov_dword_to_reg_imm(temp2, imm);
|
||||
gen_mov_word_from_reg(temp2, dest, 1);
|
||||
}
|
||||
|
||||
// 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 a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
|
||||
static void INLINE gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
|
||||
if(!imm) return;
|
||||
gen_mov_word_to_reg(temp2, dest, dword);
|
||||
gen_add_imm(temp2, imm);
|
||||
gen_mov_word_from_reg(temp2, dest, dword);
|
||||
}
|
||||
|
||||
// add an 8bit constant value to a dword memory value
|
||||
static void INLINE gen_add_direct_byte(void* dest,Bit8s imm) {
|
||||
gen_add_direct_word(dest, (Bit32s)imm, 1);
|
||||
}
|
||||
|
||||
// subtract an 8bit constant value from a dword memory value
|
||||
static void INLINE gen_sub_direct_byte(void* dest,Bit8s imm) {
|
||||
gen_add_direct_word(dest, -((Bit32s)imm), 1);
|
||||
}
|
||||
|
||||
// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
|
||||
static void INLINE gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
|
||||
gen_add_direct_word(dest, -(Bit32s)imm, dword);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (scale) {
|
||||
cache_addw((scale_reg<<11)+(scale<<6)); // sll scale_reg, scale_reg, scale
|
||||
cache_addw(scale_reg);
|
||||
}
|
||||
cache_addw((dest_reg<<11)+0x21); // addu dest_reg, dest_reg, scale_reg
|
||||
cache_addw((dest_reg<<5)+scale_reg);
|
||||
gen_add_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (scale) {
|
||||
cache_addw((dest_reg<<11)+(scale<<6)); // sll dest_reg, dest_reg, scale
|
||||
cache_addw(dest_reg);
|
||||
}
|
||||
gen_add_imm(dest_reg, imm);
|
||||
}
|
||||
|
||||
#define DELAY cache_addd(0) // nop
|
||||
|
||||
// generate a call to a parameterless function
|
||||
static void INLINE gen_call_function_raw(void * func) {
|
||||
#if C_DEBUG
|
||||
if ((cache.pos ^ func) & 0xf0000000) LOG_MSG("jump overflow\n");
|
||||
#endif
|
||||
temp1_valid = false;
|
||||
cache_addd(0x0c000000+(((Bit32u)func>>2)&0x3ffffff)); // jal func
|
||||
DELAY;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Bit32u proc_addr = (Bit32u)cache.pos;
|
||||
gen_call_function_raw(func);
|
||||
return proc_addr;
|
||||
}
|
||||
|
||||
#ifdef __mips_eabi
|
||||
// max of 8 parameters in $a0-$a3 and $t0-$t3
|
||||
|
||||
// load an immediate value as param'th function parameter
|
||||
static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
|
||||
gen_mov_dword_to_reg_imm(param+4, imm);
|
||||
}
|
||||
|
||||
// load an address as param'th function parameter
|
||||
static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
|
||||
gen_mov_dword_to_reg_imm(param+4, addr);
|
||||
}
|
||||
|
||||
// load a host-register as param'th function parameter
|
||||
static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
|
||||
gen_mov_regs(param+4, reg);
|
||||
}
|
||||
|
||||
// load a value from memory as param'th function parameter
|
||||
static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
|
||||
gen_mov_word_to_reg(param+4, (void *)mem, 1);
|
||||
}
|
||||
#else
|
||||
other mips abis
|
||||
#endif
|
||||
|
||||
// jump to an address pointed at by ptr, offset is in imm
|
||||
static void INLINE gen_jmp_ptr(void * ptr,Bits imm=0) {
|
||||
gen_mov_word_to_reg(temp2, ptr, 1);
|
||||
if((imm < -32768) || (imm >= 32768)) {
|
||||
gen_add_imm(temp2, imm);
|
||||
imm = 0;
|
||||
}
|
||||
temp1_valid = false;
|
||||
cache_addw((Bit16u)imm); // lw temp2, imm(temp2)
|
||||
cache_addw(0x8C00+(temp2<<5)+temp2);
|
||||
cache_addd((temp2<<21)+8); // jr temp2
|
||||
DELAY;
|
||||
}
|
||||
|
||||
// short conditional jump (+-127 bytes) if register is zero
|
||||
// the destination is set by gen_fill_branch() later
|
||||
static Bit32u INLINE gen_create_branch_on_zero(HostReg reg,bool dword) {
|
||||
temp1_valid = false;
|
||||
if(!dword) {
|
||||
cache_addw(0xffff); // andi temp1, reg, 0xffff
|
||||
cache_addw(0x3000+(reg<<5)+temp1);
|
||||
}
|
||||
cache_addw(0); // beq $0, reg, 0
|
||||
cache_addw(0x1000+(dword?reg:temp1));
|
||||
DELAY;
|
||||
return ((Bit32u)cache.pos-8);
|
||||
}
|
||||
|
||||
// short conditional jump (+-127 bytes) if register is nonzero
|
||||
// the destination is set by gen_fill_branch() later
|
||||
static Bit32u INLINE gen_create_branch_on_nonzero(HostReg reg,bool dword) {
|
||||
temp1_valid = false;
|
||||
if(!dword) {
|
||||
cache_addw(0xffff); // andi temp1, reg, 0xffff
|
||||
cache_addw(0x3000+(reg<<5)+temp1);
|
||||
}
|
||||
cache_addw(0); // bne $0, reg, 0
|
||||
cache_addw(0x1400+(dword?reg:temp1));
|
||||
DELAY;
|
||||
return ((Bit32u)cache.pos-8);
|
||||
}
|
||||
|
||||
// calculate relative offset and fill it into the location pointed to by data
|
||||
static void INLINE 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
|
||||
temp1_valid = false; // this is a branch target
|
||||
*(Bit16u*)data=((Bit16u)((Bit32u)cache.pos-data-4)>>2);
|
||||
}
|
||||
|
||||
#if 0 // assume for the moment no branch will go farther then +/- 128KB
|
||||
|
||||
// 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) {
|
||||
temp1_valid = false;
|
||||
if (!isdword) {
|
||||
cache_addw(0xff); // andi temp1, reg, 0xff
|
||||
cache_addw(0x3000+(reg<<5)+temp1);
|
||||
}
|
||||
cache_addw(3); // beq $0, reg, +12
|
||||
cache_addw(0x1000+(isdword?reg:temp1));
|
||||
DELAY;
|
||||
cache_addd(0x00000000); // fill j
|
||||
DELAY;
|
||||
return ((Bit32u)cache.pos-8);
|
||||
}
|
||||
|
||||
// compare 32bit-register against zero and jump if value less/equal than zero
|
||||
static Bit32u INLINE gen_create_branch_long_leqzero(HostReg reg) {
|
||||
temp1_valid = false;
|
||||
cache_addw(3); // bgtz reg, +12
|
||||
cache_addw(0x1c00+(reg<<5));
|
||||
DELAY;
|
||||
cache_addd(0x00000000); // fill j
|
||||
DELAY;
|
||||
return ((Bit32u)cache.pos-8);
|
||||
}
|
||||
|
||||
// calculate long relative offset and fill it into the location pointed to by data
|
||||
static void INLINE gen_fill_branch_long(Bit32u data) {
|
||||
temp1_valid = false;
|
||||
// this is an absolute branch
|
||||
*(Bit32u*)data=0x08000000+(((Bit32u)cache.pos>>2)&0x3ffffff);
|
||||
}
|
||||
#else
|
||||
// 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) {
|
||||
temp1_valid = false;
|
||||
if (!isdword) {
|
||||
cache_addw(0xff); // andi temp1, reg, 0xff
|
||||
cache_addw(0x3000+(reg<<5)+temp1);
|
||||
}
|
||||
cache_addw(0); // bne $0, reg, 0
|
||||
cache_addw(0x1400+(isdword?reg:temp1));
|
||||
DELAY;
|
||||
return ((Bit32u)cache.pos-8);
|
||||
}
|
||||
|
||||
// compare 32bit-register against zero and jump if value less/equal than zero
|
||||
static Bit32u INLINE gen_create_branch_long_leqzero(HostReg reg) {
|
||||
temp1_valid = false;
|
||||
cache_addw(0); // blez reg, 0
|
||||
cache_addw(0x1800+(reg<<5));
|
||||
DELAY;
|
||||
return ((Bit32u)cache.pos-8);
|
||||
}
|
||||
|
||||
// calculate long relative offset and fill it into the location pointed to by data
|
||||
static void INLINE gen_fill_branch_long(Bit32u data) {
|
||||
gen_fill_branch(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gen_run_code(void) {
|
||||
temp1_valid = false;
|
||||
cache_addd(0x27bdfff0); // addiu $sp, $sp, -16
|
||||
cache_addd(0xafb00004); // sw $s0, 4($sp)
|
||||
cache_addd(0x00800008); // jr $a0
|
||||
cache_addd(0xafbf0000); // sw $ra, 0($sp)
|
||||
}
|
||||
|
||||
// return from a function
|
||||
static void gen_return_function(void) {
|
||||
temp1_valid = false;
|
||||
cache_addd(0x8fbf0000); // lw $ra, 0($sp)
|
||||
cache_addd(0x8fb00004); // lw $s0, 4($sp)
|
||||
cache_addd(0x03e00008); // jr $ra
|
||||
cache_addd(0x27bd0010); // addiu $sp, $sp, 16
|
||||
}
|
||||
|
||||
#ifdef DRC_FLAGS_INVALIDATION
|
||||
// called when a call to a function can be replaced by a
|
||||
// call to a simpler function
|
||||
static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
|
||||
#ifdef DRC_FLAGS_INVALIDATION_DCODE
|
||||
// try to avoid function calls but rather directly fill in code
|
||||
switch (flags_type) {
|
||||
case t_ADDb:
|
||||
case t_ADDw:
|
||||
case t_ADDd:
|
||||
*(Bit32u*)pos=0x00851021; // addu $v0, $a0, $a1
|
||||
break;
|
||||
case t_ORb:
|
||||
case t_ORw:
|
||||
case t_ORd:
|
||||
*(Bit32u*)pos=0x00851025; // or $v0, $a0, $a1
|
||||
break;
|
||||
case t_ANDb:
|
||||
case t_ANDw:
|
||||
case t_ANDd:
|
||||
*(Bit32u*)pos=0x00851024; // and $v0, $a0, $a1
|
||||
break;
|
||||
case t_SUBb:
|
||||
case t_SUBw:
|
||||
case t_SUBd:
|
||||
*(Bit32u*)pos=0x00851023; // subu $v0, $a0, $a1
|
||||
break;
|
||||
case t_XORb:
|
||||
case t_XORw:
|
||||
case t_XORd:
|
||||
*(Bit32u*)pos=0x00851026; // xor $v0, $a0, $a1
|
||||
break;
|
||||
case t_CMPb:
|
||||
case t_CMPw:
|
||||
case t_CMPd:
|
||||
case t_TESTb:
|
||||
case t_TESTw:
|
||||
case t_TESTd:
|
||||
*(Bit32u*)pos=0; // nop
|
||||
break;
|
||||
case t_INCb:
|
||||
case t_INCw:
|
||||
case t_INCd:
|
||||
*(Bit32u*)pos=0x24820001; // addiu $v0, $a0, 1
|
||||
break;
|
||||
case t_DECb:
|
||||
case t_DECw:
|
||||
case t_DECd:
|
||||
*(Bit32u*)pos=0x2482ffff; // addiu $v0, $a0, -1
|
||||
break;
|
||||
case t_SHLb:
|
||||
case t_SHLw:
|
||||
case t_SHLd:
|
||||
*(Bit32u*)pos=0x00a41004; // sllv $v0, $a0, $a1
|
||||
break;
|
||||
case t_SHRb:
|
||||
case t_SHRw:
|
||||
case t_SHRd:
|
||||
*(Bit32u*)pos=0x00a41006; // srlv $v0, $a0, $a1
|
||||
break;
|
||||
case t_SARd:
|
||||
*(Bit32u*)pos=0x00a41007; // srav $v0, $a0, $a1
|
||||
break;
|
||||
#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
|
||||
case t_RORd:
|
||||
*(Bit32u*)pos=0x00a41046; // rotr $v0, $a0, $a1
|
||||
break;
|
||||
#endif
|
||||
case t_NEGb:
|
||||
case t_NEGw:
|
||||
case t_NEGd:
|
||||
*(Bit32u*)pos=0x00041023; // subu $v0, $0, $a0
|
||||
break;
|
||||
default:
|
||||
*(Bit32u*)pos=0x0c000000+((((Bit32u)fct_ptr)>>2)&0x3ffffff); // jal simple_func
|
||||
break;
|
||||
}
|
||||
#else
|
||||
*(Bit32u*)pos=0x0c000000+(((Bit32u)fct_ptr)>>2)&0x3ffffff); // jal simple_func
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cache_block_closing(Bit8u* block_start,Bitu block_size) {
|
||||
#ifdef PSP
|
||||
// writeback dcache and invalidate icache
|
||||
Bit32u inval_start = ((Bit32u)block_start) & ~63;
|
||||
Bit32u inval_end = (((Bit32u)block_start) + block_size + 64) & ~63;
|
||||
for (;inval_start < inval_end; inval_start+=64) {
|
||||
__builtin_allegrex_cache(0x1a, inval_start);
|
||||
__builtin_allegrex_cache(0x08, inval_start);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cache_block_before_close(void) { }
|
||||
|
||||
|
||||
#ifdef DRC_USE_SEGS_ADDR
|
||||
|
||||
// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DRC_USE_REGS_ADDR
|
||||
|
||||
// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
|
||||
// 16bit moves may destroy the upper 16bit of the destination register
|
||||
static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
|
||||
// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
|
||||
// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
|
||||
static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
|
||||
static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
|
||||
static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
|
||||
// stub
|
||||
}
|
||||
|
||||
// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
|
||||
static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
|
||||
// stub
|
||||
}
|
||||
|
||||
#endif
|
@ -1,730 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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
|
||||
|
||||
// protect FC_ADDR over function calls if necessaray
|
||||
// #define DRC_PROTECT_ADDR_REG
|
||||
|
||||
// try to use non-flags generating functions if possible
|
||||
#define DRC_FLAGS_INVALIDATION
|
||||
// try to replace _simple functions by code
|
||||
#define DRC_FLAGS_INVALIDATION_DCODE
|
||||
|
||||
// type with the same size as a pointer
|
||||
#define DRC_PTR_SIZE_IM Bit64u
|
||||
|
||||
// calling convention modifier
|
||||
#define DRC_CALL_CONV /* nothing */
|
||||
#define DRC_FC /* nothing */
|
||||
|
||||
|
||||
// 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, if the ABI does not
|
||||
// state that this register is preserved across function calls
|
||||
// then define DRC_PROTECT_ADDR_REG above
|
||||
#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
|
||||
|
||||
// special register that holds the third parameter for _R3 calls (byte accessible)
|
||||
#define FC_OP3 HOST_EAX
|
||||
|
||||
// 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_reg_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");
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE void gen_memaddr(Bitu op,void* data,Bitu off) {
|
||||
Bit64s diff;
|
||||
diff = (Bit64s)data-((Bit64s)cache.pos+off+5);
|
||||
if ((diff<0x80000000LL) && (diff>-0x80000000LL)) {
|
||||
// RIP-relative addressing is offset after the instruction
|
||||
cache_addb(op+1);
|
||||
cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL));
|
||||
} else if ((Bit64u)data<0x100000000LL) {
|
||||
cache_addb(op);
|
||||
cache_addb(0x25);
|
||||
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 may destroy 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_reg_memaddr(dest_reg,data);
|
||||
}
|
||||
|
||||
// move a 16bit constant value into dest_reg
|
||||
// the upper 16bit of the destination register may be destroyed
|
||||
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_reg_memaddr(src_reg,dest);
|
||||
}
|
||||
|
||||
// move an 8bit value from memory into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
|
||||
cache_addb(0x8a); // mov reg,[data]
|
||||
gen_reg_memaddr(dest_reg,data);
|
||||
}
|
||||
|
||||
// move an 8bit value from memory into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
|
||||
cache_addb(0x66);
|
||||
cache_addb(0x8b); // mov reg,[data]
|
||||
gen_reg_memaddr(dest_reg,data);
|
||||
}
|
||||
|
||||
// move an 8bit constant value into dest_reg
|
||||
// the upper 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
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 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
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_reg_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_reg_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_addb(0xc7); // mov [data],imm
|
||||
gen_memaddr(0x04,dest,4);
|
||||
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_addb(0x83); // add [data],imm
|
||||
gen_memaddr(0x4,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_addb(0x81); // add [data],imm
|
||||
if (dword) {
|
||||
gen_memaddr(0x4,dest,4); // size of following immediate value
|
||||
cache_addd((Bit32u)imm);
|
||||
} else {
|
||||
gen_memaddr(0x4,dest,2); // size of following immediate value
|
||||
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(0x2c,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_addw(0x81); // sub [data],imm
|
||||
if (dword) {
|
||||
gen_memaddr(0x2c,dest,4); // size of following immediate value
|
||||
cache_addd((Bit32u)imm);
|
||||
} else {
|
||||
gen_memaddr(0x2c,dest,2); // size of following immediate value
|
||||
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+scale_reg*(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+(scale_reg<<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_addw(0xec83);
|
||||
cache_addb(0x08); // sub rsp,0x08 (align stack to 16 byte boundary)
|
||||
|
||||
cache_addb(0x48);
|
||||
cache_addb(0xb8); // mov reg,imm64
|
||||
cache_addq((Bit64u)func);
|
||||
cache_addw(0xd0ff);
|
||||
|
||||
cache_addb(0x48);
|
||||
cache_addw(0xc483);
|
||||
cache_addb(0x08); // add rsp,0x08 (reset alignment)
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// stack is 16 byte aligned now
|
||||
|
||||
|
||||
cache_addb(0x50); // push rax (==old rsp)
|
||||
|
||||
// returned address relates to where the address is stored in gen_call_function_raw
|
||||
Bit64u proc_addr=(Bit64u)cache.pos-4;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
#ifdef DRC_FLAGS_INVALIDATION
|
||||
// called when a call to a function can be replaced by a
|
||||
// call to a simpler function
|
||||
// check gen_call_function_raw and gen_call_function_setup
|
||||
// for the targeted code
|
||||
static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
|
||||
#ifdef DRC_FLAGS_INVALIDATION_DCODE
|
||||
// try to avoid function calls but rather directly fill in code
|
||||
switch (flags_type) {
|
||||
case t_ADDb:
|
||||
case t_ADDw:
|
||||
case t_ADDd:
|
||||
*(Bit32u*)(pos+0)=0xf001f889; // mov eax,edi; add eax,esi
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_ORb:
|
||||
case t_ORw:
|
||||
case t_ORd:
|
||||
*(Bit32u*)(pos+0)=0xf009f889; // mov eax,edi; or eax,esi
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_ANDb:
|
||||
case t_ANDw:
|
||||
case t_ANDd:
|
||||
*(Bit32u*)(pos+0)=0xf021f889; // mov eax,edi; and eax,esi
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_SUBb:
|
||||
case t_SUBw:
|
||||
case t_SUBd:
|
||||
*(Bit32u*)(pos+0)=0xf029f889; // mov eax,edi; sub eax,esi
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_XORb:
|
||||
case t_XORw:
|
||||
case t_XORd:
|
||||
*(Bit32u*)(pos+0)=0xf031f889; // mov eax,edi; xor eax,esi
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_CMPb:
|
||||
case t_CMPw:
|
||||
case t_CMPd:
|
||||
case t_TESTb:
|
||||
case t_TESTw:
|
||||
case t_TESTd:
|
||||
*(Bit32u*)(pos+0)=0x909012eb; // skip
|
||||
*(Bit32u*)(pos+4)=0x90909090;
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_INCb:
|
||||
case t_INCw:
|
||||
case t_INCd:
|
||||
*(Bit32u*)(pos+0)=0xc0fff889; // mov eax,edi; inc eax
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_DECb:
|
||||
case t_DECw:
|
||||
case t_DECd:
|
||||
*(Bit32u*)(pos+0)=0xc8fff889; // mov eax,edi; dec eax
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
case t_NEGb:
|
||||
case t_NEGw:
|
||||
case t_NEGd:
|
||||
*(Bit32u*)(pos+0)=0xd8f7f889; // mov eax,edi; neg eax
|
||||
*(Bit32u*)(pos+4)=0x90900eeb; // skip
|
||||
*(Bit32u*)(pos+8)=0x90909090;
|
||||
*(Bit32u*)(pos+12)=0x90909090;
|
||||
*(Bit32u*)(pos+16)=0x90909090;
|
||||
break;
|
||||
default:
|
||||
*(Bit64u*)(pos+6)=(Bit64u)fct_ptr; // fill function pointer
|
||||
break;
|
||||
}
|
||||
#else
|
||||
*(Bit64u*)(pos+6)=(Bit64u)fct_ptr; // fill function pointer
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cache_block_closing(Bit8u* block_start,Bitu block_size) { }
|
||||
|
||||
static void cache_block_before_close(void) { }
|
@ -1,516 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2013 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
|
||||
|
||||
// protect FC_ADDR over function calls if necessaray
|
||||
// #define DRC_PROTECT_ADDR_REG
|
||||
|
||||
// try to use non-flags generating functions if possible
|
||||
#define DRC_FLAGS_INVALIDATION
|
||||
// try to replace _simple functions by code
|
||||
#define DRC_FLAGS_INVALIDATION_DCODE
|
||||
|
||||
// type with the same size as a pointer
|
||||
#define DRC_PTR_SIZE_IM Bit32u
|
||||
|
||||
// calling convention modifier
|
||||
#if defined (WIN32)
|
||||
#define DRC_CALL_CONV _fastcall
|
||||
#define DRC_FC /* nothing */
|
||||
#else
|
||||
#define DRC_CALL_CONV /* nothing */
|
||||
#define DRC_FC GCC_ATTRIBUTE(fastcall)
|
||||
#endif
|
||||
|
||||
|
||||
// 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, if the ABI does not
|
||||
// state that this register is preserved across function calls
|
||||
// then define DRC_PROTECT_ADDR_REG above
|
||||
#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
|
||||
|
||||
// special register that holds the third parameter for _R3 calls (byte accessible)
|
||||
#define FC_OP3 HOST_EAX
|
||||
|
||||
// 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 may destroy 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 may be destroyed
|
||||
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 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
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 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
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 24bit of the destination register can be destroyed
|
||||
// this function does not use FC_OP1/FC_OP2 as dest_reg as these
|
||||
// registers might not be directly byte-accessible on some architectures
|
||||
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 24bit of the destination register can be destroyed
|
||||
// this function can use FC_OP1/FC_OP2 as dest_reg which are
|
||||
// not directly byte-accessible on some architectures
|
||||
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+scale_reg*(2^scale)+imm
|
||||
cache_addb(0x8d); //LEA
|
||||
cache_addb(0x04+(dest_reg << 3)+rm_base); //The sib indicator
|
||||
cache_addb(dest_reg+(scale_reg<<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) {
|
||||
Bit32u proc_addr=(Bit32u)cache.pos;
|
||||
// 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((!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);
|
||||
}
|
||||
|
||||
// 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_addb(0x56); // push esi
|
||||
cache_addw(0xd0ff); // call eax
|
||||
cache_addb(0x5e); // pop esi
|
||||
cache_addb(0x5b); // pop ebx
|
||||
}
|
||||
|
||||
// return from a function
|
||||
static void gen_return_function(void) {
|
||||
cache_addb(0xc3); // ret
|
||||
}
|
||||
|
||||
#ifdef DRC_FLAGS_INVALIDATION
|
||||
// called when a call to a function can be replaced by a
|
||||
// call to a simpler function
|
||||
static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
|
||||
#ifdef DRC_FLAGS_INVALIDATION_DCODE
|
||||
// try to avoid function calls but rather directly fill in code
|
||||
switch (flags_type) {
|
||||
case t_ADDb:
|
||||
case t_ADDw:
|
||||
case t_ADDd:
|
||||
*(Bit32u*)pos=0xc203c18b; // mov eax,ecx; add eax,edx
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_ORb:
|
||||
case t_ORw:
|
||||
case t_ORd:
|
||||
*(Bit32u*)pos=0xc20bc18b; // mov eax,ecx; or eax,edx
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_ANDb:
|
||||
case t_ANDw:
|
||||
case t_ANDd:
|
||||
*(Bit32u*)pos=0xc223c18b; // mov eax,ecx; and eax,edx
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_SUBb:
|
||||
case t_SUBw:
|
||||
case t_SUBd:
|
||||
*(Bit32u*)pos=0xc22bc18b; // mov eax,ecx; sub eax,edx
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_XORb:
|
||||
case t_XORw:
|
||||
case t_XORd:
|
||||
*(Bit32u*)pos=0xc233c18b; // mov eax,ecx; xor eax,edx
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_CMPb:
|
||||
case t_CMPw:
|
||||
case t_CMPd:
|
||||
case t_TESTb:
|
||||
case t_TESTw:
|
||||
case t_TESTd:
|
||||
*(Bit32u*)pos=0x909003eb; // skip
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_INCb:
|
||||
case t_INCw:
|
||||
case t_INCd:
|
||||
*(Bit32u*)pos=0x9040c18b; // mov eax,ecx; inc eax
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_DECb:
|
||||
case t_DECw:
|
||||
case t_DECd:
|
||||
*(Bit32u*)pos=0x9048c18b; // mov eax,ecx; dec eax
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
case t_NEGb:
|
||||
case t_NEGw:
|
||||
case t_NEGd:
|
||||
*(Bit32u*)pos=0xd8f7c18b; // mov eax,ecx; neg eax
|
||||
*(pos+4)=0x90;
|
||||
break;
|
||||
default:
|
||||
*(Bit32u*)(pos+1)=(Bit32u)((Bit8u*)fct_ptr - (pos+1+4)); // fill function pointer
|
||||
break;
|
||||
}
|
||||
#else
|
||||
*(Bit32u*)(pos+1)=(Bit32u)((Bit8u*)fct_ptr - (pos+1+4)); // fill function pointer
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cache_block_closing(Bit8u* block_start,Bitu block_size) { }
|
||||
|
||||
static void cache_block_before_close(void) { }
|
@ -81,10 +81,6 @@ 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
|
||||
@ -1602,11 +1598,6 @@ void CPU_SET_CRX(Bitu cr,Bitu value) {
|
||||
cpudecoder=&CPU_Core_Dyn_X86_Run;
|
||||
strcpy(core_mode, "dynamic");
|
||||
}
|
||||
#elif (C_DYNREC)
|
||||
if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
|
||||
CPU_Core_Dynrec_Cache_Init(true);
|
||||
cpudecoder=&CPU_Core_Dynrec_Run;
|
||||
}
|
||||
#endif
|
||||
CPU_AutoDetermineMode<<=CPU_AUTODETERMINE_SHIFT;
|
||||
} else {
|
||||
@ -2231,7 +2222,7 @@ static void CPU_ToggleNormalCore(bool pressed) {
|
||||
}
|
||||
}
|
||||
|
||||
#if ((C_DYNAMIC_X86) || (C_DYNREC))
|
||||
#if (C_DYNAMIC_X86)
|
||||
static void CPU_ToggleDynamicCore(bool pressed) {
|
||||
if (!pressed)
|
||||
return;
|
||||
@ -2335,15 +2326,13 @@ 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");
|
||||
MAPPER_AddHandler(CPU_ToggleAutoCycles,MK_equals,MMOD1,"cycauto","Tog. Cycles Auto");
|
||||
MAPPER_AddHandler(CPU_ToggleNormalCore,MK_1,MMOD1,"normal" ,"Tog. Normal Core");
|
||||
MAPPER_AddHandler(CPU_ToggleFullCore,MK_2,MMOD1,"full","Tog. Full Core");
|
||||
#if ((C_DYNAMIC_X86) || (C_DYNREC))
|
||||
#if (C_DYNAMIC_X86)
|
||||
MAPPER_AddHandler(CPU_ToggleDynamicCore,MK_3,MMOD1,"dynamic","Tog. Dynamic Core");
|
||||
#endif
|
||||
MAPPER_AddHandler(CPU_ToggleSimpleCore,MK_4,MMOD1,"simple","Tog. Simple Core");
|
||||
@ -2457,13 +2446,7 @@ public:
|
||||
} else if (core == "dynamic_nodhfpu") {
|
||||
cpudecoder=&CPU_Core_Dyn_X86_Run;
|
||||
CPU_Core_Dyn_X86_SetFPUMode(false);
|
||||
#elif (C_DYNREC)
|
||||
CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CORE;
|
||||
}
|
||||
else if (core == "dynamic") {
|
||||
cpudecoder=&CPU_Core_Dynrec_Run;
|
||||
#else
|
||||
|
||||
#endif
|
||||
} else {
|
||||
strcpy(core_mode,"normal");
|
||||
@ -2473,8 +2456,6 @@ public:
|
||||
|
||||
#if (C_DYNAMIC_X86)
|
||||
CPU_Core_Dyn_X86_Cache_Init((core == "dynamic") || (core == "dynamic_nodhfpu"));
|
||||
#elif (C_DYNREC)
|
||||
CPU_Core_Dynrec_Cache_Init( core == "dynamic" );
|
||||
#endif
|
||||
|
||||
CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
|
||||
@ -2535,8 +2516,6 @@ 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;
|
||||
}
|
||||
@ -2622,15 +2601,9 @@ Bit16u CPU_FindDecoderType( CPU_Decoder *decoder )
|
||||
else if( cpudecoder == &CPU_Core_Simple_Run ) decoder_idx = 2;
|
||||
else if( cpudecoder == &CPU_Core_Full_Run ) decoder_idx = 3;
|
||||
else if( cpudecoder == &CPU_Core_Dyn_X86_Run ) decoder_idx = 4;
|
||||
#if (C_DYNREC)
|
||||
else if( cpudecoder == &CPU_Core_Dynrec_Run ) decoder_idx = 5;
|
||||
#endif
|
||||
|
||||
else if( cpudecoder == &CPU_Core_Normal_Trap_Run ) decoder_idx = 100;
|
||||
else if( cpudecoder == &CPU_Core_Dyn_X86_Trap_Run ) decoder_idx = 101;
|
||||
#if(C_DYNREC)
|
||||
else if( cpudecoder == &CPU_Core_Dynrec_Trap_Run ) decoder_idx = 102;
|
||||
#endif
|
||||
|
||||
else if( cpudecoder == &HLT_Decode ) decoder_idx = 200;
|
||||
|
||||
@ -2651,15 +2624,9 @@ CPU_Decoder *CPU_IndexDecoderType( Bit16u decoder_idx )
|
||||
case 2: cpudecoder = &CPU_Core_Simple_Run; break;
|
||||
case 3: cpudecoder = &CPU_Core_Full_Run; break;
|
||||
case 4: cpudecoder = &CPU_Core_Dyn_X86_Run; break;
|
||||
#if (C_DYNREC)
|
||||
case 5: cpudecoder = &CPU_Core_Dynrec_Run; break;
|
||||
#endif
|
||||
|
||||
case 100: cpudecoder = &CPU_Core_Normal_Trap_Run; break;
|
||||
case 101: cpudecoder = &CPU_Core_Dyn_X86_Trap_Run; break;
|
||||
#if (C_DYNREC)
|
||||
case 102: cpudecoder = &CPU_Core_Dynrec_Trap_Run; break;
|
||||
#endif
|
||||
|
||||
case 200: cpudecoder = &HLT_Decode; break;
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode) {
|
||||
} else {
|
||||
GFX_SetTitle(-1,-1,-1,false);
|
||||
}
|
||||
#if (C_DYNAMIC_X86) || (C_DYNREC)
|
||||
#if (C_DYNAMIC_X86)
|
||||
if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
|
||||
cpudecoder=&CPU_Core_Normal_Run;
|
||||
CPU_CycleLeft=0;
|
||||
|
@ -707,7 +707,7 @@ void DOSBOX_Init(void) {
|
||||
|
||||
secprop=control->AddSection_prop("cpu",&CPU_Init,true);//done
|
||||
const char* cores[] = { "auto",
|
||||
#if (C_DYNAMIC_X86) || (C_DYNREC)
|
||||
#if (C_DYNAMIC_X86)
|
||||
"dynamic",
|
||||
#endif
|
||||
"normal", "full", "simple", 0 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user