Skip to content

Instantly share code, notes, and snippets.

@katlogic
Created September 2, 2016 20:25
Show Gist options
  • Save katlogic/63d8ff5b1cdf3e43ab452cf0be2cb103 to your computer and use it in GitHub Desktop.
Save katlogic/63d8ff5b1cdf3e43ab452cf0be2cb103 to your computer and use it in GitHub Desktop.
#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