Created
September 2, 2016 20:25
-
-
Save katlogic/63d8ff5b1cdf3e43ab452cf0be2cb103 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <string.h> | |
enum { | |
ADD, SUB, ADDC, SUBC, CMP, | |
AND, OR, XOR, TEST, | |
SHL, SHR, ROTATE, // rotate direction and signed shift as primary flag | |
INC, // dec as primary flag | |
} | |
enum { | |
// Local flags | |
CARRY=0, // ADD | |
ROTATE=0, // SHL | |
DEC=0, // INC | |
JMPF=0, // JMP | |
XCHG=0, // MOV | |
// Global flags | |
MODRM=1, | |
BYTEOP=2, | |
} | |
enum { | |
ADD, SUB, CMP, | |
AND, OR, XOR, TEST, | |
INC, | |
PUSH, POP, | |
IMUL, | |
JMP, RET, CALL, | |
SHL, SHR, | |
} | |
enum { | |
AX=0, CX, DX, BX, SP, BP, SI, DI, | |
FS=0, GS, | |
ADD=0, OR, ADC, SBB, AND, SUB, XOR, CMP | |
}; | |
static const uint8_t parity[256] = { | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, | |
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 | |
}; | |
typedef struct { | |
uint32_t reg[8]; // general regs | |
uint32_t ip; // pc pointer (base relative) | |
uint32_t dst; | |
uint8_t cf,lastop; | |
intptr_t mask; // write mask | |
uint8_t *base; // base of cs,ds,es,ss | |
uint8_t *fsgs[2]; // fsgs bases | |
} x86_t; | |
int emu86(x86_t *init_cpu) | |
{ | |
x86_t c; | |
uint8_t *pc; | |
uint32_t op, op2; | |
int data16; | |
// computing all flags per arith op would be too slow. | |
// the interpreter only keeps carry, and the result value | |
// of last arith op (along with opcode). when some flags are | |
// requested later, they can be reconstructed on-the-fly. | |
static const uint8_t addsubcmp_mask[] = { | |
[ADD] = 1, [SUB] = 1, [CMP] = 1, | |
[ADC] = 1, [SBB] = 1 | |
}; | |
#define ZF (!c.dst) | |
#define CF (c.cf) | |
#define SF (c>>31) | |
#define OF ((SF ^ CF) & addsubcmp_mask[c.lastop]) | |
#define ARITH(op,a,b) { \ | |
uint8_t top = op >> 3; \ | |
switch (top) { \ | |
case ADD: c.dst = a += b; c.cf = a < b; break; \ | |
case OR: c.dst = a |= b; c.cf = 0; break; \ | |
case ADC: c.dst = a += (b+=c.cf); c.cf = a < b; break; \ | |
case SBB: c.dst = a -= (b+=c.cf); c.cf = a > b; break; \ | |
case AND: c.dst = a &= b; c.cf = 0; break; \ | |
case SUB: c.dst = a -= b; c.cf = a > b; \ | |
case XOR: c.dst = a ^= b; c.cf = 0; break; \ | |
case CMP: c.dst = a - b; c.cf = a > b; break; \ | |
} \ | |
c.lastop = top; \ | |
} | |
#define GETPCW(dst) { \ | |
if (data16) { \ | |
dst = *((uint16_t*) pc); \ | |
pc+=2; \ | |
} else { \ | |
dst = *((uint32_t*) pc); \ | |
pc+=4; \ | |
} \ | |
} | |
#define COPYW(dst,src) { \ | |
if (data16) { \ | |
*((uint16_t*) (dst)) = *(uint16_t*)(src); \ | |
} else { \ | |
*((uint32_t*) (dst)) = *(uint32_t*)(src); \ | |
} \ | |
} | |
// make a local copy, one level less of dereference | |
memcpy(&c, init_cpu, sizeof c); | |
pc = c.ip + c.base; | |
if (!pc) return 1; | |
#define NEXT \ | |
goto next; | |
next:; | |
data16 = 0; | |
op = *pc++; | |
switch (op) { | |
case 0x66: | |
data16 = 1; | |
NEXT; | |
case 0x04 ... 0x05: | |
case 0x0c ... 0x0d: | |
case 0x14 ... 0x15: | |
case 0x1c ... 0x1d: | |
case 0x24 ... 0x25: | |
case 0x2c ... 0x2d: | |
case 0x3c ... 0x3d: { | |
uint32_t a,b; | |
if (op&1) { | |
a = c.reg[AX]; | |
GETPCW(b) | |
ARITH(op, a, b); | |
COPYW(&c.reg[AX], &a); | |
NEXT; | |
} else { | |
a = (*(uint8_t*)&c.reg[AX]); | |
b = *pc++; | |
ARITH(op, a, b); | |
(*(uint8_t*)&c.reg[AX]) = a; | |
NEXT; | |
} | |
} | |
case 0x0f: | |
switch ((op2 = c.ip++)) { | |
case 0x9f: | |
case 0x4f: | |
case 0xb6 ... 0xb7: | |
case 0xbe ... 0xbf: | |
op = op2 | 0x100; | |
// fall thru | |
break; | |
} | |
// fallen angels | |
case 0x00 ... 0x03: | |
case 0x08 ... 0x0b: | |
case 0x10 ... 0x13: | |
case 0x18 ... 0x1b: | |
case 0x20 ... 0x23: | |
case 0x28 ... 0x2b: | |
case 0x30 ... 0x33: | |
case 0x38 ... 0x3b: | |
case 0x69: case 0x6b: | |
case 0x80 ... 0x8f: | |
case 0xc0 ... 0xc1: | |
case 0xc6 ... 0xc7: | |
case 0xd0 ... 0xd3: | |
case 0xd8 ... 0xdf: | |
case 0xf6 ... 0xf7: | |
case 0xfe ... 0xff: | |
default: | |
break; | |
} | |
memcpy(init_cpu, &c, sizeof c); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment