Created
September 2, 2016 20:21
-
-
Save katlogic/3fc8d2af0beca72730822405700f7eae to your computer and use it in GitHub Desktop.
This file contains 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
#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