Skip to content

Instantly share code, notes, and snippets.

@katlogic
Created September 2, 2016 20:25

Revisions

  1. katlogic created this gist Sep 2, 2016.
    190 changes: 190 additions & 0 deletions sketch2.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,190 @@
    #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;
    }