c0f45e0b7f
BIOS execution is provided through the `v8086` module, which provides software emulation of an 8086 processor. It is not currently working with some BIOSes and may (read: probably will be) replaced with another emulator (x86emu comes to mind) at some point in the near future. In the meantime, the default video mode for QEMU works with this and it's enough to get us on real VESA instead of fake VBE. The `bochs` module will be renamed in a future commit. Userspace programs have been adjusted to work at bitrates other than 32 *POORLY*. If you write pixels left-to-right, they should work fine. They only work with 24-bpp otherwise, and then you need to be careful of what pixels you are writing when, or you will overwrite things in other pixels. You may pass a commandline argument like the following to set display modes: vid=vesa,1024,768 Or for stranger modes under QEMU or Bochs, use the bochs VBE initializer: vid=bochs,1280,720 Note that the address of the linear framebuffer is still found via hackish probing instead of PCI or trusting the VBE information, so if you have things in the wrong memory ranges (0xE0000000+), be prepared to have them get read. Once again, this entire commit is a massive hack. I am happy that it worked, and I will continue to make it less hacky, but in the meantime, this is what we've got. Happy holidays.
2421 lines
64 KiB
C
2421 lines
64 KiB
C
/*
|
|
* Realmode Emulator Plugin
|
|
* - By John Hodge (thePowersGang)
|
|
*
|
|
* This code is published under the FreeBSD licence
|
|
* (See the file COPYING for details)
|
|
*
|
|
* ---
|
|
* Core Emulator
|
|
*/
|
|
#define _RME_C_
|
|
#include <system.h>
|
|
#include <types.h>
|
|
|
|
#define outb outportb
|
|
#define outw outports
|
|
#define outl outportl
|
|
#define inb inportb
|
|
#define inw inports
|
|
#define inl inportl
|
|
|
|
typedef uint8_t Uint8;
|
|
typedef uint16_t Uint16;
|
|
typedef uint32_t Uint32;
|
|
typedef int8_t Sint8;
|
|
typedef int16_t Sint16;
|
|
typedef int32_t Sint32;
|
|
typedef int32_t intptr_t;
|
|
|
|
#include "rme.h"
|
|
|
|
// Settings
|
|
#define DEBUG 0 // Enable debug?
|
|
#define RME_DO_NULL_CHECK 1
|
|
#define ERR_OUTPUT 1 // Enable using printf on an error?
|
|
#define printf printf // Formatted print function
|
|
#define outB(state,port,val) (outb(port,val),0) // Write 1 byte to an IO Port
|
|
#define outW(state,port,val) (outw(port,val),0) // Write 2 bytes to an IO Port
|
|
#define outD(state,port,val) (outl(port,val),0) // Write 4 bytes to an IO Port
|
|
#define inB(state,port,dst) (*(dst)=inb((port)),0) // Read 1 byte from an IO Port
|
|
#define inW(state,port,dst) (*(dst)=inw((port)),0) // Read 2 bytes from an IO Port
|
|
#define inD(state,port,dst) (*(dst)=inl((port)),0) // Read 4 bytes from an IO Port
|
|
|
|
// -- Per Compiler macros
|
|
#define WARN_UNUSED_RET __attribute__((warn_unused_result))
|
|
|
|
// === CONSTANTS ===
|
|
#define FLAG_DEFAULT 0x2
|
|
/**
|
|
* \brief FLAGS Register Values
|
|
* \{
|
|
*/
|
|
#define FLAG_CF 0x001 //!< Carry Flag
|
|
#define FLAG_PF 0x004 //!< Pairity Flag
|
|
#define FLAG_AF 0x010 //!< Adjust Flag
|
|
#define FLAG_ZF 0x040 //!< Zero Flag
|
|
#define FLAG_SF 0x080 //!< Sign Flag
|
|
#define FLAG_TF 0x100 //!< Trap Flag (for single stepping)
|
|
#define FLAG_IF 0x200 //!< Interrupt Flag
|
|
#define FLAG_DF 0x400 //!< Direction Flag
|
|
#define FLAG_OF 0x800 //!< Overflow Flag
|
|
/**
|
|
* \}
|
|
*/
|
|
|
|
/**
|
|
* \name Exception Handlers
|
|
* \{
|
|
*/
|
|
#define RME_Int_Expt_DivideError(State) (RME_ERR_DIVERR)
|
|
/**
|
|
* \}
|
|
*/
|
|
|
|
// --- Stack Primiatives ---
|
|
#define PUSH(v) do{\
|
|
ret=RME_Int_Write16(State,State->SS,State->SP.W-=2,(v));\
|
|
if(ret)return ret;\
|
|
}while(0)
|
|
#define POP(dst) do{\
|
|
uint16_t v;\
|
|
ret=RME_Int_Read16(State,State->SS,State->SP.W,&v);\
|
|
if(ret)return ret;\
|
|
State->SP.W+=2;\
|
|
(dst)=v;\
|
|
}while(0)
|
|
|
|
// --- Debug Macro ---
|
|
#if DEBUG
|
|
# define DEBUG_S(...) kprintf(__VA_ARGS__)
|
|
#else
|
|
# define DEBUG_S(...)
|
|
#endif
|
|
#if ERR_OUTPUT
|
|
# define ERROR_S(...) kprintf(__VA_ARGS__)
|
|
#else
|
|
# define ERROR_S(...)
|
|
#endif
|
|
|
|
// === MACRO VOODOO ===
|
|
#define XCHG(a,b) do{uint32_t t=(a);(a)=(b);(b)=(t);}while(0)
|
|
// --- Case Helpers
|
|
#define CASE4(b) case(b):case((b)+1):case((b)+2):case((b)+3)
|
|
#define CASE8(b) CASE4(b):CASE4((b)+4)
|
|
#define CASE16(b) CASE4(b):CASE4((b)+4):CASE4((b)+8):CASE4((b)+12)
|
|
#define CASE4K(b,k) case(b):case((b)+1*(k)):case((b)+2*(k)):case((b)+3*(k))
|
|
#define CASE8K(b,k) CASE4K(b,k):CASE4K((b)+4*(k),k)
|
|
#define CASE16K(b,k) CASE4K(b,k):CASE4K((b)+4*(k),k):CASE4K((b)+8*(k),k):CASE4K((b)+12*(k),k)
|
|
// --- Operation helpers
|
|
#define PAIRITY8(v) ((((v)>>7)&1)^(((v)>>6)&1)^(((v)>>5)&1)^(((v)>>4)&1)^(((v)>>3)&1)^(((v)>>2)&1)^(((v)>>1)&1)^((v)&1))
|
|
#define PAIRITY16(v) (PAIRITY8(v) ^ PAIRITY8(v>>8))
|
|
#define SET_PF(State,v,w) do{\
|
|
if(w==8) State->Flags |= PAIRITY8(v) ? 0 : FLAG_PF;\
|
|
else if(w==16) State->Flags |= PAIRITY16(v) ? 0 : FLAG_PF;\
|
|
else State->Flags |= (PAIRITY16(v) ^ PAIRITY16((v)>>16)) ? 0 : FLAG_PF;\
|
|
}while(0)
|
|
#define SET_COMM_FLAGS(State,v,w) do{\
|
|
State->Flags &= ~(FLAG_ZF|FLAG_SF|FLAG_CF);\
|
|
State->Flags |= ((v) == 0) ? FLAG_ZF : 0;\
|
|
State->Flags |= ((v) >> ((w)-1)) ? FLAG_SF : 0;\
|
|
SET_PF(State, (v), (w));\
|
|
}while(0)
|
|
|
|
// --- OPERATIONS ---
|
|
// Logical TEST (Set flags according to AND)
|
|
#define RME_Int_DoTest(State, to, from, width) do{\
|
|
int v = (to) & (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,v,(width));\
|
|
}while(0)
|
|
#define RME_Int_DoRol(State, to, from, width) do{\
|
|
(to) = ((to) << (from)) | ((to) >> ((width)-(from)));\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to) >> ((width)-1)) ? FLAG_CF : 0;\
|
|
}while(0)
|
|
#define RME_Int_DoRor(State, to, from, width) do{\
|
|
(to) = ((to) >> (from)) | ((to) << ((width)-(from)));\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to) >> ((width)-1)) ? FLAG_CF : 0;\
|
|
}while(0)
|
|
#define RME_Int_DoShl(State, to, from, width) do{\
|
|
(to) <<= (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to) >> ((width)-1)) ? FLAG_CF : 0;\
|
|
}while(0)
|
|
#define RME_Int_DoShr(State, to, from, width) do{\
|
|
(to) >>= (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to) & 1) ? FLAG_CF : 0;\
|
|
}while(0)
|
|
|
|
// --- Memory Helpers
|
|
/**
|
|
* \brief Read an unsigned byte from the instruction stream
|
|
* Reads 1 byte as an unsigned integer from CS:IP and increases IP by 1.
|
|
*/
|
|
#define READ_INSTR8(dst) do{int r;uint8_t v;\
|
|
r=RME_Int_Read8(State,State->CS,State->IP+State->Decoder.IPOffset,&v);\
|
|
if(r) return r;\
|
|
State->Decoder.IPOffset++;\
|
|
(dst) = v;\
|
|
}while(0)
|
|
/**
|
|
* \brief Read a signed byte from the instruction stream
|
|
* Reads 1 byte as an signed integer from CS:IP and increases IP by 1.
|
|
*/
|
|
#define READ_INSTR8S(dst) do{int r;int8_t v;\
|
|
r=RME_Int_Read8(State,State->CS,State->IP+State->Decoder.IPOffset,(uint8_t*)&v);\
|
|
if(r) return r;\
|
|
State->Decoder.IPOffset++;\
|
|
(dst) = v;\
|
|
}while(0)
|
|
/**
|
|
* \brief Read a word from the instruction stream
|
|
* Reads 2 bytes as an unsigned integer from CS:IP and increases IP by 2.
|
|
*/
|
|
#define READ_INSTR16(dst) do{int r;uint16_t v;\
|
|
r=RME_Int_Read16(State,State->CS,State->IP+State->Decoder.IPOffset,&v);\
|
|
if(r) return r;\
|
|
State->Decoder.IPOffset+=2;\
|
|
(dst) = v;\
|
|
}while(0)
|
|
/**
|
|
* \brief Read a word from the instruction stream
|
|
* Reads 4 bytes as an unsigned integer from CS:IP and increases IP by 4.
|
|
*/
|
|
#define READ_INSTR32(dst) do{int r;uint32_t v;\
|
|
r=RME_Int_Read32(State,State->CS,State->IP+State->Decoder.IPOffset,&v);\
|
|
if(r) return r;\
|
|
State->Decoder.IPOffset+=4;\
|
|
(dst) = v;\
|
|
}while(0)
|
|
/**
|
|
* \brief Get a segment with overrides
|
|
*/
|
|
#define GET_SEGMENT(State,_def) (Seg(State, (State->Decoder.OverrideSegment==-1?(_def):State->Decoder.OverrideSegment)))
|
|
|
|
// === PROTOTYPES ===
|
|
tRME_State *RME_CreateState(void);
|
|
void RME_DumpRegs(tRME_State *State);
|
|
int RME_CallInt(tRME_State *State, int Num);
|
|
int RME_Call(tRME_State *State);
|
|
static int RME_Int_DoOpcode(tRME_State *State);
|
|
|
|
static inline WARN_UNUSED_RET int RME_Int_GetPtr(tRME_State *State, uint16_t Seg, uint16_t Ofs, void **Ptr);
|
|
static inline WARN_UNUSED_RET int RME_Int_Read8(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint8_t *Dst);
|
|
static inline WARN_UNUSED_RET int RME_Int_Read16(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint16_t *Dst);
|
|
static inline WARN_UNUSED_RET int RME_Int_Read32(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint32_t *Dst);
|
|
static inline WARN_UNUSED_RET int RME_Int_Write8(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint8_t Val);
|
|
static inline WARN_UNUSED_RET int RME_Int_Write16(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint16_t Val);
|
|
static inline WARN_UNUSED_RET int RME_Int_Write32(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint32_t Val);
|
|
|
|
static inline WARN_UNUSED_RET int RME_Int_DoArithOp8(int Num, tRME_State *State, uint8_t *Dest, uint8_t Src);
|
|
static inline WARN_UNUSED_RET int RME_Int_DoArithOp16(int Num, tRME_State *State, uint16_t *Dest, uint16_t Src);
|
|
static inline WARN_UNUSED_RET int RME_Int_DoArithOp32(int Num, tRME_State *State, uint32_t *Dest, uint32_t Src);
|
|
|
|
static WARN_UNUSED_RET int RME_Int_ParseModRM(tRME_State *State, uint8_t **to, uint8_t **from) __attribute__((warn_unused_result));
|
|
static WARN_UNUSED_RET int RME_Int_ParseModRMX(tRME_State *State, uint16_t **to, uint16_t **from) __attribute__((warn_unused_result));
|
|
static inline uint16_t *Seg(tRME_State *State, int code);
|
|
static inline uint8_t *RegB(tRME_State *State, int num);
|
|
static inline uint16_t *RegW(tRME_State *State, int num);
|
|
static inline WARN_UNUSED_RET int RME_Int_DoCondJMP(tRME_State *State, uint8_t type, uint16_t offset, const char *name);
|
|
|
|
// === GLOBALS ===
|
|
#if DEBUG
|
|
static const char *casArithOps[] = {"ADD", "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP"};
|
|
static const char *casLogicOps[] = {"ROL", "ROR", "RCL", "RCR", "SHL", "SHR", "L6-", "L7-"};
|
|
#endif
|
|
|
|
// === CODE ===
|
|
/**
|
|
* \brief Creates a blank RME State
|
|
*/
|
|
tRME_State *RME_CreateState(void)
|
|
{
|
|
tRME_State *state = calloc(sizeof(tRME_State), 1);
|
|
|
|
if(state == NULL) return NULL;
|
|
|
|
// Initial Stack
|
|
state->Flags = FLAG_DEFAULT;
|
|
|
|
// Stub CS/IP
|
|
state->CS = 0xF000;
|
|
state->IP = 0xFFF0;
|
|
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* \brief Dump Realmode Registers
|
|
*/
|
|
void RME_DumpRegs(tRME_State *State)
|
|
{
|
|
DEBUG_S("\n");
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
DEBUG_S("EAX %x ECX %x EDX %x EBX %x\n",
|
|
State->AX.D, State->CX.D, State->DX.D, State->BX.D);
|
|
DEBUG_S("ESP %x EBP %x ESI %x EDI %x\n",
|
|
State->SP.D, State->BP.D, State->SI.D, State->DI.D);
|
|
#else
|
|
DEBUG_S("AX %x CX %x DX %x BX %x\n",
|
|
State->AX.W, State->CX.W, State->DX.W, State->BX.W);
|
|
DEBUG_S("SP %x BP %x SI %x DI %x\n",
|
|
State->SP.W, State->BP.W, State->SI.W, State->DI.W);
|
|
#endif
|
|
DEBUG_S("SS %x DS %x ES %x\n",
|
|
State->SS, State->DS, State->ES);
|
|
DEBUG_S("CS:IP = 0x%x:%x\n", State->CS, State->IP);
|
|
DEBUG_S("Flags = %x\n", State->Flags);
|
|
}
|
|
|
|
/**
|
|
* \brief Run Realmode interrupt
|
|
*/
|
|
int RME_CallInt(tRME_State *State, int Num)
|
|
{
|
|
int ret;
|
|
DEBUG_S("RM_Int: Calling Int 0x%x\n", Num);
|
|
|
|
if(Num < 0 || Num > 0xFF) {
|
|
ERROR_S("WARNING: %d is not a valid interrupt number", Num);
|
|
return RME_ERR_INVAL;
|
|
}
|
|
|
|
ret = RME_Int_Read16(State, 0, Num*4, &State->IP);
|
|
if(ret) return ret;
|
|
ret = RME_Int_Read16(State, 0, Num*4+2, &State->CS);
|
|
if(ret) return ret;
|
|
|
|
PUSH(State->Flags);
|
|
PUSH(RME_MAGIC_CS);
|
|
PUSH(RME_MAGIC_IP);
|
|
|
|
return RME_Call(State);
|
|
}
|
|
|
|
/**
|
|
* \brief Call a realmode function (a jump to a magic location is used as the return)
|
|
*/
|
|
int RME_Call(tRME_State *State)
|
|
{
|
|
int ret;
|
|
for(;;)
|
|
{
|
|
#if DEBUG >= 2
|
|
RME_DumpRegs(State);
|
|
#endif
|
|
if(State->IP == RME_MAGIC_IP && State->CS == RME_MAGIC_CS)
|
|
return 0;
|
|
ret = RME_Int_DoOpcode(State);
|
|
if(ret) return ret;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Processes a single instruction
|
|
*/
|
|
int RME_Int_DoOpcode(tRME_State *State)
|
|
{
|
|
uint16_t pt2, pt1 = 0; // Spare Words, used for values read from memory
|
|
uint16_t seg; // Segment value
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
uint32_t dword; // Spare Double Word
|
|
#endif
|
|
// Destination/Source pointers (word sized)
|
|
union {
|
|
uint32_t *D;
|
|
uint16_t *W;
|
|
} to, from;
|
|
// Destination/Source pointers (byte sized)
|
|
uint8_t *toB, *fromB;
|
|
uint8_t repType = 0; // Repeat flag
|
|
uint8_t opcode, byte2; // Current opcode and second byte
|
|
int ret; // Return value from functions
|
|
// Initial CPU State
|
|
uint16_t startIP, startCS;
|
|
|
|
startIP = State->IP;
|
|
startCS = State->CS;
|
|
|
|
State->Decoder.OverrideSegment = -1;
|
|
State->Decoder.bOverrideOperand = 0;
|
|
State->Decoder.bOverrideAddress = 0;
|
|
State->Decoder.IPOffset = 0;
|
|
State->InstrNum ++;
|
|
|
|
DEBUG_S("(%d) [0x%x] %x:%x ", State->InstrNum, State->CS*16+State->IP, State->CS, State->IP);
|
|
|
|
decode:
|
|
READ_INSTR8( opcode );
|
|
switch( opcode )
|
|
{
|
|
|
|
// Prefixes
|
|
// - Segment Overrides
|
|
case OVR_CS:
|
|
DEBUG_S("<CS> ");
|
|
State->Decoder.OverrideSegment = SREG_CS;
|
|
goto decode;
|
|
case OVR_SS:
|
|
DEBUG_S("<SS> ");
|
|
State->Decoder.OverrideSegment = SREG_SS;
|
|
goto decode;
|
|
case OVR_DS:
|
|
DEBUG_S("<DS> ");
|
|
State->Decoder.OverrideSegment = SREG_DS;
|
|
goto decode;
|
|
case OVR_ES:
|
|
DEBUG_S("<ES> ");
|
|
State->Decoder.OverrideSegment = SREG_ES;
|
|
goto decode;
|
|
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
case 0x66: // Operand Size Override
|
|
DEBUG_S("<OPER> ");
|
|
State->Decoder.bOverrideOperand = 1;
|
|
goto decode;
|
|
case 0x67: // Memory Size Override
|
|
DEBUG_S("<ADDR> ");
|
|
State->Decoder.bOverrideAddress = 1;
|
|
return RME_ERR_UNDEFOPCODE;
|
|
#elif USE_SIZE_OVERRIDES == 0
|
|
case 0x66: // Operand Size Override
|
|
case 0x67: // Memory Size Override
|
|
goto decode;
|
|
#endif
|
|
case 0x69:
|
|
case 0x6D:
|
|
goto decode;
|
|
|
|
// Repeat Prefix
|
|
case REP: DEBUG_S("REP ");
|
|
repType = REP;
|
|
goto decode;
|
|
case REPNZ: DEBUG_S("REPNZ ");
|
|
repType = REPNZ;
|
|
goto decode;
|
|
|
|
case 0x37:
|
|
DEBUG_S("AAA");
|
|
break;
|
|
|
|
// <op> MR - Memory from Register
|
|
CASE8K(0x00, 0x8):
|
|
DEBUG_S("%s (MR)", casArithOps[opcode >> 3]);
|
|
ret = RME_Int_ParseModRM(State, &fromB, &toB);
|
|
if(ret) return ret;
|
|
ret = RME_Int_DoArithOp8( opcode >> 3, State, toB, *fromB );
|
|
if(ret) return ret;
|
|
break;
|
|
// <op> MRX - Memory from Register (Word)
|
|
CASE8K(0x01, 0x8):
|
|
DEBUG_S("%s (MRX)", casArithOps[opcode >> 3]);
|
|
ret = RME_Int_ParseModRMX(State, &from.W, &to.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
ret = RME_Int_DoArithOp32( opcode >> 3, State, to.D, *from.D );
|
|
else
|
|
#endif
|
|
ret = RME_Int_DoArithOp16( opcode >> 3, State, to.W, *from.W );
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// <op> RM - Register from Memory
|
|
CASE8K(0x02, 0x8):
|
|
DEBUG_S("%s (RM)", casArithOps[opcode >> 3]);
|
|
ret = RME_Int_ParseModRM(State, &toB, &fromB);
|
|
if(ret) return ret;
|
|
ret = RME_Int_DoArithOp8( opcode >> 3, State, toB, *fromB );
|
|
if(ret) return ret;
|
|
break;
|
|
// <op> RMX - Register from Memory (Word)
|
|
CASE8K(0x03, 0x8):
|
|
DEBUG_S("%s (RM)", casArithOps[opcode >> 3]);
|
|
ret = RME_Int_ParseModRMX(State, &to.W, &from.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
ret = RME_Int_DoArithOp32( opcode >> 3, State, to.D, *from.D );
|
|
else
|
|
#endif
|
|
ret = RME_Int_DoArithOp16( opcode >> 3, State, to.W, *from.W );
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// <op> AI - Accumulator from Immedate
|
|
CASE8K(0x04, 8):
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S("%s (AI) AL 0x%x", casArithOps[opcode >> 3], pt2);
|
|
ret = RME_Int_DoArithOp8( opcode >> 3, State, &State->AX.B.L, pt2 );
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// <op> AIX - Accumulator from Immedate (Word)
|
|
CASE8K(0x05, 8):
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
READ_INSTR32( dword );
|
|
DEBUG_S("%s (AIX) EAX 0x%x", casArithOps[opcode >> 3], dword);
|
|
ret = RME_Int_DoArithOp32( opcode >> 3, State, &State->AX.D, dword );
|
|
} else {
|
|
#endif
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S("%s (AIX) AX 0x%x", casArithOps[opcode >> 3], pt2);
|
|
ret = RME_Int_DoArithOp16( opcode >> 3, State, &State->AX.W, pt2 );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// <op> RI - Register from Immediate
|
|
case 0x80:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RI)", casArithOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
ret = RME_Int_DoArithOp8( (byte2 >> 3) & 7, State, toB, pt2 );
|
|
if(ret) return ret;
|
|
break;
|
|
// <op> RIX - Register from Immediate (Word)
|
|
case 0x81:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RIX)", casArithOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); // Get Register Value
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
READ_INSTR32( dword );
|
|
DEBUG_S(" 0x%x", dword);
|
|
ret = RME_Int_DoArithOp32( (byte2 >> 3) & 7, State, to.D, dword );
|
|
} else {
|
|
#endif
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
ret = RME_Int_DoArithOp16( (byte2 >> 3) & 7, State, to.W, pt2 );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// 0x82 MAY be a valid instruction, with the same effect as 0x80 (<op> RI)
|
|
// NOPE - 0x82 is a #UD
|
|
|
|
// <op> RI8X - Register from Imm8 (Word)
|
|
case 0x83:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RI8X)", casArithOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); //Get Register Value
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
READ_INSTR8S( dword );
|
|
DEBUG_S(" 0x%x", dword);
|
|
ret = RME_Int_DoArithOp32( (byte2 >> 3) & 7, State, to.D, dword );
|
|
} else {
|
|
#endif
|
|
READ_INSTR8S( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
ret = RME_Int_DoArithOp16( (byte2 >> 3) & 7, State, to.W, pt2 );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// ==== Logic Functions (Shifts) ===
|
|
#define RME_Int_DoLogicOp( num, State, to, from, width ) do{\
|
|
switch( (num) ) {\
|
|
case 1: RME_Int_DoRor(State, (to), (from), (width)); break;\
|
|
case 4: RME_Int_DoShl(State, (to), (from), (width)); break;\
|
|
case 5: RME_Int_DoShr(State, (to), (from), (width)); break;\
|
|
default: /*ERROR_S(" - DoLogicOp Undef %d\n", (num));*/ return RME_ERR_UNDEFOPCODE;\
|
|
}}while(0)
|
|
// <op> RI8 - Register by Imm8
|
|
case 0xC0:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RI8)", casLogicOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *toB, pt2, 8 );
|
|
break;
|
|
// <op> RI8X - Register by Imm8 (Word)
|
|
case 0xC1:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RI8X)", casLogicOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *to.D, pt2, 32 );
|
|
else
|
|
#endif
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *to.W, pt2, 16 );
|
|
break;
|
|
// <op> R1 - Register by 1
|
|
case 0xD0:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (R1)", casLogicOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
DEBUG_S(" 1");
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *toB, 1, 8 );
|
|
break;
|
|
// <op> R1X - Register by 1 (Word)
|
|
case 0xD1:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (R1X)", casLogicOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
DEBUG_S(" 1");
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *to.D, 1, 32 );
|
|
else
|
|
#endif
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *to.W, 1, 16 );
|
|
break;
|
|
// <op> RCl - Register by CL
|
|
case 0xD2:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RCl)", casLogicOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
DEBUG_S(" CL");
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *toB, State->CX.B.L, 8 );
|
|
break;
|
|
// <op> RClX - Register by CL (Word)
|
|
case 0xD3:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
DEBUG_S("%s (RClX)", casLogicOps[(byte2 >> 3) & 7]);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
DEBUG_S(" CL");
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *to.D, State->CX.B.L, 32 );
|
|
else
|
|
#endif
|
|
RME_Int_DoLogicOp( (byte2 >> 3) & 7, State, *to.W, State->CX.B.L, 16 );
|
|
break;
|
|
|
|
// ==== Misc ALU ====
|
|
// <op> RI - Register by Immediate
|
|
case 0xF6:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
switch( (byte2>>3) & 7 )
|
|
{
|
|
case 0: // TEST r/m8, Imm8
|
|
DEBUG_S("TEST (MI)");
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
READ_INSTR8(pt2);
|
|
DEBUG_S(" 0x%x", pt2);
|
|
RME_Int_DoTest(State, *toB, pt2, 8);
|
|
break;
|
|
// Undefined Opcode
|
|
case 1: DEBUG_S("0xF6 /1 Undefined\n"); return RME_ERR_UNDEFOPCODE;
|
|
// NOT r/m8
|
|
case 2: DEBUG_S("NOT (M)");
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
*toB = ~*toB;
|
|
break;
|
|
// NEG r/m8
|
|
case 3: DEBUG_S("NEG (M)");
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
*toB = -*toB;
|
|
State->Flags &= ~FLAG_OF;
|
|
State->Flags |= (*toB == 0) ? FLAG_CF : 0;
|
|
SET_COMM_FLAGS(State, *toB, 8);
|
|
break;
|
|
case 4: // MUL AX = AL * r/m8
|
|
ERROR_S("MUL (MI) AL");
|
|
ret = RME_Int_ParseModRM(State, NULL, &fromB);
|
|
if(ret) return ret;
|
|
pt2 = (Uint16)State->AX.B.L * (*fromB);
|
|
State->AX.W = pt2;
|
|
// TODO: Flags
|
|
break;
|
|
case 5: // IMUL AX = AL * r/m8 (signed)
|
|
DEBUG_S("IMUL (MI) AL");
|
|
ret = RME_Int_ParseModRM(State, NULL, &fromB);
|
|
if(ret) return ret;
|
|
pt2 = (Sint16)(Sint8)State->AX.B.L * (Sint8)*fromB;
|
|
State->AX.W = pt2;
|
|
// TODO: Flags
|
|
break;
|
|
case 6: // DIV AX, r/m8 (unsigned)
|
|
DEBUG_S("DIV (MI) AX");
|
|
ret = RME_Int_ParseModRM(State, NULL, &fromB);
|
|
if(ret) return ret;
|
|
if(*fromB == 0) return RME_Int_Expt_DivideError(State);
|
|
pt2 = State->AX.W / *fromB;
|
|
if(pt2 > 0xFF) return RME_Int_Expt_DivideError(State);
|
|
pt2 |= (State->AX.W - pt2 * (*fromB)) << 8;
|
|
State->AX.W = pt2;
|
|
break;
|
|
case 7: // IDIV AX, r/m8 (signed)
|
|
ERROR_S("0xF6 /7 - IDIV AL, r/m8 unimplemented\n");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
break;
|
|
// <op> RIX - Register by Immediate (Word)
|
|
case 0xF7:
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
switch( (byte2>>3) & 7 )
|
|
{
|
|
case 0: // TEST r/m16, Imm16
|
|
DEBUG_S("TEST (RIX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
{
|
|
READ_INSTR32(dword);
|
|
DEBUG_S(" 0x%x", dword);
|
|
RME_Int_DoTest(State, *to.D, dword, 32);
|
|
} else {
|
|
#endif
|
|
READ_INSTR16(pt2);
|
|
DEBUG_S(" 0x%x", pt2);
|
|
RME_Int_DoTest(State, *to.W, pt2, 16);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
// Undefined Opcode
|
|
case 1:
|
|
DEBUG_S("0xF7 /1 Undefined\n");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
// NOT r/m16
|
|
case 2: DEBUG_S("NOT (MX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(!ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
{
|
|
*to.D = ~*to.D;
|
|
} else {
|
|
#endif
|
|
*to.W = ~*to.W;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
// TODO: Flags
|
|
break;
|
|
// NEG r/m16
|
|
case 3: DEBUG_S("NEG (MX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(!ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
{
|
|
*to.D = -*to.D;
|
|
State->Flags &= ~FLAG_OF;
|
|
State->Flags |= (*to.D == 0) ? FLAG_CF : 0;
|
|
SET_COMM_FLAGS(State, *to.D, 32);
|
|
}
|
|
else {
|
|
#endif
|
|
*to.W = -*to.W;
|
|
State->Flags &= ~FLAG_OF;
|
|
State->Flags |= (*to.W == 0) ? FLAG_CF : 0;
|
|
SET_COMM_FLAGS(State, *to.W, 16);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
case 4: // MUL AX, r/m16
|
|
{
|
|
uint32_t dword;
|
|
DEBUG_S("MUL (RIX) AX");
|
|
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
// TODO: Handle Size Overrides
|
|
//#warning "Implement MUL (RIX) 32-bit"
|
|
if(State->Decoder.bOverrideOperand) return RME_ERR_UNDEFOPCODE;
|
|
#endif
|
|
|
|
ret = RME_Int_ParseModRMX(State, NULL, &from.W);
|
|
if(ret) return ret;
|
|
#if DEBUG >= 2
|
|
DEBUG_S(" (0x%x)", *from.W);
|
|
#endif
|
|
|
|
dword = State->AX.W * *from.W;
|
|
if(dword == State->AX.W)
|
|
State->Flags &= ~(FLAG_CF|FLAG_OF);
|
|
else
|
|
State->Flags |= FLAG_CF|FLAG_OF;
|
|
// TODO: More flags?
|
|
State->DX.W = dword >> 16;
|
|
State->AX.W = dword & 0xFFFF;
|
|
}
|
|
break;
|
|
case 5: // IMUL AX, r/m16
|
|
{
|
|
uint32_t dword;
|
|
DEBUG_S("IMUL (RIX) AX");
|
|
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
// TODO: Handle Size Overrides
|
|
//#warning "Implement IMUL (RIX) 32-bit"
|
|
if(State->Decoder.bOverrideOperand) return RME_ERR_UNDEFOPCODE;
|
|
#endif
|
|
|
|
ret = RME_Int_ParseModRMX(State, NULL, &from.W);
|
|
if(ret) return ret;
|
|
dword = (int16_t)State->AX.W * (int16_t)*from.W;
|
|
DEBUG_S(" %x * %x = %x", State->AX.W, *from.W, dword);
|
|
if((int32_t)dword == (int16_t)State->AX.W)
|
|
State->Flags &= ~(FLAG_CF|FLAG_OF);
|
|
else
|
|
State->Flags |= FLAG_CF|FLAG_OF;
|
|
// TODO: More flags?
|
|
State->DX.W = dword >> 16;
|
|
State->AX.W = dword & 0xFFFF;
|
|
}
|
|
break;
|
|
case 6: // DIV DX:AX, r/m16
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
DEBUG_S("DIV (RIX) EDX:EAX");
|
|
else
|
|
#endif
|
|
DEBUG_S("DIV (RIX) DX:AX");
|
|
|
|
ret = RME_Int_ParseModRMX(State, NULL, &from.W);
|
|
if(ret) return ret;
|
|
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
uint64_t qword, qword2;
|
|
if( *from.D == 0 ) return RME_Int_Expt_DivideError(State);
|
|
#if DEBUG >= 2
|
|
DEBUG_S(" /= 0x%x", *from.D);
|
|
#endif
|
|
qword = ((uint64_t)State->DX.D << 32) | State->AX.D;
|
|
//qword2 = qword / *from.D;
|
|
if(qword2 > 0xFFFFFFFF) return RME_Int_Expt_DivideError(State);
|
|
State->AX.D = qword2;
|
|
State->DX.D = qword - qword2 * (*from.D);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
uint32_t dword, dword2;
|
|
if( *from.W == 0 ) return RME_Int_Expt_DivideError(State);
|
|
#if DEBUG >= 2
|
|
DEBUG_S(" /= 0x%x", *from.W);
|
|
#endif
|
|
dword = (State->DX.W << 16) | State->AX.W;
|
|
dword2 = dword / *from.W;
|
|
if(dword2 > 0xFFFF) return RME_Int_Expt_DivideError(State);
|
|
State->AX.W = dword2;
|
|
State->DX.W = dword - dword2 * (*from.W);
|
|
//#warning "Implement Flags for DIV (RIX)"
|
|
}
|
|
break;
|
|
case 7: // IDIV DX:AX, r/m16
|
|
//#warning "TODO: IDIV RIX"
|
|
ERROR_S("0xF7 /7 - IDIV DX:AX, r/m16 unimplemented\n");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
default:
|
|
ERROR_S("0xF7 /%x unknown\n", (byte2>>3) & 7);
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
break;
|
|
|
|
// ==== Unary ALU ===
|
|
// <op> R
|
|
case 0xFE: // Register
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
switch( (byte2>>3) & 7 )
|
|
{
|
|
case 0:
|
|
DEBUG_S("INC (R)");
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB); //Get Register Value
|
|
if(ret) return ret;
|
|
(*toB) ++;
|
|
State->Flags &= ~(FLAG_OF|FLAG_ZF|FLAG_SF|FLAG_PF);
|
|
SET_COMM_FLAGS(State, *toB, 8);
|
|
State->Flags |= (State->Flags & FLAG_ZF) ? FLAG_OF : 0;
|
|
break;
|
|
case 1:
|
|
DEBUG_S("DEC (R)");
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB); //Get Register Value
|
|
if(ret) return ret;
|
|
(*toB) --;
|
|
State->Flags &= ~(FLAG_OF|FLAG_ZF|FLAG_SF|FLAG_PF);
|
|
SET_COMM_FLAGS(State, *toB, 8);
|
|
State->Flags |= (*toB == 0xFF) ? FLAG_OF : 0;
|
|
break;
|
|
default:
|
|
DEBUG_S("0xFE /%x unknown", (byte2>>3) & 7);
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
break;
|
|
|
|
// <op> RX
|
|
case 0xFF: // Register (Word)
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
switch( (byte2>>3) & 7 )
|
|
{
|
|
// Increment
|
|
case 0:
|
|
DEBUG_S("INC (RX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); //Get Register Value
|
|
if(ret) return ret;
|
|
|
|
State->Flags &= ~(FLAG_OF|FLAG_ZF|FLAG_SF|FLAG_PF);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
(*to.D) ++;
|
|
SET_COMM_FLAGS(State, *to.D, 32);
|
|
} else {
|
|
#endif
|
|
(*to.W) ++;
|
|
SET_COMM_FLAGS(State, *to.W, 16);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
State->Flags |= (State->Flags & FLAG_ZF) ? FLAG_OF : 0;
|
|
break;
|
|
// Decrement
|
|
case 1:
|
|
DEBUG_S("DEC (RX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); //Get Register Value
|
|
if(ret) return ret;
|
|
|
|
State->Flags &= ~(FLAG_OF|FLAG_ZF|FLAG_SF|FLAG_PF);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
(*to.D) --;
|
|
SET_COMM_FLAGS(State, *to.D, 32);
|
|
State->Flags |= (*to.D == 0xFFFFFFFF) ? FLAG_OF : 0;
|
|
} else {
|
|
#endif
|
|
(*to.W) --;
|
|
SET_COMM_FLAGS(State, *to.W, 16);
|
|
State->Flags |= (*to.W == 0xFFFF) ? FLAG_OF : 0;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
case 2:
|
|
DEBUG_S("CALL (RX) NEAR");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); //Get Register Value
|
|
if(ret) return ret;
|
|
PUSH( State->IP + State->Decoder.IPOffset );
|
|
DEBUG_S(" (0x%x)", *to.W);
|
|
State->IP = *to.W;
|
|
goto ret;
|
|
case 3:
|
|
ERROR_S("CALL (MX) FAR --NI--\n");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
case 4:
|
|
DEBUG_S("JMP (RX) NEAR");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); //Get Register Value
|
|
if(ret) return ret;
|
|
DEBUG_S(" (0x%x)", *to.W);
|
|
State->IP = *to.W;
|
|
goto ret;
|
|
case 5:
|
|
ERROR_S("JMP (MX) FAR --NI--\n");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
case 6:
|
|
DEBUG_S("PUSH (RX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W); //Get Register Value
|
|
if(ret) return ret;
|
|
PUSH( *to.W );
|
|
break;
|
|
case 7:
|
|
ERROR_S("0xFF /7 - Undefined\n");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
break;
|
|
|
|
// --- TEST ---
|
|
case TEST_RM: DEBUG_S("TEST (RM)"); //Test Register
|
|
ret = RME_Int_ParseModRM(State, &toB, &fromB);
|
|
if(ret) return ret;
|
|
RME_Int_DoTest(State, *toB, *fromB, 8);
|
|
break;
|
|
case TEST_RMX: DEBUG_S("TEST (RMX)"); //Test Register Extended
|
|
ret = RME_Int_ParseModRMX(State, &to.W, &from.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
RME_Int_DoTest(State, *to.D, *from.D, 32);
|
|
else
|
|
#endif
|
|
RME_Int_DoTest(State, *to.W, *from.W, 16);
|
|
break;
|
|
case TEST_AI: DEBUG_S("TEST (AI)");
|
|
READ_INSTR8( pt2 );
|
|
RME_Int_DoTest(State, State->AX.B.L, pt2, 8);
|
|
break;
|
|
case TEST_AIX: DEBUG_S("TEST (AIX)");
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
READ_INSTR32( dword );
|
|
DEBUG_S(" EAX 0x%x", dword);
|
|
RME_Int_DoTest(State, State->AX.D, dword, 32);
|
|
} else {
|
|
#endif
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(" AX 0x%x", pt2);
|
|
RME_Int_DoTest(State, State->AX.W, pt2, 16);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
// Flag Control
|
|
case CLC: DEBUG_S("CLC"); State->Flags &= ~FLAG_CF; break;
|
|
case STC: DEBUG_S("STC"); State->Flags |= FLAG_CF; break;
|
|
case CLI: DEBUG_S("CLI");
|
|
State->Flags &= ~FLAG_IF;
|
|
//if(State->bReflectIF) __asm__ __volatile__ ("cli");
|
|
break;
|
|
case STI: DEBUG_S("STI");
|
|
State->Flags |= FLAG_IF;
|
|
//if(State->bReflectIF) __asm__ __volatile__ ("sti");
|
|
break;
|
|
case CLD: DEBUG_S("CLD"); State->Flags &= ~FLAG_DF; break;
|
|
case STD: DEBUG_S("STD"); State->Flags |= FLAG_DF; break;
|
|
|
|
// INC Register
|
|
CASE8(INC_A):
|
|
DEBUG_S("INC");
|
|
to.W = RegW(State, opcode&7);
|
|
State->Flags &= ~(FLAG_OF|FLAG_ZF|FLAG_SF|FLAG_PF);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
(*to.D) ++;
|
|
SET_COMM_FLAGS(State, *to.D, 32);
|
|
} else {
|
|
#endif
|
|
(*to.W) ++;
|
|
SET_COMM_FLAGS(State, *to.W, 16);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
State->Flags |= (State->Flags & FLAG_ZF) ? FLAG_OF : 0;
|
|
break;
|
|
// DEC Register
|
|
CASE8(DEC_A):
|
|
DEBUG_S("DEC");
|
|
to.W = RegW(State, opcode&7);
|
|
State->Flags &= ~(FLAG_OF|FLAG_ZF|FLAG_SF|FLAG_PF);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
(*to.D) --;
|
|
SET_COMM_FLAGS(State, *to.D, 32);
|
|
State->Flags |= (*to.D == 0xFFFFFFFF) ? FLAG_OF : 0;
|
|
} else {
|
|
#endif
|
|
(*to.W) --;
|
|
SET_COMM_FLAGS(State, *to.W, 16);
|
|
State->Flags |= (*to.W == 0xFFFF) ? FLAG_OF : 0;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
// ==== Port IO ====
|
|
// IN <port>, A
|
|
case IN_AI: // Imm8, AL
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S("IN (AI) 0x%x AL", pt2);
|
|
ret = inB( State, pt2, &State->AX.B.L );
|
|
if(ret) return ret;
|
|
break;
|
|
case IN_AIX: // Imm8, AX
|
|
READ_INSTR8( pt2 );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand ) {
|
|
DEBUG_S("IN (AIX) 0x%x EAX", pt2);
|
|
ret = inD( State, pt2, &State->AX.D );
|
|
} else {
|
|
#endif
|
|
DEBUG_S("IN (AIX) 0x%x AX", pt2);
|
|
ret = inW( State, pt2, &State->AX.W );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
case IN_ADx: // DX, AL
|
|
DEBUG_S("IN (ADx) DX AL");
|
|
ret = inB(State, State->DX.W, &State->AX.B.L);
|
|
if(ret) return ret;
|
|
break;
|
|
case IN_ADxX: // DX, AX
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand ) {
|
|
DEBUG_S("IN (ADxX) DX EAX");
|
|
ret = inD(State, State->DX.W, &State->AX.D);
|
|
} else {
|
|
#endif
|
|
DEBUG_S("IN (ADxX) DX AX");
|
|
ret = inW(State, State->DX.W, &State->AX.W);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// OUT <port>, A
|
|
case OUT_IA: // Imm8, AL
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S("OUT (IA) 0x%x AL", pt2);
|
|
ret = outB( State, pt2, State->AX.B.L );
|
|
if(ret) return ret;
|
|
break;
|
|
case OUT_IAX: // Imm8, AX
|
|
READ_INSTR8( pt2 );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand ) {
|
|
DEBUG_S("OUT (IAX) 0x%x EAX", pt2);
|
|
ret = outD( State, pt2, State->AX.D );
|
|
} else {
|
|
#endif
|
|
DEBUG_S("OUT (IAX) 0x%x AX", pt2);
|
|
ret = outW( State, pt2, State->AX.W );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
case OUT_DxA: // DX, AL
|
|
DEBUG_S("OUT (DxA) DX AL");
|
|
ret = outB( State, State->DX.W, State->AX.B.L );
|
|
if(ret) return ret;
|
|
break;
|
|
case OUT_DxAX: // DX, AX
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand ) {
|
|
DEBUG_S("OUT (DxAX) DX EAX");
|
|
ret = outD( State, State->DX.W, State->AX.D );
|
|
} else {
|
|
#endif
|
|
DEBUG_S("OUT (DxAX) DX AX");
|
|
ret = outW( State, State->DX.W, State->AX.W );
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
// ==== Software Interrupts ====
|
|
case INT3:
|
|
DEBUG_S("INT 3");
|
|
// High-Level Emulation Call
|
|
if( State->HLECallbacks[3] ) {
|
|
State->HLECallbacks[3](State, 3);
|
|
break;
|
|
}
|
|
// Full emulation then
|
|
ret = RME_Int_Read16(State, 0, 3*4, &pt1); // Offset
|
|
if(ret) return ret;
|
|
ret = RME_Int_Read16(State, 0, 3*4+2, &pt2); // Segment
|
|
if(ret) return ret;
|
|
PUSH( State->Flags );
|
|
PUSH( State->CS );
|
|
PUSH( State->IP );
|
|
State->IP = pt1;
|
|
State->CS = pt2;
|
|
goto ret; // Don't change IP after the instruction
|
|
case INT_I:
|
|
READ_INSTR8( byte2 );
|
|
DEBUG_S("INT 0x%x", byte2);
|
|
// High-Level Emulation Call
|
|
if( State->HLECallbacks[byte2] ) {
|
|
State->HLECallbacks[byte2](State, byte2);
|
|
break;
|
|
}
|
|
// Full emulation then
|
|
ret = RME_Int_Read16(State, 0, (int)byte2*4, &pt1); // Offset
|
|
if(ret) return ret;
|
|
ret = RME_Int_Read16(State, 0, (int)byte2*4+2, &pt2); // Segment
|
|
if(ret) return ret;
|
|
|
|
if(pt1 == 0 && pt2 == 0) {
|
|
ERROR_S(" Caught attempt to execute IVT pointing to 0000:0000");
|
|
return RME_ERR_BADMEM;
|
|
}
|
|
|
|
PUSH( State->Flags );
|
|
PUSH( State->CS );
|
|
PUSH( State->IP );
|
|
State->IP = pt1;
|
|
State->CS = pt2;
|
|
goto ret; // Don't change IP after the instruction
|
|
case IRET: DEBUG_S("IRET");
|
|
POP( State->IP );
|
|
POP( State->CS );
|
|
POP( State->Flags );
|
|
goto ret; // Don't change IP after the instruction
|
|
|
|
// ==== MOV ====
|
|
case MOV_MoA: // Store AL at Memory Offset
|
|
DEBUG_S("MOV (MoA)");
|
|
seg = *GET_SEGMENT(State, SREG_DS);
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(":0x%x AL", pt2);
|
|
ret = RME_Int_Write8(State, seg, pt2, State->AX.W & 0xFF);
|
|
if(ret) return ret;
|
|
break;
|
|
case MOV_MoAX: // Store AX at Memory Offset
|
|
DEBUG_S("MOV (MoAX)");
|
|
seg = *GET_SEGMENT(State, SREG_DS);
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(":0x%x", pt2);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
DEBUG_S(" EAX");
|
|
ret = RME_Int_Write32(State, seg, pt2, State->AX.D);
|
|
} else {
|
|
#endif
|
|
DEBUG_S(" AX");
|
|
ret = RME_Int_Write16(State, seg, pt2, State->AX.W);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
if(ret) return ret;
|
|
break;
|
|
case MOV_AMo: // Memory Offset to AL
|
|
DEBUG_S("MOV (AMo) AL");
|
|
seg = *GET_SEGMENT(State, SREG_DS);
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(":0x%x", pt2);
|
|
ret = RME_Int_Read8(State, seg, pt2, (uint8_t*)&State->AX.W);
|
|
if(ret) return ret;
|
|
break;
|
|
case MOV_AMoX: // Memory Offset to AX
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
DEBUG_S("MOV (AMoX) EAX");
|
|
else
|
|
#endif
|
|
DEBUG_S("MOV (AMoX) AX");
|
|
seg = *GET_SEGMENT(State, SREG_DS);
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(":0x%x", pt2);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
ret = RME_Int_Read32(State, State->DS, pt2, &State->AX.D);
|
|
else
|
|
#endif
|
|
ret = RME_Int_Read16(State, State->DS, pt2, &State->AX.W);
|
|
if(ret) return ret;
|
|
break;
|
|
case MOV_MI: // Store Immediate at Memory
|
|
DEBUG_S("MOV (MI)");
|
|
ret = RME_Int_ParseModRM(State, NULL, &toB);
|
|
if(ret) return ret;
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
*toB = pt2;
|
|
break;
|
|
case MOV_MIX: // Store Immediate at Memory
|
|
DEBUG_S("MOV (RIX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
READ_INSTR32( dword );
|
|
DEBUG_S(" 0x%x", dword);
|
|
*to.D = dword;
|
|
} else {
|
|
#endif
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
*to.W = pt2;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
case MOV_RM: DEBUG_S("MOV (RM)");
|
|
ret = RME_Int_ParseModRM(State, &toB, &fromB);
|
|
if(ret) return ret;
|
|
*toB = *fromB;
|
|
break;
|
|
case MOV_RMX: DEBUG_S("MOV (RMX)");
|
|
ret = RME_Int_ParseModRMX(State, &to.W, &from.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
*to.D = *from.D;
|
|
else
|
|
#endif
|
|
*to.W = *from.W;
|
|
break;
|
|
case MOV_MR: DEBUG_S("MOV (RM) REV");
|
|
ret = RME_Int_ParseModRM(State, &fromB, &toB);
|
|
if(ret) return ret;
|
|
*toB = *fromB;
|
|
break;
|
|
case MOV_MRX: DEBUG_S("MOV (RMX) REV");
|
|
ret = RME_Int_ParseModRMX(State, &from.W, &to.W);
|
|
if(ret) return ret;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
*to.D = *from.D;
|
|
else
|
|
#endif
|
|
*to.W = *from.W;
|
|
break;
|
|
|
|
case MOV_RI_AL: case MOV_RI_CL:
|
|
case MOV_RI_DL: case MOV_RI_BL:
|
|
case MOV_RI_AH: case MOV_RI_CH:
|
|
case MOV_RI_DH: case MOV_RI_BH:
|
|
DEBUG_S("MOV (RI)");
|
|
toB = RegB(State, opcode&7);
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
*toB = pt2;
|
|
break;
|
|
|
|
case MOV_RI_AX: case MOV_RI_CX:
|
|
case MOV_RI_DX: case MOV_RI_BX:
|
|
case MOV_RI_SP: case MOV_RI_BP:
|
|
case MOV_RI_SI: case MOV_RI_DI:
|
|
DEBUG_S("MOV (RIX)");
|
|
to.W = RegW(State, opcode&7);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
READ_INSTR16( dword );
|
|
DEBUG_S(" 0x%x", dword);
|
|
*to.D = dword;
|
|
} else {
|
|
#endif
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S(" 0x%x", pt2);
|
|
*to.W = pt2;
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
// Segment Registers
|
|
case MOV_RS:
|
|
DEBUG_S("MOV (RS)");
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
from.W = Seg(State, (byte2>>3)&7);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
*to.W = *from.W;
|
|
break;
|
|
|
|
case MOV_SR:
|
|
DEBUG_S("MOV (SR)");
|
|
READ_INSTR8( byte2 ); State->Decoder.IPOffset --;
|
|
to.W = Seg(State, (byte2>>3)&7);
|
|
ret = RME_Int_ParseModRMX(State, NULL, &from.W);
|
|
if(ret) return ret;
|
|
*to.W = *from.W;
|
|
break;
|
|
|
|
|
|
// === JMP Family ===
|
|
case JMP_S: // Short Jump
|
|
READ_INSTR8S( pt2 );
|
|
DEBUG_S("JMP (S) .+0x%x", pt2);
|
|
State->IP += pt2;
|
|
//if(pt2 == 0xFFFE) return RME_ERR_BUG;
|
|
break;
|
|
case JMP_N: // Near Jump
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S("JMP (N) .+0x%x", pt2 );
|
|
State->IP += pt2;
|
|
//if(pt2 == 0xFFFE) return RME_ERR_BUG;
|
|
break;
|
|
case JMP_F: // Far Jump
|
|
READ_INSTR16( pt1 );
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S("JMP FAR %x:%x", pt2, pt1);
|
|
State->CS = pt2; State->IP = pt1;
|
|
goto ret;
|
|
|
|
// === XCHG Family ===
|
|
case XCHG_AA: // NOP 0x90
|
|
DEBUG_S("NOP");
|
|
break;
|
|
case XCHG_AC:
|
|
case XCHG_AD: case XCHG_AB:
|
|
case XCHG_ASp: case XCHG_ABp:
|
|
case XCHG_ASi: case XCHG_ADi:
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
DEBUG_S("XCHG EAX");
|
|
else
|
|
#endif
|
|
DEBUG_S("XCHG AX");
|
|
from.W = RegW(State, opcode&7);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
XCHG(State->AX.D, *from.D);
|
|
else
|
|
#endif
|
|
XCHG(State->AX.W, *from.W);
|
|
break;
|
|
|
|
case XCHG_RM:
|
|
DEBUG_S("XCHG (RM)");
|
|
ret = RME_Int_ParseModRMX(State, &to.W, &from.W);
|
|
if(ret) return ret;
|
|
// Note: Check for BX-BX for a breakpoint?
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand)
|
|
XCHG(*to.D, *from.D);
|
|
else
|
|
#endif
|
|
XCHG(*to.W, *from.W);
|
|
break;
|
|
|
|
// PUSH Family
|
|
case PUSHF:
|
|
DEBUG_S("PUSHF");
|
|
PUSH(State->Flags);
|
|
break;
|
|
case PUSHA:
|
|
DEBUG_S("PUSHA");
|
|
pt2 = State->SP.W;
|
|
PUSH(State->AX.W); PUSH(State->CX.W);
|
|
PUSH(State->DX.W); PUSH(State->BX.W);
|
|
PUSH(pt2); PUSH(State->BP.W);
|
|
PUSH(State->SI.W); PUSH(State->DI.W);
|
|
break;
|
|
case PUSH_AX: DEBUG_S("PUSH AX"); PUSH(State->AX.W); break;
|
|
case PUSH_BX: DEBUG_S("PUSH BX"); PUSH(State->BX.W); break;
|
|
case PUSH_CX: DEBUG_S("PUSH CX"); PUSH(State->CX.W); break;
|
|
case PUSH_DX: DEBUG_S("PUSH DX"); PUSH(State->DX.W); break;
|
|
case PUSH_SP: DEBUG_S("PUSH Sp"); pt2 = State->SP.W; PUSH(pt2); break;
|
|
case PUSH_BP: DEBUG_S("PUSH BP"); PUSH(State->BP.W); break;
|
|
case PUSH_SI: DEBUG_S("PUSH SI"); PUSH(State->SI.W); break;
|
|
case PUSH_DI: DEBUG_S("PUSH DI"); PUSH(State->DI.W); break;
|
|
case PUSH_ES: DEBUG_S("PUSH ES"); PUSH(State->ES); break;
|
|
case PUSH_CS: DEBUG_S("PUSH CS"); PUSH(State->CS); break;
|
|
case PUSH_SS: DEBUG_S("PUSH SS"); PUSH(State->SS); break;
|
|
case PUSH_DS: DEBUG_S("PUSH DS"); PUSH(State->DS); break;
|
|
case PUSH_I8:
|
|
READ_INSTR8( pt2 );
|
|
DEBUG_S("PUSH (I8) 0x%x", pt2);
|
|
PUSH(pt2);
|
|
break;
|
|
case PUSH_I:
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S("PUSH (I) 0x%x", pt2);
|
|
PUSH(pt2);
|
|
break;
|
|
|
|
//POP Family
|
|
case POPF:
|
|
DEBUG_S("POPF");
|
|
POP(State->Flags);
|
|
break;
|
|
case POPA:
|
|
DEBUG_S("POPA");
|
|
POP(State->DI.W); POP(State->SI.W);
|
|
POP(State->BP.W); State->SP.W += 2;
|
|
POP(State->BX.W); POP(State->DX.W);
|
|
POP(State->CX.W); POP(State->AX.W);
|
|
break;
|
|
case POP_AX: DEBUG_S("POP AX"); POP(State->AX.W); break;
|
|
case POP_CX: DEBUG_S("POP CX"); POP(State->CX.W); break;
|
|
case POP_DX: DEBUG_S("POP DX"); POP(State->DX.W); break;
|
|
case POP_BX: DEBUG_S("POP BX"); POP(State->BX.W); break;
|
|
// The POP macro is indirect, so no need to do anthing special
|
|
case POP_SP: DEBUG_S("POP SP"); POP(State->SP.W); break;
|
|
case POP_BP: DEBUG_S("POP BP"); POP(State->BP.W); break;
|
|
case POP_SI: DEBUG_S("POP SI"); POP(State->SI.W); break;
|
|
case POP_DI: DEBUG_S("POP DI"); POP(State->DI.W); break;
|
|
case POP_ES: DEBUG_S("POP ES"); POP(State->ES); break;
|
|
case POP_SS: DEBUG_S("POP SS"); POP(State->SS); break;
|
|
case POP_DS: DEBUG_S("POP DS"); POP(State->DS); break;
|
|
|
|
case POP_MX:
|
|
DEBUG_S("POP (MX)");
|
|
ret = RME_Int_ParseModRMX(State, NULL, &to.W);
|
|
if(ret) return ret;
|
|
POP(*to.W);
|
|
break;
|
|
|
|
// === CALL Family ===
|
|
case CALL_N:
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S("CALL (N) .+0x%x", pt2);
|
|
State->IP += State->Decoder.IPOffset;
|
|
PUSH(State->IP);
|
|
State->IP += pt2;
|
|
goto ret;
|
|
case CALL_F:
|
|
READ_INSTR16( pt1 );
|
|
READ_INSTR16( pt2 );
|
|
DEBUG_S("CALL (F) %x:%x", pt2, pt1);
|
|
PUSH(State->CS);
|
|
PUSH(State->IP + State->Decoder.IPOffset);
|
|
State->CS = pt2;
|
|
State->IP = pt1;
|
|
goto ret;
|
|
case RET_N:
|
|
DEBUG_S("RET (N)");
|
|
POP(State->IP);
|
|
goto ret;
|
|
case RET_F:
|
|
DEBUG_S("RET (F)");
|
|
POP(State->IP);
|
|
POP(State->CS);
|
|
goto ret;
|
|
|
|
// -- String Operations --
|
|
// Move
|
|
case MOVSB: DEBUG_S("MOVS");
|
|
DEBUG_S(" DS:[SI]"); // TODO: Address Overrides
|
|
DEBUG_S(" ES:[DI]");
|
|
if( repType == REP ) {
|
|
DEBUG_S(" (0x%x times)", State->CX.W);
|
|
if( State->CX.W == 0 ) { repType = 0; break; }
|
|
}
|
|
do {
|
|
uint8_t tmp;
|
|
ret = RME_Int_Read8(State, State->DS, State->SI.W, &tmp);
|
|
if(ret) return ret;
|
|
ret = RME_Int_Write8(State, State->ES, State->DI.W, tmp);
|
|
if(ret) return ret;
|
|
if(State->Flags & FLAG_DF) {
|
|
State->SI.W --;
|
|
State->DI.W --;
|
|
}
|
|
else {
|
|
State->DI.W ++;
|
|
State->SI.W ++;
|
|
}
|
|
} while(repType && --State->CX.W);
|
|
repType = 0;
|
|
break;
|
|
case MOVSW: DEBUG_S("MOVSW");
|
|
DEBUG_S(" DS:[SI]"); // TODO: Address Overrides
|
|
DEBUG_S(" ES:[DI]");
|
|
if( repType == REP ) {
|
|
DEBUG_S(" (0x%x times)", State->CX.W);
|
|
if( State->CX.W == 0 ) { repType = 0; break; }
|
|
}
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
{
|
|
int step = 4;
|
|
if(State->Flags & FLAG_DF) step = -step;
|
|
do {
|
|
uint32_t tmp;
|
|
ret = RME_Int_Read32(State, State->DS, State->SI.W, &tmp);
|
|
if(ret) return ret;
|
|
ret = RME_Int_Write32(State, State->ES, State->DI.W, tmp);
|
|
if(ret) return ret;
|
|
State->DI.W += step;
|
|
State->SI.W += step;
|
|
} while(repType == REP && --State->CX.W);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int step = 2;
|
|
if(State->Flags & FLAG_DF) step = -step;
|
|
do {
|
|
uint16_t tmp;
|
|
ret = RME_Int_Read16(State, State->DS, State->SI.W, &tmp);
|
|
if(ret) return ret;
|
|
ret = RME_Int_Write16(State, State->ES, State->DI.W, tmp);
|
|
if(ret) return ret;
|
|
State->DI.W += step;
|
|
State->SI.W += step;
|
|
} while(repType && --State->CX.W);
|
|
}
|
|
repType = 0;
|
|
break;
|
|
|
|
// Compare String
|
|
case CMPSB: DEBUG_S("CMPSB");
|
|
DEBUG_S(" ES:[DI]");
|
|
DEBUG_S(" DS:[SI]"); // TODO: Address Override
|
|
if(repType) DEBUG_S(" (limit 0x%x)", State->CX.W);
|
|
// Check for initial CX of zero
|
|
if(repType && State->CX.W == 0) { repType = 0; break; }
|
|
// Do the operation
|
|
do {
|
|
uint8_t byte1, byte2;
|
|
int v;
|
|
ret = RME_Int_Read8(State, State->DS, State->SI.W, &byte1);
|
|
if(ret) return ret;
|
|
ret = RME_Int_Read8(State, State->ES, State->DI.W, &byte2);
|
|
if(ret) return ret;
|
|
|
|
#if DEBUG >= 2
|
|
DEBUG_S(" %x==%x", byte1, byte2);
|
|
#endif
|
|
|
|
// Increment
|
|
if(State->Flags & FLAG_DF) {
|
|
State->SI.W --;
|
|
State->DI.W --;
|
|
}
|
|
else {
|
|
State->SI.W ++;
|
|
State->DI.W ++;
|
|
}
|
|
|
|
// Do Comparison
|
|
v = byte1 - byte2;
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);
|
|
SET_COMM_FLAGS(State, v, 8);
|
|
State->Flags |= (v < 0) ? FLAG_OF|FLAG_CF : 0;
|
|
|
|
if(repType == REP && !(State->Flags & FLAG_ZF) )
|
|
break;
|
|
if(repType == REPNZ && (State->Flags & FLAG_ZF) )
|
|
break;
|
|
} while(repType && --State->CX.W);
|
|
repType = 0;
|
|
break;
|
|
case CMPSW: DEBUG_S("CMPSW");
|
|
ERROR_S(" TODO: Implement CMPSW");
|
|
return RME_ERR_UNDEFOPCODE;
|
|
|
|
// Store String
|
|
case STOSB: DEBUG_S("STOSB");
|
|
DEBUG_S(" ES:[DI]"); // TODO: Address Override
|
|
DEBUG_S(" AL");
|
|
if( repType == REP ) {
|
|
DEBUG_S(" (0x%x times)", State->CX.W);
|
|
if( State->CX.W == 0 ) { repType = 0; break; }
|
|
}
|
|
do {
|
|
ret = RME_Int_Write8(State, State->ES, State->DI.W, State->AX.B.L);
|
|
if(ret) return ret;
|
|
if(State->Flags & FLAG_DF)
|
|
State->DI.W --;
|
|
else
|
|
State->DI.W ++;
|
|
} while(repType == REP && --State->CX.W);
|
|
repType = 0;
|
|
break;
|
|
case STOSW: DEBUG_S("STOSW");
|
|
DEBUG_S(" ES:[DI]"); // TODO: Address Override
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
DEBUG_S(" EAX");
|
|
else
|
|
#endif
|
|
DEBUG_S(" AX");
|
|
|
|
if( repType == REP ) {
|
|
DEBUG_S(" (0x%x times)", State->CX.W);
|
|
if( State->CX.W == 0 ) { repType = 0; break; }
|
|
}
|
|
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
{
|
|
int step = 4;
|
|
if(State->Flags & FLAG_DF) step = -step;
|
|
do {
|
|
ret = RME_Int_Write32(State, State->ES, State->DI.W, State->AX.D);
|
|
if(ret) return ret;
|
|
State->DI.W += step;
|
|
} while(repType == REP && --State->CX.W);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int step = 2;
|
|
if(State->Flags & FLAG_DF) step = -step;
|
|
do {
|
|
ret = RME_Int_Write16(State, State->ES, State->DI.W, State->AX.W);
|
|
if(ret) return ret;
|
|
State->DI.W += step;
|
|
} while(repType == REP && --State->CX.W);
|
|
}
|
|
repType = 0;
|
|
break;
|
|
|
|
// Load String
|
|
case LODSB: DEBUG_S("LODS AL");
|
|
DEBUG_S(" DS:[SI]"); // TODO: Address Overrides
|
|
if( repType == REP ) {
|
|
DEBUG_S(" (0x%x times)", State->CX.W);
|
|
if( State->CX.W == 0 ) { repType = 0; break; }
|
|
}
|
|
do {
|
|
ret = RME_Int_Read8(State, State->DS, State->SI.W, &State->AX.B.L);
|
|
if(ret) return ret;
|
|
if(State->Flags & FLAG_DF)
|
|
State->SI.W --;
|
|
else
|
|
State->SI.W ++;
|
|
} while(repType == REP && --State->CX.W);
|
|
repType = 0;
|
|
break;
|
|
case LODSW: DEBUG_S("LODS");
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
DEBUG_S(" EAX");
|
|
else
|
|
#endif
|
|
DEBUG_S(" AX");
|
|
DEBUG_S(" DS:[SI]"); // TODO: Address Overrides
|
|
|
|
if( repType == REP ) {
|
|
DEBUG_S(" (0x%x times)", State->CX.W);
|
|
if( State->CX.W == 0 ) { repType = 0; break; }
|
|
}
|
|
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
{
|
|
int step = 4;
|
|
if(State->Flags & FLAG_DF) step = -step;
|
|
do {
|
|
ret = RME_Int_Read32(State, State->DS, State->SI.W, &State->AX.D);
|
|
if(ret) return ret;
|
|
State->SI.W += step;
|
|
} while(repType == REP && --State->CX.W);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int step = 2;
|
|
if(State->Flags & FLAG_DF) step = -step;
|
|
do {
|
|
ret = RME_Int_Read16(State, State->DS, State->SI.W, &State->AX.W);
|
|
if(ret) return ret;
|
|
State->SI.W += step;
|
|
} while(repType == REP && --State->CX.W);
|
|
}
|
|
repType = 0;
|
|
break;
|
|
|
|
// === Misc ===
|
|
// LES - Load ES:r(16,32) with m16:m(16,32)
|
|
case LES:
|
|
DEBUG_S("LES");
|
|
ret = RME_Int_ParseModRMX(State, &to.W, &from.W);
|
|
if(ret) return ret;
|
|
State->ES = *from.W;
|
|
from.W = (void*)((intptr_t)from.W + 2);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
*to.D = *from.D;
|
|
else
|
|
#endif
|
|
*to.W = *from.W;
|
|
break;
|
|
// LDS - Load DS:r(16,32) with m16:m(16,32)
|
|
case LDS:
|
|
DEBUG_S("LDS");
|
|
ret = RME_Int_ParseModRMX(State, &to.W, &from.W);
|
|
if(ret) return ret;
|
|
State->DS = *from.W;
|
|
from.W = (void*)((intptr_t)from.W + 2);
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if( State->Decoder.bOverrideOperand )
|
|
*to.D = *from.D;
|
|
else
|
|
#endif
|
|
*to.W = *from.W;
|
|
break;
|
|
|
|
// Load effective address
|
|
case LEA:
|
|
DEBUG_S("LEA");
|
|
// LEA parses the address itself, because it needs the
|
|
// emulated address, not the true address.
|
|
// Hence, it cannot use RME_Int_ParseModRM/W
|
|
READ_INSTR8(byte2);
|
|
switch(byte2 >> 6)
|
|
{
|
|
case 0: // No Offset
|
|
pt1 = 0;
|
|
break;
|
|
case 1: // 8-bit signed
|
|
READ_INSTR8S(pt1);
|
|
break;
|
|
case 2: // 16-bit
|
|
READ_INSTR16(pt1);
|
|
break;
|
|
case 3: // Register -- ERROR!!!
|
|
return RME_ERR_UNDEFOPCODE;
|
|
default:
|
|
return RME_ERR_BUG;
|
|
}
|
|
|
|
to.W = RegW( State, (byte2>>3)&7 );
|
|
|
|
switch(byte2 & 7)
|
|
{
|
|
case 0:
|
|
DEBUG_S(" DS:[BX+SI+0x%x]", pt1);
|
|
pt1 += State->BX.W + State->SI.W;
|
|
break;
|
|
case 1:
|
|
DEBUG_S(" DS:[BX+DI+0x%x]", pt1);
|
|
pt1 += State->BX.W + State->DI.W;
|
|
break;
|
|
case 2:
|
|
DEBUG_S(" SS:[BP+SI+0x%x]", pt1);
|
|
pt1 += State->BP.W + State->SI.W;
|
|
break;
|
|
case 3:
|
|
DEBUG_S(" SS:[BP+DI+0x%x]", pt1);
|
|
pt1 += State->BP.W + State->DI.W;
|
|
break;
|
|
case 4:
|
|
DEBUG_S(" DS:[SI+0x%x]", pt1);
|
|
pt1 += State->SI.W;
|
|
break;
|
|
case 5:
|
|
DEBUG_S(" DS:[DI+0x%x]", pt1);
|
|
pt1 += State->DI.W;
|
|
break;
|
|
case 6:
|
|
if( (byte2 >> 6) == 0 ) {
|
|
READ_INSTR16(pt1);
|
|
DEBUG_S(" DS:[0x%x]", pt1);
|
|
} else {
|
|
DEBUG_S(" SS:[BP+0x%x]", pt1);
|
|
pt1 += State->BP.W;
|
|
}
|
|
break;
|
|
case 7:
|
|
DEBUG_S(" DS:[BX+0x%x]", pt1);
|
|
pt1 += State->BX.W;
|
|
break;
|
|
}
|
|
*to.W = pt1;
|
|
break;
|
|
|
|
// -- Loops --
|
|
case LOOP: DEBUG_S("LOOP ");
|
|
READ_INSTR8S( pt2 );
|
|
DEBUG_S(".+0x%x", pt2);
|
|
State->CX.W --;
|
|
if(State->CX.W != 0)
|
|
State->IP += pt2;
|
|
break;
|
|
case LOOPNZ: DEBUG_S("LOOPNZ ");
|
|
READ_INSTR8S( pt2 );
|
|
DEBUG_S(".+0x%x", pt2);
|
|
State->CX.W --;
|
|
if(State->CX.W != 0 && !(State->Flags & FLAG_ZF))
|
|
State->IP += pt2;
|
|
break;
|
|
case LOOPZ: DEBUG_S("LOOPZ ");
|
|
READ_INSTR8S( pt2 );
|
|
DEBUG_S(".+0x%x", pt2);
|
|
State->CX.W --;
|
|
if(State->CX.W != 0 && State->Flags & FLAG_ZF)
|
|
State->IP += pt2;
|
|
break;
|
|
|
|
// Short Jumps
|
|
CASE16(0x70):
|
|
READ_INSTR8S( pt2 );
|
|
ret = RME_Int_DoCondJMP(State, opcode & 0xF, pt2, "(S)");
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
|
|
// -- Two Byte Opcodes --
|
|
case 0x0F:
|
|
READ_INSTR8( byte2 );
|
|
|
|
switch(byte2)
|
|
{
|
|
//--- Near Jump --- (1000cccc)
|
|
CASE16(0x80):
|
|
READ_INSTR16( pt2 );
|
|
ret = RME_Int_DoCondJMP(State, byte2&0xF, pt2, "(N)");
|
|
if(ret) return ret;
|
|
break;
|
|
|
|
default:
|
|
ERROR_S("0x0F 0x%x unknown\n", byte2);
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_S("Unknown Opcode 0x%x at 0x%x\n", opcode, State->IP);
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
|
|
// repType is cleared if it is used, so if it's not used, it's invalid
|
|
if(repType)
|
|
{
|
|
DEBUG_S("Prefix 0x%x used with wrong opcode 0x%x\n", repType, opcode);
|
|
return RME_ERR_UNDEFOPCODE;
|
|
}
|
|
|
|
State->IP += State->Decoder.IPOffset;
|
|
|
|
ret:
|
|
#if DEBUG
|
|
{
|
|
uint16_t i = startIP;
|
|
uint8_t byte;
|
|
int j = State->Decoder.IPOffset;
|
|
|
|
DEBUG_S("\t;");
|
|
while(i < 0x10000 && j--) {
|
|
ret = RME_Int_Read8(State, startCS, i, &byte);
|
|
DEBUG_S(" %x", byte);
|
|
i ++;
|
|
}
|
|
if(j > 0)
|
|
{
|
|
while(j--) {
|
|
ret = RME_Int_Read8(State, startCS, i, &byte);
|
|
DEBUG_S(" %x", byte);
|
|
i ++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
DEBUG_S("\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert an eumulated Segment:Offset address into a host pointer
|
|
* \param State Emulator State
|
|
* \param Seg Segment
|
|
* \param Offset Offset
|
|
* \param Ptr Pointer to a void* where the final pointer will be stored
|
|
*/
|
|
static inline int RME_Int_GetPtr(tRME_State *State, uint16_t Seg, uint16_t Ofs, void* *Ptr)
|
|
{
|
|
uint32_t addr = Seg * 16 + Ofs;
|
|
#if RME_DO_NULL_CHECK
|
|
# if RME_ALLOW_ZERO_TO_BE_NULL
|
|
if(addr/RME_BLOCK_SIZE && State->Memory[addr/RME_BLOCK_SIZE] == NULL)
|
|
# else
|
|
if(State->Memory[addr/RME_BLOCK_SIZE] == NULL)
|
|
# endif
|
|
return RME_ERR_BADMEM;
|
|
#endif
|
|
*Ptr = &State->Memory[addr/RME_BLOCK_SIZE][addr%RME_BLOCK_SIZE];
|
|
return 0;
|
|
}
|
|
|
|
static inline int RME_Int_Read8(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint8_t *Dst)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
ret = RME_Int_GetPtr(State, Seg, Ofs, (void**)&ptr);
|
|
if(ret) return ret;
|
|
*Dst = *(uint8_t*)ptr;
|
|
return 0;
|
|
}
|
|
|
|
static inline int RME_Int_Read16(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint16_t *Dst)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
ret = RME_Int_GetPtr(State, Seg, Ofs, (void**)&ptr);
|
|
if(ret) return ret;
|
|
*Dst = *(uint16_t*)ptr;
|
|
return 0;
|
|
}
|
|
|
|
static inline int RME_Int_Read32(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint32_t *Dst)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
ret = RME_Int_GetPtr(State, Seg, Ofs, (void**)&ptr);
|
|
if(ret) return ret;
|
|
*Dst = *(uint32_t*)ptr;
|
|
return 0;
|
|
}
|
|
|
|
static inline int RME_Int_Write8(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint8_t Val)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
ret = RME_Int_GetPtr(State, Seg, Ofs, (void**)&ptr);
|
|
if(ret) return ret;
|
|
*(uint8_t*)ptr = Val;
|
|
return 0;
|
|
}
|
|
|
|
static inline int RME_Int_Write16(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint16_t Val)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
ret = RME_Int_GetPtr(State, Seg, Ofs, (void**)&ptr);
|
|
if(ret) return ret;
|
|
*(uint16_t*)ptr = Val;
|
|
return 0;
|
|
}
|
|
|
|
static inline int RME_Int_Write32(tRME_State *State, uint16_t Seg, uint16_t Ofs, uint32_t Val)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
ret = RME_Int_GetPtr(State, Seg, Ofs, (void**)&ptr);
|
|
if(ret) return ret;
|
|
*(uint32_t*)ptr = Val;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief 0 - Add
|
|
* \todo Set AF
|
|
*/
|
|
#define RME_Int_DoAdd(State, to, from, width) do{\
|
|
(to) += (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to) < (from)) ? FLAG_OF|FLAG_CF : 0;\
|
|
}while(0)
|
|
/**
|
|
* \brief 1 - Bitwise OR
|
|
*/
|
|
#define RME_Int_DoOr(State, to, from, width) do{\
|
|
(to) |= (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
}while(0)
|
|
/**
|
|
* \brief 2 - Add with Carry
|
|
*/
|
|
#define RME_Int_DoAdc(State, to, from, width) do{\
|
|
(to) += (from) + ((State->Flags&FLAG_CF)?1:0);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to) < (from)) ? FLAG_OF|FLAG_CF : 0;\
|
|
}while(0)
|
|
/**
|
|
* \brief 3 - Subtract with borrow
|
|
*/
|
|
#define RME_Int_DoSbb(State, to, from, width) do{\
|
|
int v = (to) - (from) + ((State->Flags&FLAG_CF)?1:0);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to)<(from) || (from)==((1<<((width)-1)-1)|(1<<((width)-1)))) ? FLAG_CF : 0;\
|
|
State->Flags |= (((((to) ^ (from)) & ((to) ^ (v))) & (1<<((width)-1))) != 0) ? FLAG_OF : 0;\
|
|
(to) = v;\
|
|
}while(0)
|
|
// 4: Bitwise AND
|
|
#define RME_Int_DoAnd(State, to, from, width) do{\
|
|
(to) &= (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
}while(0)
|
|
// 5: Subtract
|
|
#define RME_Int_DoSub(State, to, from, width) do{\
|
|
int v = (to) - (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
State->Flags |= ((to)<(from)) ? FLAG_CF : 0;\
|
|
State->Flags |= (((((to) ^ (from)) & ((to) ^ (v))) & (1<<((width)-1))) != 0) ? FLAG_OF : 0;\
|
|
(to) = v;\
|
|
}while(0)
|
|
// 6: Bitwise XOR
|
|
#define RME_Int_DoXor(State, to, from, width) do{\
|
|
(to) ^= (from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,(to),(width));\
|
|
}while(0)
|
|
// 7: Compare (Set flags according to SUB)
|
|
#define RME_Int_DoCmp(State, to, from, width) do{\
|
|
int v = (to)-(from);\
|
|
State->Flags &= ~(FLAG_PF|FLAG_ZF|FLAG_SF|FLAG_OF|FLAG_CF);\
|
|
SET_COMM_FLAGS(State,v,(width));\
|
|
State->Flags |= ((to)<(from)) ? FLAG_CF : 0;\
|
|
State->Flags |= (((((to) ^ (from)) & ((to) ^ (v))) & (1<<((width)-1))) != 0) ? FLAG_OF : 0;\
|
|
}while(0)
|
|
|
|
/**
|
|
* \brief Delegates an Arithmatic Operation to the required helper
|
|
*/
|
|
#define RME_Int_DoArithOp(num, State, to, from, width) do{\
|
|
switch( (num) ) {\
|
|
case 0: RME_Int_DoAdd(State, (to), (from), (width)); break;\
|
|
case 1: RME_Int_DoOr (State, (to), (from), (width)); break;\
|
|
case 2: RME_Int_DoAdc(State, (to), (from), (width)); break;\
|
|
case 3: RME_Int_DoSbb(State, (to), (from), (width)); break;\
|
|
case 4: RME_Int_DoAnd(State, (to), (from), (width)); break;\
|
|
case 5: RME_Int_DoSub(State, (to), (from), (width)); break;\
|
|
case 6: RME_Int_DoXor(State, (to), (from), (width)); break;\
|
|
case 7: RME_Int_DoCmp(State, (to), (from), (width)); break;\
|
|
default: DEBUG_S(" - Undef DoArithOP %d\n", (num)); return RME_ERR_BUG;\
|
|
}}while(0)
|
|
/**
|
|
* \brief Do an arithmatic operation on an 8-bit integer
|
|
*/
|
|
static inline int RME_Int_DoArithOp8(int Num, tRME_State *State, uint8_t *Dest, uint8_t Src)
|
|
{
|
|
RME_Int_DoArithOp(Num, State, *Dest, Src, 8);
|
|
return 0;
|
|
}
|
|
/**
|
|
* \brief Do an arithmatic operation on a 16-bit integer
|
|
*/
|
|
static inline int RME_Int_DoArithOp16(int Num, tRME_State *State, uint16_t *Dest, uint16_t Src)
|
|
{
|
|
RME_Int_DoArithOp(Num, State, *Dest, Src, 16);
|
|
return 0;
|
|
}
|
|
/**
|
|
* \brief Do an arithmatic operation on a 32-bit integer
|
|
*/
|
|
static inline int RME_Int_DoArithOp32(int Num, tRME_State *State, uint32_t *Dest, uint32_t Src)
|
|
{
|
|
RME_Int_DoArithOp(Num, State, *Dest, Src, 32);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a pointer to the specified byte register
|
|
* \param State Emulator State
|
|
* \param num Register Number
|
|
*/
|
|
static inline uint8_t *RegB(tRME_State *State, int num)
|
|
{
|
|
switch(num)
|
|
{
|
|
case 0: DEBUG_S(" AL"); return &State->AX.B.L;
|
|
case 1: DEBUG_S(" CL"); return &State->CX.B.L;
|
|
case 2: DEBUG_S(" DL"); return &State->DX.B.L;
|
|
case 3: DEBUG_S(" BL"); return &State->BX.B.L;
|
|
case 4: DEBUG_S(" AH"); return &State->AX.B.H;
|
|
case 5: DEBUG_S(" CH"); return &State->CX.B.H;
|
|
case 6: DEBUG_S(" DH"); return &State->DX.B.H;
|
|
case 7: DEBUG_S(" BH"); return &State->BX.B.H;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a pointer to the specified word register
|
|
* \param State Emulator State
|
|
* \param num Register Number
|
|
*/
|
|
static inline uint16_t *RegW(tRME_State *State, int num)
|
|
{
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
if(State->Decoder.bOverrideOperand) {
|
|
switch(num)
|
|
{
|
|
case 0: DEBUG_S(" EAX"); return &State->AX.W;
|
|
case 1: DEBUG_S(" ECX"); return &State->CX.W;
|
|
case 2: DEBUG_S(" EDX"); return &State->DX.W;
|
|
case 3: DEBUG_S(" EBX"); return &State->BX.W;
|
|
case 4: DEBUG_S(" ESP"); return &State->SP.W;
|
|
case 5: DEBUG_S(" EBP"); return &State->BP.W;
|
|
case 6: DEBUG_S(" ESI"); return &State->SI.W;
|
|
case 7: DEBUG_S(" EDI"); return &State->DI.W;
|
|
}
|
|
} else {
|
|
#endif
|
|
switch(num)
|
|
{
|
|
case 0: DEBUG_S(" AX"); return &State->AX.W;
|
|
case 1: DEBUG_S(" CX"); return &State->CX.W;
|
|
case 2: DEBUG_S(" DX"); return &State->DX.W;
|
|
case 3: DEBUG_S(" BX"); return &State->BX.W;
|
|
case 4: DEBUG_S(" SP"); return &State->SP.W;
|
|
case 5: DEBUG_S(" BP"); return &State->BP.W;
|
|
case 6: DEBUG_S(" SI"); return &State->SI.W;
|
|
case 7: DEBUG_S(" DI"); return &State->DI.W;
|
|
}
|
|
#if USE_SIZE_OVERRIDES == 1
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a pointer to the specified segment register
|
|
* \param State Emulator State
|
|
* \param code Segment register Number (0 to 3)
|
|
*/
|
|
static inline uint16_t *Seg(tRME_State *State, int code)
|
|
{
|
|
switch(code) {
|
|
case 0: DEBUG_S(" ES"); return &State->ES;
|
|
case 1: DEBUG_S(" CS"); return &State->CS;
|
|
case 2: DEBUG_S(" SS"); return &State->SS;
|
|
case 3: DEBUG_S(" DS"); return &State->DS;
|
|
default:
|
|
DEBUG_S("ERROR - Invalid value passed to Seg(). (%d is not a segment)", code);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Performs a memory addressing function
|
|
* \param State Emulator State
|
|
* \param mmm Function ID (mmm field from ModR/M byte)
|
|
* \param disp Displacement
|
|
* \param ptr Destination for final pointer
|
|
*/
|
|
static int DoFunc(tRME_State *State, int mmm, int16_t disp, void *ptr)
|
|
{
|
|
uint32_t addr;
|
|
uint16_t seg;
|
|
|
|
switch(mmm){
|
|
case 2: case 3: case 6:
|
|
seg = SREG_SS;
|
|
break;
|
|
default:
|
|
seg = SREG_DS;
|
|
break;
|
|
}
|
|
|
|
if(State->Decoder.OverrideSegment != -1)
|
|
seg = State->Decoder.OverrideSegment;
|
|
|
|
seg = *Seg(State, seg);
|
|
|
|
switch(mmm)
|
|
{
|
|
case -1: // R/M == 6 when Mod == 0
|
|
READ_INSTR16( disp );
|
|
DEBUG_S(":[0x%x]", disp);
|
|
addr = disp;
|
|
break;
|
|
|
|
case 0:
|
|
DEBUG_S(":[BX+SI+0x%x]", disp);
|
|
addr = State->BX.W + State->SI.W + disp;
|
|
break;
|
|
case 1:
|
|
DEBUG_S(":[BX+DI+0x%x]", disp);
|
|
addr = State->BX.W + State->DI.W + disp;
|
|
break;
|
|
case 2:
|
|
DEBUG_S(":[BP+SI+0x%x]", disp);
|
|
addr = State->BP.W + State->SI.W + disp;
|
|
break;
|
|
case 3:
|
|
DEBUG_S(":[BP+DI+0x%x]", disp);
|
|
addr = State->BP.W + State->DI.W + disp;
|
|
break;
|
|
case 4:
|
|
DEBUG_S(":[SI+0x%x]", disp);
|
|
addr = State->SI.W + disp;
|
|
break;
|
|
case 5:
|
|
DEBUG_S(":[DI+0x%x]", disp);
|
|
addr = State->DI.W + disp;
|
|
break;
|
|
case 6:
|
|
DEBUG_S(":[BP+0x%x]", disp);
|
|
addr = State->BP.W + disp;
|
|
break;
|
|
case 7:
|
|
DEBUG_S(":[BX+0x%x]", disp);
|
|
addr = State->BX.W + disp;
|
|
break;
|
|
default:
|
|
return RME_ERR_BUG;
|
|
}
|
|
return RME_Int_GetPtr(State, seg, addr, ptr);
|
|
}
|
|
|
|
/**
|
|
* \brief Parses the ModR/M byte as a 8-bit value
|
|
* \param State Emulator State
|
|
* \param to R field destination (ignored if NULL)
|
|
* \param from M field destination (ignored if NULL)
|
|
*/
|
|
int RME_Int_ParseModRM(tRME_State *State, uint8_t **to, uint8_t **from)
|
|
{
|
|
uint8_t d;
|
|
uint16_t ofs;
|
|
int ret;
|
|
|
|
READ_INSTR8(d);
|
|
switch(d >> 6)
|
|
{
|
|
case 0: //No Offset
|
|
if(to) *to = RegB( State, (d>>3) & 7 );
|
|
if(from) {
|
|
if((d & 7) == 6)
|
|
ret = DoFunc( State, -1, 0, from );
|
|
else
|
|
ret = DoFunc( State, d & 7, 0, from );
|
|
if(ret) return ret;
|
|
}
|
|
return 0;
|
|
case 1: //8 Bit
|
|
if(to) *to = RegB( State, (d>>3) & 7 );
|
|
if(from) {
|
|
READ_INSTR8S( ofs );
|
|
ret = DoFunc( State, d & 7, ofs, from);
|
|
if(ret) return ret;
|
|
}
|
|
return 0;
|
|
case 2: //16 Bit
|
|
if(to) *to = RegB( State, (d>>3) & 7 );
|
|
if(from) {
|
|
READ_INSTR16( ofs );
|
|
ret = DoFunc( State, d & 7, ofs, from );
|
|
if(ret) return ret;
|
|
}
|
|
return 0;
|
|
case 3: //Regs Only
|
|
if(to) *to = RegB( State, (d>>3) & 7 );
|
|
if(from) *from = RegB( State, d & 7 );
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses the ModR/M byte as a 16-bit value
|
|
* \param State Emulator State
|
|
* \param to R field destination (ignored if NULL)
|
|
* \param from M field destination (ignored if NULL)
|
|
*/
|
|
int RME_Int_ParseModRMX(tRME_State *State, uint16_t **to, uint16_t **from)
|
|
{
|
|
uint8_t d;
|
|
uint16_t ofs;
|
|
int ret;
|
|
|
|
READ_INSTR8(d);
|
|
switch(d >> 6)
|
|
{
|
|
case 0: //No Offset
|
|
if(to) *to = RegW( State, (d>>3) & 7 );
|
|
if(from) {
|
|
if( (d & 7) == 6 )
|
|
ret = DoFunc( State, -1, 0, from );
|
|
else
|
|
ret = DoFunc( State, d & 7, 0, from );
|
|
if(ret) return ret;
|
|
}
|
|
return 0;
|
|
case 1: //8 Bit
|
|
if(to) *to = RegW( State, (d>>3) & 7 );
|
|
if(from) {
|
|
READ_INSTR8S( ofs );
|
|
ret = DoFunc( State, d & 7, ofs, from );
|
|
if(ret) return ret;
|
|
|
|
}
|
|
return 0;
|
|
case 2: //16 Bit
|
|
if(to) *to = RegW( State, (d>>3) & 7 );
|
|
if(from) {
|
|
READ_INSTR16( ofs );
|
|
ret = DoFunc( State, d & 7, ofs, from );
|
|
if(ret) return ret;
|
|
}
|
|
return 0;
|
|
case 3: //Regs Only
|
|
if(to) *to = RegW( State, (d>>3) & 7 );
|
|
if(from) *from = RegW( State, d & 7 );
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Does a conditional jump
|
|
* \param State Current emulator state
|
|
* \param type Jump Type (0-15)
|
|
* \param offset Offset to add to IP if jump succeeds
|
|
* \param name Jump class name (Short or Near)
|
|
*/
|
|
static inline int RME_Int_DoCondJMP(tRME_State *State, uint8_t type, uint16_t offset, const char *name)
|
|
{
|
|
DEBUG_S("J");
|
|
switch(type) {
|
|
case 0x0: DEBUG_S("O"); // Overflow
|
|
if(State->Flags & FLAG_OF) State->IP += offset;
|
|
break;
|
|
case 0x1: DEBUG_S("NO"); // No Overflow
|
|
if(!(State->Flags & FLAG_OF)) State->IP += offset;
|
|
break;
|
|
case 0x2: DEBUG_S("C"); // Carry
|
|
if(State->Flags & FLAG_CF) State->IP += offset;
|
|
break;
|
|
case 0x3: DEBUG_S("NC"); // No Carry
|
|
if(!(State->Flags & FLAG_CF)) State->IP += offset;
|
|
break;
|
|
case 0x4: DEBUG_S("Z"); // Equal
|
|
if(State->Flags & FLAG_ZF) State->IP += offset;
|
|
break;
|
|
case 0x5: DEBUG_S("NZ"); // Not Equal
|
|
if(!(State->Flags & FLAG_ZF)) State->IP += offset;
|
|
break;
|
|
case 0x6: DEBUG_S("BE"); // Below or Equal
|
|
if(State->Flags & FLAG_CF || State->Flags & FLAG_ZF) State->IP += offset;
|
|
break;
|
|
case 0x7: DEBUG_S("A"); // Above
|
|
if( !(State->Flags & FLAG_CF) && !(State->Flags & FLAG_ZF))
|
|
State->IP += offset;
|
|
break;
|
|
case 0x8: DEBUG_S("S"); // Sign
|
|
if( State->Flags & FLAG_SF )
|
|
State->IP += offset;
|
|
break;
|
|
case 0x9: DEBUG_S("NS"); // Not Sign
|
|
if( !(State->Flags & FLAG_SF) )
|
|
State->IP += offset;
|
|
break;
|
|
case 0xA: DEBUG_S("PE"); // Pairity Even
|
|
if( State->Flags & FLAG_PF )
|
|
State->IP += offset;
|
|
break;
|
|
case 0xB: DEBUG_S("PO"); // Pairity Odd
|
|
if( !(State->Flags & FLAG_PF) )
|
|
State->IP += offset;
|
|
break;
|
|
case 0xC: DEBUG_S("L"); // Less
|
|
if( !!(State->Flags & FLAG_SF) != !!(State->Flags & FLAG_OF) )
|
|
State->IP += offset;
|
|
break;
|
|
case 0xD: DEBUG_S("GE"); // Greater or Equal
|
|
if( !!(State->Flags & FLAG_SF) == !!(State->Flags & FLAG_OF) )
|
|
State->IP += offset;
|
|
break;
|
|
case 0xE: DEBUG_S("LE"); // Less or Equal
|
|
if( State->Flags & FLAG_ZF || !!(State->Flags & FLAG_SF) != !!(State->Flags & FLAG_OF) )
|
|
State->IP += offset;
|
|
break;
|
|
case 0xF: DEBUG_S("G"); // Greater
|
|
if( !(State->Flags & FLAG_ZF) || !!(State->Flags & FLAG_SF) == !!(State->Flags & FLAG_OF) )
|
|
State->IP += offset;
|
|
break;
|
|
|
|
default:
|
|
DEBUG_S(" 0x%x", type);
|
|
return RME_ERR_BUG;
|
|
}
|
|
DEBUG_S(" %s .+0x%x", name, offset);
|
|
return 0;
|
|
}
|