Skip to content

Instantly share code, notes, and snippets.

@katlogic
Created September 2, 2016 20:21
Show Gist options
  • Save katlogic/3fc8d2af0beca72730822405700f7eae to your computer and use it in GitHub Desktop.
Save katlogic/3fc8d2af0beca72730822405700f7eae to your computer and use it in GitHub Desktop.
#define ADDR16 1
#define REPNZ 2
#define REPZ 4
#define SEGOVER 8
typedef struct {
uint32_t reg[8]; // general regs
uint32_t ip;
uint32_t seg[6]; // segment bases
uint32_t sel[6]; // segment selectors (not really used)
uint8_t cf, pf, zf, sf; // `of` is computed from cf^sf
uint8_t *pages[0]; // mmu
uint32_t fault, faultip, faultptr;
uint32_t wordlen, addrlen;
} x86_t;
// mmu translation, lookup per byte
static uint32_t getset_slow(x86_t *cpu, uint32_t ptr, int n, uint32_t nval, int set)
{
int i;
uint32_t ret = 0;
#define CHECK() \
if (!page) { \
cpu->faultptr = ptr; \
cpu->fault = set+1; \
return 0; \
}
for (i = 0; i < n; i++) {
uint32_t off = ptr % PAGE_SIZE;
uint8_t *page = cpu->pages[(ptr++)%PAGE_SIZE];
CHECK();
if (set) {
page[off] = ((uint8_t*)(&nval))[i];
} else {
((uint8_t*)(&ret))[i] = page[i];
}
}
return ret;
}
// compiler specializes the fast case for individual word sizes
static inline uint32_t getset(const uint8_t **pages, const uint32_t ptr,
const int n, const uint32_t nval, const uint32_t *retval)
{
int i;
uint32_t off = ptr % PAGE_SIZE;
uint32_t ret = 0;
uint8_t *page;
if (off+n > PAGE_SIZE) // page boundary
return 0;
page = cpu->pages[ptr%PAGE_SIZE];
CHECK();
if (set) {
memcpy(page + off, &nval, n);
return 1;
}
memcpy(ret, page + off, n);
return 1;
}
#undef CHECK
static uint32_t addrmode(x86_t *cpu, uint8_t *op)
{
uint32_t n=0;
uint32_t ptr=0;
uint8_t d, c, mb, b = op[0];
const uint32_t *reg = cpu->reg;
uint32_t br = DS;
// 16 bit address modes are not that bad...
if (cpu->addrlen == 2) {
if ((b & 0xc7)!=6) {
ptr = 0;
uint8_t t = b >> 6;
if (t == 1) {
ptr = (int32_t)(char)op[1];
cpu.ip+=1;
} else if (t) {
ptr = *(uint16_t*)(op+1);
cpu.ip+=2;
}
switch (b) {
case 0: br=DS;ptr+=reg[BX]+reg[SI];break;
case 1: br=DS;ptr+=reg[BX]+reg[DI];break;
case 2: br=SS;ptr+=reg[BP]+reg[SI];break;
case 3: br=SS;ptr+=reg[BP]+reg[DI];break;
case 4: br=DS;ptr+=reg[SI];break;
case 5: br=DS;ptr+=reg[DI];break;
case 6: br=SS;ptr+=reg[BP];break;
case 7: br=DS;ptr+=reg[BX];break;
}
} else {
ptr = *(uint16_t*)(op+1);
cpu.ip+=2;
}
ptr &= 0xffff;
return ptr + cpu->seg[br];
}
switch ((mb = (b&7)|((b>>3)&0x18))) {
case 8 ... 0xb: case 0xd ... 0xf:
ptr = (int32_t)(char)op[1];
cpu.ip++;
case 0 ... 3: case 5 ... 7:
base = b&7;
ptr = reg[base];
break;
case 4: case 0xc: case 0x14:
ptr = 0;
c = op[1];
base = c & 7;
d = (c >> 3) & 7;
if (d != 4)
ptr = (reg[d] << (c>>6))
if (mb == 4) {
if (base == BP) {
ptr += *(int32_t*)(op+1);
cpu->ip+=5;
} else {
ptr += reg[base];
cpu->ip+=1;
}
} else if (mb == 0xc) {
ptr += (int32_t)(char)op[1] + regs[base];
} else if (mb == 0x14) {
ptr += *(int32_t*)(op+1) + regs[base];
}
default:
ptr = *(uint32_t*)(op+1);
base = b&7;
ptr += reg[base];
}
if (base == BP || base == SP)
br = SS;
return ptr + cpu->seg[br];
}
void emu86(x86_t *init, uint8_t *p)
{
uint8_t c;
uint32_t flags;
uint32_t base;
x86_t cpu;
uint8_t args[12], op, *page, **pages = init->pages;
uint32_t remain;
uint8_t c,*op, tmpops[16];
// make some of the context local
memcpy(&cpu, init, sizeof(cpu));
fetch:;
idx = (cpu.ip / PAGE_SIZE);
page = pages[idx];
off = (cpu.ip % PAGE_SIZE);
remain = PAGE_SIZE - off;
if (remain >= 16) {
op = page + off;
} else {
op = tmpops;
memcpy(tmpops, page + off, remain);
page = pages[++idx];
if (page)
memcpy(tmpops+remain, page, 16-remain);
}
base = cpu.seg[DS];
cpu.wordlen = 4;
cpu.addrlen = 4;
nflags = 0;
#define ADDRMODE(a) addrmode(&cpu, op, &off)
#define GETSET(ptr,n,nval,ret) if (!getset(pages,ptr,n,nval,ret)) FAULT(ptr);
#define WORDLEN cpu.wordlen
#define ADDRLEN cpu.addrlen
#define SETWORD(dst,src) \
if (wordlen == 2) \
*((uint16_t*)dst) = *((uint16_t*)src); \
else \
*((uint32_t*)dst) = *((uint32_t*)src);
#define SKIP(n) \
cpu.ip += n+nflags; goto fetch;
#define NEXT_PFX \
if (nflags++ == 4) break; op++; \
goto nextop;;
nextop:;
switch ((c=*op)) {
// prefixes. 4 maximum to prevent chains
case 0x66:
WORDLEN = 2;
NEXT_PFX;
case 0x67:
ADDRLEN = 2;
NEXT_PFX;
case 0xF0:
NEXT_PFX;
case 0xF2:
flags |= REPNZ;
case 0xF3:
flags |= REPZ;
NEXT_PFX;
case 0x26: case 0x2E: case 0x36: case 0x3E:
flags |= SEGOVER;
if (nflags++ == 4) break;
base = cpu.seg[(c-0x26)/8];
NEXT_PFX;
case 0x64 ... 0x65:
flags |= SEGOVER;
base = cpu.seg[FS+(c-0x64)];
NEXT_PFX;
case 0x06: case 0x0E: case 0x16: case 0x1E:
GETSET(PUSH(2), 2, cpu.sel([(c-6)/8]), NULL);
SKIP(1);
case 0xB0 ... 0xB7: // mov al, imm..
((uint8_t*)(&cpu.regs[c&3]))[((c&4)>>2)] = *op;
SKIP(2);
case 0xB8 ... 0xBF: // mov eax, imm...
SETWORD(&cpu.regs[c&7], op);
SKIP(1+WORDLEN);
case 0x88: { // mov r/m8, r8
uint8_t c2 = op[1];
uint8_t sreg = (c2 >> 3) & 7;
uint8_t sval = ((uint8_t*)(&cpu.regs[sreg&3]))[((sreg&4)>>2)];
if (c2 >= 0xc0) { // mov r8, r8
((uint8_t*)(&cpu.regs[c2&3]))[((c2&4)>>2)] = sval;
} else {
GETSET(ADDRMODE(op), 1, sval, NULL);
}
SKIP(2);
}
OP_END;
remain = PAGE_SIZE - off;
page += off; // CAVEAT: no longer references page
if (remain >= 12) {
memcpy(ops, page, 12);
} else {
memcpy(ops, page, remain);
// it is ok for next page to not exist
if (page = pages[idx+1])
memcpy(ops, page, 16-remain);
}
uint8_t *tab = ht;
if ((op=ops[0]) == 0xf) {
tab += DELTA_OPCODES;
op = ops[1];
} else if (c >= 0xa0 && c <= 0xa3) {
if (flags & DATA16)
flags |= ADDR16;
else
flags &= ~ADDR16;
}
switch (ops[0]) {
}
#define MOD ((c >> 6) & 3)
#define REG ((c >> 3) & 7)
#define RM (c >> 7)
#define GET(f,n) getset(cpu,(cpu->ip+=n)-n,n,0,0)
#define NEXT c = GET(ip,1)
#define PUSH(n) cpu->seg[SS]+(cpu->reg[SP]-=n)
next:;
if (cpu->fault)
return;
flags = 0;
base = cpu->seg[DS];
restart:;
cpu->faultip = cpu->ip;
switch (NEXT) {
case 0x67: // 16bit addr
flags |= ADDR16;
goto restart;
case 0xf2:
case 0xf3:
flags |= (REPNZ << (c - 0xf2));
goto restart;
case 0x26: // es
case 0x2e: // cs
case 0x36: // ss
case 0x3e: // ds
flags |= SEGOVER;
base = cpu->seg[(c-0x26)/8];
goto restart;
case 0x64: // fs
case 0x65: // gs
flags |= SEGOVER;
base = cpu->seg[FS-(c-0x64)];
goto restart;
case 0x06: // push es
case 0x0e: // push cs
case 0x16: // push ss
case 0x1e: // push ds
getset(cpu, PUSH(2), 2, cpu->sel[(c-6)/8], 1);
goto next;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment