toaruos/kernel/v8086/rme.c
2012-01-31 20:12:19 -06:00

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)==(uint32_t)((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;
}