diff --git a/decoding.odin b/decoding.odin index 32b5bcf..0adf893 100644 --- a/decoding.odin +++ b/decoding.odin @@ -66,7 +66,7 @@ get_op :: proc(inst: Instruction) -> (Op, bool) { } return op, interseg } -parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, word: bool, has_segreg: Maybe(Register)) -> Operand { +parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand { operand: Operand = None{} switch opinfo { case .None: @@ -80,11 +80,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr case .SecondByteLast3: reg = data[1] & 0b111 } if opinfo == .SegmentRegister { - operand = (RegisterId){id = SEGMENT_REGISTER_START + (int)(reg), access = .Full} + operand = (RegisterId){idx = SEGMENT_REGISTER_START + (int)(reg), access = .Full} } else if word { - operand = RegisterId { id = (int)(reg), access = .Full } + operand = RegisterId { idx = (int)(reg), access = .Full } } else { - operand = RegisterId { id = (int)(reg % 4), access = reg < 4 ? .Low : .High } + operand = RegisterId { idx = (int)(reg % 4), access = reg < 4 ? .Low : .High } } case .RegisterMemory: mod := data[1] >> 6 @@ -107,9 +107,9 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr processed^ += 2 } else if mod == 3 { if word { - op = RegisterId { id = (int)(rm), access = .Full } + op = RegisterId { idx = (int)(rm), access = .Full } } else { - op = RegisterId { id = (int)(rm % 4), access = rm < 4 ? .Low : .High } + op = RegisterId { idx = (int)(rm % 4), access = rm < 4 ? .Low : .High } } } operand = op @@ -126,7 +126,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 } processed^ += 1 case .Accumulator: - operand = RegisterId { id = 0, access = word ? .Full : .Low } + operand = RegisterId { idx = 0, access = word ? .Full : .Low } case .DirectAddress: // operand = DirectAddress { value = get_i16(data[1:]) } operand = (DirectAddress)(get_i16(data[1:])) @@ -136,10 +136,10 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr // NOTE: In order to mimic the label offset, you have to take the value you got and add two operand = (Jump)((i8)(data[1]) + 2) case .VariablePort: - operand = RegisterId { id = (int)(variable_port.code), access = .Full } + operand = RegisterId { idx = (int)(Register.dx), access = .Full } case .ShiftRotate: v_flag := data[0] & 0b10 != 0 - operand = v_flag ? RegisterId { id = 1, access = .Low } : Immediate { value = 1 } + operand = v_flag ? RegisterId { idx = 1, access = .Low } : Immediate { value = 1 } case .Repeat: bits := (data[1] & 0b1110) >> 1 w := (data[1] & 0b1) == 1 ? "w" : "b" @@ -169,7 +169,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) { idx := 0 - has_segment: Maybe(Register) + has_segment: Maybe(RegisterId) has_lock: bool for idx < bytes_to_read { instruction: Instruction @@ -203,7 +203,7 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: continue } else if inst.opname == .SEGMENT { reg := (curr_byte & 0b11000) >> 3 - has_segment = CPU.registers[SEGMENT_REGISTER_START+reg] + has_segment = RegisterId { idx = int(SEGMENT_REGISTER_START+reg) } idx += 1 continue } else if inst.opname == .AAM { diff --git a/execution.odin b/execution.odin index 94cd283..ef96d33 100644 --- a/execution.odin +++ b/execution.odin @@ -4,34 +4,40 @@ import "core:os" import "core:fmt" import "core:math" import "core:strings" +import "core:reflect" get_operand_value :: proc(operand: Operand) -> u16 { #partial switch opr in operand { case Immediate: return opr.value case RegisterId: + reg_val := CPU.registers[opr.idx] switch opr.access { case .Low, .High: - val := opr.access == .Low ? CPU.registers[opr.id].value.low : CPU.registers[opr.id].value.high - return u16(val) + return u16(opr.access == .Low ? reg_val.low : reg_val.high) case .Full: - return CPU.registers[opr.id].value.full + return reg_val.full } } return 0 } -set_register_value :: proc(reg_id: RegisterId, value: u16) { - switch reg_id.access { +set_register_value :: proc(reg: RegisterId, value: u16) { + switch reg.access { case .Low: - CPU.registers[reg_id.id].value.low = u8(value) + CPU.registers[reg.idx].low = u8(value) case .High: - CPU.registers[reg_id.id].value.high = u8(value) + CPU.registers[reg.idx].high = u8(value) case .Full: - CPU.registers[reg_id.id].value.full = u16(value) + CPU.registers[reg.idx].full = u16(value) } } +get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue { + reg,_ := reflect.enum_from_name(Register, name) + return &cpu.registers[int(reg)] +} + check_zero_flag :: proc(value: u16) { CPU.flags.ZF = value == 0 } @@ -48,19 +54,19 @@ execute_instruction :: proc(inst: Instruction) { set_register_value(reg, src_val) case .ADD: src_val := get_operand_value(inst.src) - val := CPU.registers[reg.id].value.full + src_val + val := CPU.registers[reg.idx].full + src_val set_register_value(reg, val) check_zero_flag(val) check_sign_flag(val) case .SUB: src_val := get_operand_value(inst.src) - val := CPU.registers[reg.id].value.full - src_val + val := CPU.registers[reg.idx].full - src_val set_register_value(reg, val) check_zero_flag(val) check_sign_flag(val) case .CMP: src_val := get_operand_value(inst.src) - val := CPU.registers[reg.id].value.full - src_val + val := CPU.registers[reg.idx].full - src_val check_zero_flag(val) check_sign_flag(val) } diff --git a/printing.odin b/printing.odin index 9282772..c051931 100644 --- a/printing.odin +++ b/printing.odin @@ -37,7 +37,7 @@ calculate_effective_address :: proc(r_m: u8) -> string { return val } -get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(Register)) -> string { +get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(RegisterId)) -> string { disp: string switch value in memoryAddr.displacement { case None: @@ -53,7 +53,7 @@ get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(Register)) } seg_string: string if segreg, ok := has_segment.?; ok { - seg_string = fmt.aprintf("%s:", segreg.fullname) + seg_string = fmt.aprintf("%s:", get_register_name(segreg)) } text := fmt.aprintf("%s[%s%s]", seg_string, calculate_effective_address(memoryAddr.addr_id), disp) return text @@ -78,14 +78,14 @@ get_register_name :: proc(reg_id: RegisterId) -> string { low_names := [?]string{"al", "cl", "dl", "bl"} high_names := [?]string{"ah", "ch", "dh", "bh"} switch reg_id.access { - case .Full: return CPU.registers[reg_id.id].fullname - case .Low: return low_names[reg_id.id] - case .High: return high_names[reg_id.id % 4] + case .Full: return reflect.enum_string(Register(reg_id.idx)) + case .Low: return low_names[reg_id.idx] + case .High: return high_names[reg_id.idx % 4] } return "" } -get_operand_string :: proc(operand: Operand, has_segment: Maybe(Register)) -> string { +get_operand_string :: proc(operand: Operand, has_segment: Maybe(RegisterId)) -> string { string_val: string switch val in operand { case None: @@ -106,7 +106,7 @@ get_operand_string :: proc(operand: Operand, has_segment: Maybe(Register)) -> st case DirectAddress: seg_string: string if segreg, ok := has_segment.?; ok { - seg_string = fmt.aprintf("%s:", segreg.fullname) + seg_string = fmt.aprintf("%s:", get_register_name(segreg)) } string_val = fmt.aprintf("%s[%d]", seg_string, val) case Jump: diff --git a/sim8086.odin b/sim8086.odin index 19891b2..479dcdf 100644 --- a/sim8086.odin +++ b/sim8086.odin @@ -1,32 +1,15 @@ package sim_8086 import "core:os" -import "core:path" +import path "core:path/filepath" import "core:fmt" import "core:math" import "core:strings" CPU := Cpu { - registers = [12]Register { - {fullname = "ax", code = 0b000}, - {fullname = "cx", code = 0b001}, - {fullname = "dx", code = 0b010}, - {fullname = "bx", code = 0b011}, - {fullname = "sp", code = 0b100}, - {fullname = "bp", code = 0b101}, - {fullname = "si", code = 0b110}, - {fullname = "di", code = 0b111}, - {fullname = "es", code = 0b000}, - {fullname = "cs", code = 0b001}, - {fullname = "ss", code = 0b010}, - {fullname = "ds", code = 0b011}, - }, memory = make([dynamic]u8, 65536), } -variable_port := CPU.registers[2] - - main :: proc() { f,err := os.open(os.args[1]) if err != os.ERROR_NONE { @@ -59,22 +42,23 @@ main :: proc() { } if what_to_print == "registers" || what_to_print == "all" { - print_reg :: proc(reg: Register) { - full := fmt.aprintf("%s: %d ", reg.fullname, reg.value.full) - hex := fmt.aprintf("0x%04x ", reg.value.full) + for reg,i in CPU.registers { + full := fmt.aprintf("%s: %d ", get_register_name(RegisterId{idx=i}), reg.full) + hex := fmt.aprintf("0x%04x ", reg.full) fmt.printf("%s %*[1]s %s %*[4]s %08b %08b", - full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg.value.high, reg.value.low) + full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg.high, reg.low) fmt.println() } - for reg in CPU.registers { - print_reg(reg) - } - // fmt.println("Checking Against Expected State") - // fmt.println(os.args[1]) - // path := path.base_no_ext(os.args[1]) - // fmt.println(path) - // extract_expected_cpu_state(path) + path,ok := strings.replace(os.args[1], ".bin", ".txt", 1) + expected_cpu,_ := extract_expected_cpu_state(path) + for reg,i in expected_cpu.registers { + if CPU.registers[i].full != reg.full { + name := get_register_name(RegisterId{idx=i}) + msg := "%s register does not match - Expected %04x | Actual %04x" + fmt.eprintfln(msg, name, reg.full, CPU.registers[i].full) + } + } } if what_to_print == "instructions" || what_to_print == "all" { print_instructions_stdout(instructions_list[:]) diff --git a/testing.odin b/testing.odin index ee6a34e..ff126b9 100644 --- a/testing.odin +++ b/testing.odin @@ -4,35 +4,41 @@ import "core:os" import "core:fmt" import "core:math" import "core:strings" -import "core:text/regex" +import "core:strconv" -extract_expected_cpu_state :: proc(listing_num: int) -> (Cpu, bool) { +extract_expected_cpu_state :: proc(filename: string) -> (Cpu, bool) { cpu: Cpu - // filename := fmt.aprintf("./asm_files/list-%04d.txt", listing_num) - // fmt.println(filename) - // data,ok := os.read_entire_file(filename) - // if !ok { - // return cpu, false - // } - // defer delete(data) + data,ok := os.read_entire_file(filename) + if !ok { + return cpu, false + } + defer delete(data) - // content := string(data) - // lines := strings.split(content, "\n") - // defer delete(lines) + content := string(data) + lines := strings.split(content, "\n") + defer delete(lines) - // for line in lines { - // for c in line { - // if c != ' ' { - // continue - // } else { - // fmt.print(c) - // } - // } - // fmt.println() - // } + for line in lines { + space_count := 0 + for c,i in line { + if space_count == 0 && c != ' ' { + break + } else if c == ' ' { + space_count += 1 + } else { + if line[i:i+5] != "flags" { + reg_name := line[i:i+2] + reg_value := get_cpu_register_by_name(&cpu, reg_name) + hex_string := line[i+6:i+10] + if hex_num,ok := strconv.parse_int(hex_string, 16); ok { + reg_value^.full = u16(hex_num) + } + } + break + } + } + } - - // cpu.registers[] return cpu, true } diff --git a/types.odin b/types.odin index 10cec11..577e4db 100644 --- a/types.odin +++ b/types.odin @@ -1,14 +1,25 @@ package sim_8086 -Register :: struct { - fullname: string, - value: struct #raw_union { - using _: struct { - low, high: byte, - }, - full: u16, +Register :: enum { + ax, + cx, + dx, + bx, + sp, + bp, + si, + di, + es, + cs, + ss, + ds, +} + +RegisterValue :: struct #raw_union { + using _: struct { + low, high: byte, }, - code: u8, + full: u16, } Flags :: struct { @@ -34,14 +45,14 @@ Displacement :: union { } RegisterAccess :: enum { + Full, Low, High, - Full, } RegisterId :: struct { + idx: int, access: RegisterAccess, - id: int, } ImmediateSize :: enum { Signed8, @@ -125,7 +136,7 @@ Instruction :: struct { indirect_intersegment: bool, // TODO: This is trickier than I thought, it's more than just the one instruction // that uses it - has_segment: Maybe(Register), + has_segment: Maybe(RegisterId), has_lock: bool, bytes_read: int, raw_data: []u8, @@ -134,7 +145,7 @@ Instruction :: struct { Cpu :: struct { flags: Flags, - registers: [12]Register, + registers: [12]RegisterValue, memory: [dynamic]u8, total_bytes_processed: int }