diff --git a/Makefile b/Makefile index 9326c0c..3ae6e77 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ QEMU_ARGS = $(addprefix --option-rom ,$(ROMS)) NASM = nasm NASM_ARGS = -s -I lib +EMUL = utils/emul + ifdef KVM QEMU_ARGS += --enable-kvm endif @@ -52,7 +54,7 @@ hdimage.img: boot/mbr.bs fdimage.img clean: find -name '*.lst' -delete - rm -f $$(cat .gitignore) programs/*.com boot/*.bs + rm -f $$(cat .gitignore) programs/*.com boot/*.bs utils/emul qemu-floppy: fdimage.img $(ROMS) $(QEMU) $(QEMU_ARGS) -boot c -fda fdimage.img diff --git a/utils/emul.c b/utils/emul.c new file mode 100644 index 0000000..1b4f2d8 --- /dev/null +++ b/utils/emul.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include + +uint8_t mem[64*1024]; + +union regset { + uint8_t r8[8]; // AL, AH, CL, CH... + uint16_t r16[8]; // AX, CX, DX, BX, SP, BP, SI, DI +} regset; + +uint16_t ip = 0x100; +uint16_t flags; +uint16_t scratch; +uint16_t reg; +int off; + +void* rmptr; +#define RM8 *(uint8_t*)rmptr +#define RM16 *(uint16_t*)rmptr + +#define R8(x) regset.r8[fixr8ref(x)] +#define R16(x) regset.r16[x] + +#define syscall_number regset.r8[4] +#define AL regset.r8[0] +#define CL regset.r8[2] +#define DL regset.r8[4] +#define BX regset.r16[3] +#define SP regset.r16[4] +#define BP regset.r16[5] +#define SI regset.r16[6] +#define DI regset.r16[7] + +const char* const regnames[] = { "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI", 0 }; + +// Dump registers +void dump() { + printf("\n"); + int i; + for (i=0; i<8; i++) { + printf("%s=%04X ", regnames[i], regset.r16[i]); + } + printf("\n"); + printf("IP=%04X : %X\n", ip, mem[ip]); +} + +int fixr8ref(int r) { + return ((r << 1) & 0b110) | ((r >> 2) & 0b1); +} + +uint8_t imm8() { + uint8_t r = *(uint8_t*)&mem[ip]; + ip++; + return r; +} + +uint16_t imm16() { + uint16_t r = *(uint16_t*)&mem[ip]; + ip+=2; + return r; +} + +#define BIT8 8 +#define BIT16 16 +void modrm(int size) { + uint8_t mod = (mem[ip] >> 6) & 0b11; // highest 2 bits + reg = (mem[ip] >> 3) & 0b111; + uint8_t rm = (mem[ip]) & 0b111; + ip++; + if (mod == 3) { + if (size == BIT8) { + rmptr = &R16(rm); + } else { + rmptr = &R8(rm); + } + return; + } + if (mod == 0 && rm == 6) { + rmptr = mem + (int16_t)imm16(); + return; + } + switch(rm) { + case 0: rmptr = mem + BX + SI; break; + case 1: rmptr = mem + BX + DI; break; + case 2: rmptr = mem + BP + SI; break; + case 3: rmptr = mem + BP + DI; break; + case 4: rmptr = mem + SI; break; + case 5: rmptr = mem + DI; break; + case 6: rmptr = mem + BP; break; + case 7: rmptr = mem + BX; break; + } + if (mod == 1) rmptr += (int8_t)imm8(); + if (mod == 2) rmptr += (int16_t)imm16(); +} + +int cond(int num) { + int r = 0; + switch(num >> 1) { + case 2: + r = !scratch; + break; + default: + fprintf(stderr, "Condition code %d not implemented\n", num); + exit(1); + break; + } + // Each odd condition is the negation of the previous + if (num & 1) r=1-r; + return r; +} + +void push(uint16_t v) { + SP -= 2; + *(uint16_t*)&mem[SP] = v; +} + +uint16_t pop() { + uint16_t v = *(uint16_t*)&mem[SP]; + SP += 2; + return v; +} + +void handle_syscall(int number) { + switch(number) { + case 0: + exit(0); + break; + case 2: + printf("%c", DL); + break; + default: + fprintf(stderr, "Fatal: Unhandled syscall CL=%Xh\n", number); + exit(1); + } +} + +void handle_intr(int number) { + switch(number) { + case 0x20: + handle_syscall(0); + break; + case 0xE0: + handle_syscall(CL); + break; + default: + fprintf(stderr, "Fatal: Unhandled interrupt %Xh\n", number); + exit(1); + } +} + +// Execute a single instruction +void step() { + uint8_t opcode = mem[ip]; + ip++; + switch(opcode) { + case 0x70 ... 0x7F: // Jcc + reg = ip + (int8_t)imm8(); + if (cond(opcode-0x70)) { + ip = reg; + } + break; + case 0x84: // TEST r/m8,reg8 + modrm(BIT8); + scratch = AL & RM8; + break; + case 0x88: // MOV r/m8,reg8 + modrm(BIT8); + RM8 = R8(reg); + break; + case 0xAC: // LODSB + AL = mem[SI]; + SI++; + break; + case 0xB0 ... 0xB7: // MOV reg8,imm8 + reg = opcode-0xB0; + R8(reg) = imm8(); + break; + case 0xB8 ... 0xBF: // MOV reg16,imm16 + reg = opcode-0xB8; + R16(reg) = imm16(); + break; + case 0xC3: // RET + ip = pop(); + break; + case 0xCC: // INT3 + dump(); + break; + case 0xCD: // INT imm8 + handle_intr(imm8()); + break; + case 0xE8: // CALL rw + reg = ip + (int16_t)imm16(); + if (reg == 5) { + handle_syscall(CL); + } else { + push(ip); + ip += reg; + } + break; + case 0xEB: // JMP rb + ip = ip + (int8_t)imm8(); + break; + case 0xFA: // CLI + case 0xFB: // STI + break; // no-op, since we dont have interrupts + default: + ip--; + dump(); + fprintf(stderr, "Invalid opcode at IP=%04X\n", ip); + exit(1); + break; + } +} + +int main(int argc, char** argv) { + memset(&mem, sizeof(mem), 0); + memset(®set, sizeof(regset), 0); + mem[0]=0xCD; + mem[1]=0x20; + push(0); + + FILE* fd = fopen(argv[1], "r"); + fread(mem + ip, 1, sizeof(mem) - ip, fd); + while(1) { + step(); + } +}