diff --git a/Makefile b/Makefile index 0b76af9..6652838 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ C_FLAGS = \ -Wall -Wextra -Wpedantic -Wconversion \ -pedantic -pedantic-errors \ -Wno-unused-variable \ + -Wno-unused-parameter \ -I. \ L_FLAGS = -pthread diff --git a/compile_flags.txt b/compile_flags.txt index e7d8f39..ea92534 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -7,7 +7,7 @@ -pedantic -pedantic-errors -Wno-unused-variable -# -Wno-unused-parameter +-Wno-unused-parameter # -Wno-unused-function -I. diff --git a/vm/asm.c b/vm/asm.c index de65851..3e595a1 100644 --- a/vm/asm.c +++ b/vm/asm.c @@ -163,6 +163,13 @@ Line s_lit_l(int op1_label) .op1 = (Ex) { .label = op1_label }, }; } +Line s_int(uint8_t int_id) +{ + return (Line) { + .ty = LineTy_Int, + .op1 = (Ex) { .imm = int_id }, + }; +} Line s_iret(void) { return (Line) { .ty = LineTy_IRet }; @@ -456,6 +463,14 @@ void assemble_to_binary(uint16_t* out, const Line* lines, size_t lines_size) ADD_LABEL(op1); break; } + case LineTy_Int: { + uint16_t int_id = line->op1.imm & 0xff; + + uint32_t ins = Op_Int; + ins |= (uint16_t)(int_id << 8); + out[ip++] = (uint16_t)ins; + break; + } case LineTy_IRet: { out[ip++] = Op_IRet; break; diff --git a/vm/asm.h b/vm/asm.h index 93c6a8f..de45593 100644 --- a/vm/asm.h +++ b/vm/asm.h @@ -26,6 +26,7 @@ typedef enum { LineTy_In_Imm, LineTy_Lit_Imm, LineTy_Lit_Label, + LineTy_Int, LineTy_IRet, LineTy_Or_Imm, LineTy_And_Imm, @@ -72,6 +73,7 @@ Line s_mov16_ml_r(int dst_label, Reg op2_reg); Line s_in_i(Reg dst_reg, uint16_t op1_imm); Line s_lit_i(uint16_t op1_imm); Line s_lit_l(int op1_label); +Line s_int(uint8_t int_id); Line s_iret(void); Line s_or_i(Reg dst_reg, Reg op1_reg, uint16_t op2_imm); Line s_and_i(Reg dst_reg, Reg op1_reg, uint16_t op2_imm); diff --git a/vm/main.c b/vm/main.c index ab2d02e..31114c3 100644 --- a/vm/main.c +++ b/vm/main.c @@ -400,18 +400,20 @@ int main(void) Line program_asm[] = { // clang-format off s_nop(), - // set video character display flag - s_or_i(Rfl, Rfl, 1 << Fl_Vcd), - // setup stack - s_mov16_r_i(Rbp, 2048), // rsp points *at* the top element + s_mov16_r_i(Rbp, 2048), s_mov16_r_i(Rsp, 2048 - 2), - // load interrupt table s_lit_l(interrupt_table), - // set interrupt flag s_or_i(Rfl, Rfl, 1 << Fl_Int), + + s_or_i(Rfl, Rfl, 1 << Fl_Vcd), + + s_mov16_r_i(R0, 512), + s_mov16_r_i(R1, 1), + s_int(Int_DiskRead), + L(main_loop), s_hlt(), s_jmp_l(main_loop), @@ -430,7 +432,7 @@ int main(void) s_push_r(R2), // read keyboard port - s_in_i(R0, 0), + s_in_i(R0, Device_Keyboard), s_add_i(R0, R0, 'A' - 4), s_cmp_i(R0, 105), @@ -467,7 +469,7 @@ int main(void) size_t program_asm_size = sizeof(program_asm) / sizeof(program_asm[0]); - uint16_t* program = calloc(512, sizeof(uint16_t)); + uint16_t* program = calloc(512 * 2, sizeof(uint16_t)); assemble_to_binary(program, program_asm, program_asm_size); // dump_program(program); @@ -485,6 +487,7 @@ int main(void) vm_start(&drive.drive, &io_device.io_device); + free(program); sdldevice_destroy(&io_device); } diff --git a/vm/vm.c b/vm/vm.c index cb90887..24555bc 100644 --- a/vm/vm.c +++ b/vm/vm.c @@ -17,7 +17,12 @@ typedef struct { } VM; static inline void run_arithm_ins(VM* vm, uint16_t ins); -static inline int jump_to_interrupt(VM* vm, uint16_t int_id); +static inline void handle_device_read(VM* vm, Reg dst_reg, uint16_t device_id); +static inline void handle_device_write( + VM* vm, uint16_t op1, uint16_t device_id); +static inline int handle_interrupt(VM* vm, uint8_t int_id); +static inline int jump_to_interrupt_handler(VM* vm, uint8_t int_id); +static inline void interrupt_return(VM* vm); static inline void maybe_update_vcd(VM* vm, uint16_t addr); static inline uint16_t eat_uint16(VM* vm); static inline uint16_t read_seg_uint16(VM* vm, uint16_t ptr); @@ -57,8 +62,9 @@ void vm_start(Drive* boot_drive, IODevice* io_device) uint16_t* rfl = &vm->regs[Rfl]; uint16_t* rcs = &vm->regs[Rcs]; + const uint16_t bootloader_size = 512; const uint16_t block_size = vm->boot_drive->block_size; - for (uint16_t i = 0; i * block_size < 512; ++i) { + for (uint16_t i = 0; i * block_size < bootloader_size; ++i) { vm->boot_drive->read(vm->boot_drive, &vm->mem[i * block_size], i); } @@ -209,29 +215,13 @@ void vm_start(Drive* boot_drive, IODevice* io_device) case Op_In: { Reg dst_reg = ins_dst_reg(ins); uint16_t device_id = ins_op1_or_imm(vm, ins); - - switch (device_id) { - case 0: - vm->regs[dst_reg] = vm->keyboard_port_input; - break; - default: - fprintf( - stderr, "warning: no input device %d\n", device_id); - break; - } + handle_device_read(vm, dst_reg, device_id); break; } case Op_Out: { uint16_t op1 = ins_op2(vm, ins); uint16_t device_id = ins_op1_or_imm(vm, ins); - - switch (device_id) { - default: - fprintf(stderr, - "warning: no output device %d\n", - device_id); - break; - } + handle_device_write(vm, op1, device_id); break; } case Op_Lit: { @@ -242,17 +232,14 @@ void vm_start(Drive* boot_drive, IODevice* io_device) case Op_Int: { uint8_t int_id = (uint8_t)(ins >> 8 & 0xff); - int res = jump_to_interrupt(vm, int_id); + int res = handle_interrupt(vm, int_id); if (res != 0) { goto halt_execution; } break; } case Op_IRet: { - *rip = *(uint16_t*)&vm->mem[*rsp]; - *rsp -= 2; - *rcs = *(uint16_t*)&vm->mem[*rsp]; - *rsp -= 2; + interrupt_return(vm); break; } @@ -287,7 +274,7 @@ void vm_start(Drive* boot_drive, IODevice* io_device) goto halt_execution; case InterruptType_KeyEvent: { if (vm->interrupt_timeout <= 0 && (*rfl >> Fl_Int & 1)) { - int res = jump_to_interrupt(vm, 0); + int res = jump_to_interrupt_handler(vm, Int_Key); if (res != 0) { goto halt_execution; } @@ -376,11 +363,53 @@ static inline void run_arithm_ins(VM* vm, uint16_t ins) } } -static inline int jump_to_interrupt(VM* vm, uint16_t int_id) +static inline void handle_device_read(VM* vm, Reg dst_reg, uint16_t device_id) +{ + switch (device_id) { + case Device_Keyboard: + vm->regs[dst_reg] = vm->keyboard_port_input; + break; + default: + fprintf(stderr, "warning: no input device %d\n", device_id); + break; + } +} + +static inline void handle_device_write(VM* vm, uint16_t op1, uint16_t device_id) +{ + switch (device_id) { + default: + fprintf(stderr, "warning: no output device %d\n", device_id); + break; + } +} + +static inline int handle_interrupt(VM* vm, uint8_t int_id) +{ + switch ((VM_Int)int_id) { + case Int_DiskRead: { + uint16_t addr = vm->regs[R0]; + uint16_t block = vm->regs[R1]; + vm->boot_drive->read(vm->boot_drive, &vm->mem[addr], block); + break; + } + case Int_DiskWrite: { + uint16_t addr = vm->regs[R0]; + uint16_t block = vm->regs[R1]; + vm->boot_drive->write(vm->boot_drive, &vm->mem[addr], block); + break; + } + default: + return jump_to_interrupt_handler(vm, int_id); + } + return 0; +} + +static inline int jump_to_interrupt_handler(VM* vm, uint8_t int_id) { - uint16_t* rip = &vm->regs[Rip]; uint16_t* rsp = &vm->regs[Rsp]; uint16_t* rfl = &vm->regs[Rfl]; + uint16_t* rip = &vm->regs[Rip]; uint16_t* rcs = &vm->regs[Rcs]; if ((*rfl >> Fl_Int & 1) == 0) { @@ -389,15 +418,16 @@ static inline int jump_to_interrupt(VM* vm, uint16_t int_id) return -1; } + uint16_t offset = int_id - 32; uint16_t int_table_size = *(uint16_t*)&vm->mem[vm->int_table]; - if (int_id >= int_table_size) { + if (offset >= int_table_size) { fprintf(stderr, "error: interrupt outside table (%d)\n", int_id); vm->regs[Rfl] |= 1 << Fl_Err; return -1; } - uint16_t int_addr = *(uint16_t*)&vm->mem[vm->int_table + int_id * 2 + 2]; + uint16_t int_addr = *(uint16_t*)&vm->mem[vm->int_table + offset * 2 + 2]; *rsp += 2; *(uint16_t*)&vm->mem[*rsp] = *rcs; @@ -410,6 +440,18 @@ static inline int jump_to_interrupt(VM* vm, uint16_t int_id) return 0; } +static inline void interrupt_return(VM* vm) +{ + uint16_t* rsp = &vm->regs[Rsp]; + uint16_t* rip = &vm->regs[Rip]; + uint16_t* rcs = &vm->regs[Rcs]; + + *rip = *(uint16_t*)&vm->mem[*rsp]; + *rsp -= 2; + *rcs = *(uint16_t*)&vm->mem[*rsp]; + *rsp -= 2; +} + static inline void maybe_update_vcd(VM* vm, uint16_t addr) { if (!vm->io_device) diff --git a/vm/vm.h b/vm/vm.h index 0ee7603..2b6f2dc 100644 --- a/vm/vm.h +++ b/vm/vm.h @@ -59,6 +59,16 @@ typedef enum { Op_RMod, } Op; +typedef enum { + Int_DiskRead = 0, + Int_DiskWrite = 1, + Int_Key = 32, +} VM_Int; + +typedef enum { + Device_Keyboard, +} VM_Device; + typedef struct Drive Drive; typedef void (*DriveReadFn)(Drive* drive, uint8_t* block, uint16_t i); typedef void (*DriveWriteFn)(Drive* drive, const uint8_t* block, uint16_t i);