Add emulator to run 8086 native assembler on linux
This commit is contained in:
parent
59f0720934
commit
fcb4db25ad
4
Makefile
4
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
|
||||
|
229
utils/emul.c
Normal file
229
utils/emul.c
Normal file
@ -0,0 +1,229 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user