From 5cf4768b80057697f8bc1189ebc1cc54fecc4623 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Thu, 20 Mar 2025 16:13:37 +0700 Subject: [PATCH] Decode then check reference flags, change registers and flags to use enumerated arrays --- decoding.odin | 18 +++++++++--------- execution.odin | 20 ++++++++++---------- printing.odin | 6 +++--- sim8086.odin | 31 +++++++++++++++++++++---------- testing.odin | 16 ++++++++++++++-- types.odin | 21 +++++++++++++++------ 6 files changed, 72 insertions(+), 40 deletions(-) diff --git a/decoding.odin b/decoding.odin index 0adf893..648fd57 100644 --- a/decoding.odin +++ b/decoding.odin @@ -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){idx = SEGMENT_REGISTER_START + (int)(reg), access = .Full} + operand = (RegisterId){name = Register(SEGMENT_REGISTER_START + (int)(reg)), access = .Full} } else if word { - operand = RegisterId { idx = (int)(reg), access = .Full } + operand = RegisterId { name = Register(reg), access = .Full } } else { - operand = RegisterId { idx = (int)(reg % 4), access = reg < 4 ? .Low : .High } + operand = RegisterId { name = Register(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 { idx = (int)(rm), access = .Full } + op = RegisterId { name = Register(rm), access = .Full } } else { - op = RegisterId { idx = (int)(rm % 4), access = rm < 4 ? .Low : .High } + op = RegisterId { name = Register(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 { idx = 0, access = word ? .Full : .Low } + operand = RegisterId { name = Register(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 { idx = (int)(Register.dx), access = .Full } + operand = RegisterId { name = Register.dx, access = .Full } case .ShiftRotate: v_flag := data[0] & 0b10 != 0 - operand = v_flag ? RegisterId { idx = 1, access = .Low } : Immediate { value = 1 } + operand = v_flag ? RegisterId { name = Register(1), access = .Low } : Immediate { value = 1 } case .Repeat: bits := (data[1] & 0b1110) >> 1 w := (data[1] & 0b1) == 1 ? "w" : "b" @@ -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 = RegisterId { idx = int(SEGMENT_REGISTER_START+reg) } + has_segment = RegisterId { name = Register(SEGMENT_REGISTER_START+reg) } idx += 1 continue } else if inst.opname == .AAM { diff --git a/execution.odin b/execution.odin index ef96d33..bf08a62 100644 --- a/execution.odin +++ b/execution.odin @@ -11,7 +11,7 @@ get_operand_value :: proc(operand: Operand) -> u16 { case Immediate: return opr.value case RegisterId: - reg_val := CPU.registers[opr.idx] + reg_val := CPU.registers[opr.name] switch opr.access { case .Low, .High: return u16(opr.access == .Low ? reg_val.low : reg_val.high) @@ -25,25 +25,25 @@ get_operand_value :: proc(operand: Operand) -> u16 { set_register_value :: proc(reg: RegisterId, value: u16) { switch reg.access { case .Low: - CPU.registers[reg.idx].low = u8(value) + CPU.registers[reg.name].low = u8(value) case .High: - CPU.registers[reg.idx].high = u8(value) + CPU.registers[reg.name].high = u8(value) case .Full: - CPU.registers[reg.idx].full = u16(value) + CPU.registers[reg.name].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)] + return &cpu.registers[reg] } check_zero_flag :: proc(value: u16) { - CPU.flags.ZF = value == 0 + CPU.flags[.ZF] = value == 0 } check_sign_flag :: proc(value: u16) { - CPU.flags.SF = value >> 15 == 1 + CPU.flags[.SF] = value >> 15 == 1 } execute_instruction :: proc(inst: Instruction) { @@ -54,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.idx].full + src_val + val := CPU.registers[reg.name].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.idx].full - src_val + val := CPU.registers[reg.name].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.idx].full - src_val + val := CPU.registers[reg.name].full - src_val check_zero_flag(val) check_sign_flag(val) } diff --git a/printing.odin b/printing.odin index c051931..60d7fae 100644 --- a/printing.odin +++ b/printing.odin @@ -78,9 +78,9 @@ 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 reflect.enum_string(Register(reg_id.idx)) - case .Low: return low_names[reg_id.idx] - case .High: return high_names[reg_id.idx % 4] + case .Full: return reflect.enum_string(reg_id.name) + case .Low: return low_names[int(reg_id.name)] + case .High: return high_names[int(reg_id.name) % 4] } return "" } diff --git a/sim8086.odin b/sim8086.odin index 479dcdf..d4131c7 100644 --- a/sim8086.odin +++ b/sim8086.odin @@ -5,6 +5,7 @@ import path "core:path/filepath" import "core:fmt" import "core:math" import "core:strings" +import "core:reflect" CPU := Cpu { memory = make([dynamic]u8, 65536), @@ -42,21 +43,31 @@ main :: proc() { } if what_to_print == "registers" || what_to_print == "all" { - 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.println("\nRegisters") + for reg_val,name in CPU.registers { + full := fmt.aprintf("%s: %d ", name, reg_val.full) + hex := fmt.aprintf("0x%04x ", reg_val.full) fmt.printf("%s %*[1]s %s %*[4]s %08b %08b", - full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg.high, reg.low) + full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg_val.high, reg_val.low) fmt.println() } + fmt.println("\nFlags") + for state,flag in CPU.flags { + fmt.printfln("%c: %d",reflect.enum_string(flag)[0], state ? 1 : 0) + } 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) + ref_cpu,_ := extract_reference_cpu_state(path) + for reg_val,name in ref_cpu.registers { + if CPU.registers[name].full != reg_val.full { + msg := "%s register does not match reference - Expected %04x | Actual %04x" + fmt.eprintfln(msg, name, reg_val.full, CPU.registers[name].full) + } + } + for f in Flag { + if ref_cpu.flags[f] != CPU.flags[f] { + msg := "%s flag does not match reference - Expected %t | Actual %t" + fmt.eprintfln(msg, f, ref_cpu.flags[f], CPU.flags[f]) } } } diff --git a/testing.odin b/testing.odin index ff126b9..e4d6463 100644 --- a/testing.odin +++ b/testing.odin @@ -5,8 +5,9 @@ import "core:fmt" import "core:math" import "core:strings" import "core:strconv" +import "core:reflect" -extract_expected_cpu_state :: proc(filename: string) -> (Cpu, bool) { +extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) { cpu: Cpu data,ok := os.read_entire_file(filename) @@ -27,7 +28,18 @@ extract_expected_cpu_state :: proc(filename: string) -> (Cpu, bool) { } else if c == ' ' { space_count += 1 } else { - if line[i:i+5] != "flags" { + if line[i:i+5] == "flags" { + idx := i + 7 + for idx < len(line) { + flag_name := fmt.tprintf("%cF", line[idx]) + if flag,ok := reflect.enum_from_name(Flag, flag_name); ok { + cpu.flags[flag] = true + } else { + fmt.eprintfln("Error parsing flag enum %s", flag_name) + } + idx += 1 + } + } else { reg_name := line[i:i+2] reg_value := get_cpu_register_by_name(&cpu, reg_name) hex_string := line[i+6:i+10] diff --git a/types.odin b/types.odin index 577e4db..9e777a3 100644 --- a/types.odin +++ b/types.odin @@ -22,9 +22,18 @@ RegisterValue :: struct #raw_union { full: u16, } -Flags :: struct { - ZF: bool, - SF: bool, +Flag :: enum { + OF, + SF, + ZF, + AF, + PF, + CF, + // NOTE: These are the control flags, previous are status flags, justing noting in + // case we have to make that distinction in later modeling + TF, + DF, + IF, } WordSize :: enum { @@ -51,7 +60,7 @@ RegisterAccess :: enum { } RegisterId :: struct { - idx: int, + name: Register, access: RegisterAccess, } ImmediateSize :: enum { @@ -144,8 +153,8 @@ Instruction :: struct { } Cpu :: struct { - flags: Flags, - registers: [12]RegisterValue, + flags: [Flag]bool, + registers: [Register]RegisterValue, memory: [dynamic]u8, total_bytes_processed: int }