toaruos/kernel/v8086/rme.c
Kevin Lange c0f45e0b7f VESA mode switching support.
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.
2011-12-25 00:40:40 -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)==((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;
}