From df105ea3cf922815be9403466093880110f589e9 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 22 Mar 2025 18:54:36 +0700 Subject: [PATCH] Generalize with perform_load and perform_store, use bit_set for cpu flags --- execution.odin | 195 +++++++++++++++++++------------------------------ printing.odin | 2 +- testing.odin | 5 ++ types.odin | 2 + 4 files changed, 85 insertions(+), 119 deletions(-) diff --git a/execution.odin b/execution.odin index e8ba329..95c1f02 100644 --- a/execution.odin +++ b/execution.odin @@ -23,7 +23,7 @@ get_effective_address_value :: proc(cpu: ^Cpu, id: u8) -> i16 { return -1 } -get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 { +perform_load :: proc(cpu: ^Cpu, operand: Operand, is_word: bool) -> i16 { #partial switch opr in operand { case Immediate: return i16(opr.value) @@ -36,139 +36,74 @@ get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 { return i16(reg_val.full) } case DirectAddress: - value := i16(u16(cpu.memory[opr+1] << 8) | u16(cpu.memory[opr])) + value: i16 + if is_word { + value = i16(u16(cpu.memory[opr+1] << 8) | u16(cpu.memory[opr])) + } else { + value = i16(cpu.memory[opr]) + } return value case MemoryAddr: + value: i16 idx := get_effective_address_value(cpu, opr.addr_id) + opr.displacement - value := i16(u16(cpu.memory[idx+1] << 8) | u16(cpu.memory[idx])) - // fmt.println("Checking", idx) - // for i in 0..<6 { - // fmt.printf("%04x ", cpu.memory[int(idx)-3+i]) - // } - // fmt.println() + if is_word { + value = i16(u16(cpu.memory[idx+1] << 8) | u16(cpu.memory[idx])) + } else { + value = i16(cpu.memory[idx]) + } return value } return 0 } -set_register_value :: proc(cpu: ^Cpu, reg: RegisterId, value: i16) { - switch reg.access { - case .Low: - cpu.registers[reg.name].low = u8(value) - case .High: - cpu.registers[reg.name].high = u8(value) - case .Full: - cpu.registers[reg.name].full = i16(value) +check_flags :: proc(cpu: ^Cpu, flags: bit_set[Flag], dst: i16, src: i16, result: i16, is_add: bool) { + if .ZF in flags do cpu.flags[.ZF] = result == 0 + if .SF in flags do cpu.flags[.SF] = (result >> 15) & 0x1 == 1 + if .CF in flags { + cpu.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst } -} - -get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue { - reg,_ := reflect.enum_from_name(Register, name) - return &cpu.registers[reg] -} - -check_zero_flag :: proc(cpu: ^Cpu, value: i16) { - cpu.flags[.ZF] = value == 0 -} - -check_sign_flag :: proc(cpu: ^Cpu, value: i16) { - cpu.flags[.SF] = (value >> 15) & 0x1 == 1 -} - -check_carry_flag :: proc(cpu: ^Cpu, dst: i16, src: i16, is_add: bool) { - cpu.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst -} - -check_parity_flag :: proc(cpu: ^Cpu, value: i16) { - val := value - bit_count := 0 - val &= 0x00FF - for val != 0 { - if val & 0x1 == 1 { - bit_count += 1 + if .AF in flags { + lhs, rhs := dst & 0xF, src & 0xF + cpu.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs + } + if .PF in flags { + val := result + bit_count := 0 + val &= 0x00FF + for val != 0 { + if val & 0x1 == 1 { + bit_count += 1 + } + val >>= 1 } - val >>= 1 + cpu.flags[.PF] = bit_count % 2 == 0 } - cpu.flags[.PF] = bit_count % 2 == 0 } -check_auxiliary_carry_flag :: proc(cpu: ^Cpu, dst: i16, src: i16, is_add: bool) { - lhs, rhs := dst & 0xF, src & 0xF - cpu.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs -} - -check_flags :: proc(cpu: ^Cpu, value: i16) { - check_zero_flag(cpu, value) - check_sign_flag(cpu, value) - check_parity_flag(cpu, value) +perform_store :: proc(cpu: ^Cpu, value: i16, operand: Operand, is_word: bool) { + #partial switch opr in operand { + case RegisterId: + switch opr.access { + case .Low: + cpu.registers[opr.name].low = u8(value) + case .High: + cpu.registers[opr.name].high = u8(value) + case .Full: + cpu.registers[opr.name].full = i16(value) + } + case DirectAddress: + cpu.memory[opr] = u8(value) + if is_word do cpu.memory[opr+1] = u8((u16(value) & 0xFF00) >> 8) + case MemoryAddr: + effective_addr_val := get_effective_address_value(cpu, opr.addr_id) + addr := effective_addr_val + opr.displacement + cpu.memory[addr] = u8(value) + if is_word do cpu.memory[addr+1] = u8((u16(value) & 0xFF00) >> 8) + } } execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) { - if reg,ok := inst.dst.(RegisterId); ok { - #partial switch inst.opname { - case .MOV: - src_val := get_operand_value(cpu, inst.src) - set_register_value(cpu, reg, src_val) - case .ADD: - src_val := get_operand_value(cpu, inst.src) - dst_val := get_operand_value(cpu, inst.dst) - val := i16(cpu.registers[reg.name].full) + src_val - set_register_value(cpu, reg, val) - check_flags(cpu, val) - check_auxiliary_carry_flag(cpu, dst_val, src_val, true) - check_carry_flag(cpu, dst_val, src_val, true) - case .SUB: - src_val := get_operand_value(cpu, inst.src) - dst_val := get_operand_value(cpu, inst.dst) - val := i16(cpu.registers[reg.name].full) - src_val - set_register_value(cpu, reg, val) - check_flags(cpu, val) - check_auxiliary_carry_flag(cpu, dst_val, src_val, false) - check_carry_flag(cpu, dst_val, src_val, false) - case .CMP: - src_val := get_operand_value(cpu, inst.src) - dst_val := get_operand_value(cpu, inst.dst) - val := i16(cpu.registers[reg.name].full) - src_val - check_flags(cpu, val) - check_auxiliary_carry_flag(cpu, dst_val, src_val, false) - check_carry_flag(cpu, dst_val, src_val, false) - } - } else if addr,ok := inst.dst.(DirectAddress); ok { - #partial switch inst.opname { - case .MOV: - if imm,ok := inst.src.(Immediate); ok { - if imm.size == ._16 { - cpu.memory[addr] = u8(imm.value & 0x00FF) - cpu.memory[addr+1] = u8((u16(imm.value) & 0xFF00) >> 8) - } else { - cpu.memory[addr] = u8(imm.value) - } - } - } - } else if mem_addr,ok := inst.dst.(MemoryAddr); ok { - #partial switch inst.opname { - case .MOV: - value := get_operand_value(cpu, inst.src) - - effective_addr_val := get_effective_address_value(cpu, mem_addr.addr_id) - addr := effective_addr_val + mem_addr.displacement - cpu.memory[addr] = u8(value & 0x00FF) - cpu.memory[addr+1] = u8((u16(value) & 0xFF00) >> 8) - - // TODO: We need a way to detect if it's a byte or a word - // if imm,ok := inst.src.(Immediate); ok { - // // TODO: We need a function that returns the registers to check out - // effective_addr_val := get_effective_address_value(cpu, mem_addr.addr_id) - // addr := effective_addr_val + mem_addr.displacement_value - // if imm.size == .Signed16 { - // cpu.memory[addr] = u8(imm.value & 0x00FF) - // cpu.memory[addr+1] = u8((u16(imm.value) & 0xFF00) >> 8) - // } else { - // cpu.memory[addr] = u8(imm.value) - // } - // } - } - } else if jmp_offset,ok := inst.src.(Jump); ok { + if jmp_offset,ok := inst.src.(Jump); ok { jump: bool #partial switch inst.opname { case .JNZ, .JNE: jump = !cpu.flags[.ZF] @@ -187,6 +122,30 @@ execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) { cpu.registers[.ip].full += i16(jmp_offset) cpu.registers[.ip].full -= i16(inst.bytes_read) } + return + } + status_flags := bit_set[Flag]{.OF, .SF, .ZF, .AF, .PF, .CF} + #partial switch inst.opname { + case .MOV: + src_val := perform_load(cpu, inst.src, inst.is_word) + perform_store(cpu, src_val, inst.dst, inst.is_word) + case .ADD: + src_val := perform_load(cpu, inst.src, inst.is_word) + dst_val := perform_load(cpu, inst.dst, inst.is_word) + val := dst_val + src_val + perform_store(cpu, val, inst.dst, inst.is_word) + check_flags(cpu, status_flags, dst_val, src_val, val, true) + case .SUB: + src_val := perform_load(cpu, inst.src, inst.is_word) + dst_val := perform_load(cpu, inst.dst, inst.is_word) + val := dst_val - src_val + perform_store(cpu, val, inst.dst, inst.is_word) + check_flags(cpu, status_flags, dst_val, src_val, val, false) + case .CMP: + src_val := perform_load(cpu, inst.src, inst.is_word) + dst_val := perform_load(cpu, inst.dst, inst.is_word) + val := dst_val - src_val + check_flags(cpu, status_flags, dst_val, src_val, val, false) } } diff --git a/printing.odin b/printing.odin index 6b66340..488ca80 100644 --- a/printing.odin +++ b/printing.odin @@ -70,7 +70,7 @@ get_operand_string :: proc(operand: Operand, has_segment: Maybe(RegisterId)) -> case RegisterId: string_val = get_register_name(val) case Immediate: - string_val = fmt.aprintf("%d", i16(val.value)) + string_val = fmt.aprintf("%d", val.value) case MemoryAddr: string_val = get_memory_string(val, has_segment) case DirectAddress: diff --git a/testing.odin b/testing.odin index 4ca3aff..c9fc145 100644 --- a/testing.odin +++ b/testing.odin @@ -7,6 +7,11 @@ import "core:strings" import "core:strconv" import "core:reflect" +get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue { + reg,_ := reflect.enum_from_name(Register, name) + return &cpu.registers[reg] +} + parse_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) { cpu: Cpu diff --git a/types.odin b/types.odin index fb0bfbf..e2b8509 100644 --- a/types.odin +++ b/types.odin @@ -68,6 +68,7 @@ Immediate :: struct { MemoryAddr :: struct { addr_id: u8, displacement: i16, + disp_size: ImmediateSize, } DirectAddress :: distinct i16 Jump :: distinct i8 @@ -119,6 +120,7 @@ InstructionInfo :: struct { desc: string, src: OperandInfo, dst: OperandInfo, + flags: bit_set[Flag], word_size: WordSize, reg_info: RegisterEncodingBits, has_flip: bool,