diff --git a/decoding.odin b/decoding.odin index 648fd57..d295f8d 100644 --- a/decoding.odin +++ b/decoding.odin @@ -119,11 +119,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr if inst.has_sign_extension { word_signed &&= data[0] & 0b0000_0010 == 0 } - value: u16 = word_signed ? u16(get_i16(data[data_idx:])) : u16(data[data_idx]) + value: i16 = word_signed ? get_i16(data[data_idx:]) : i16(i8(data[data_idx])) operand = Immediate { value = value, size = word_signed ? .Signed16 : .Signed8 } processed^ += word_signed ? 2 : 1 case .ImmediateUnsigned: - operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 } + operand = Immediate { value = i16(data[processed^]), size = .Unsigned8 } processed^ += 1 case .Accumulator: operand = RegisterId { name = Register(0), access = word ? .Full : .Low } @@ -155,7 +155,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr processed^ += 1 case .DirectWithinSegment: value := (int)(get_i16(data[1:])) + CPU.total_bytes_processed + 3 - operand = Immediate { value = u16(value), size = .Signed16 } + operand = Immediate { value = i16(value), size = .Signed16 } processed^ += 2 case .Intersegment: operand = Intersegment { diff --git a/execution.odin b/execution.odin index bf08a62..f281d97 100644 --- a/execution.odin +++ b/execution.odin @@ -6,30 +6,31 @@ import "core:math" import "core:strings" import "core:reflect" -get_operand_value :: proc(operand: Operand) -> u16 { +get_operand_value :: proc(operand: Operand) -> i16 { #partial switch opr in operand { case Immediate: - return opr.value + // fmt.printfln("0x%4x %d", i16(opr.value), opr.value) + return i16(opr.value) case RegisterId: reg_val := CPU.registers[opr.name] switch opr.access { case .Low, .High: - return u16(opr.access == .Low ? reg_val.low : reg_val.high) + return i16(opr.access == .Low ? reg_val.low : reg_val.high) case .Full: - return reg_val.full + return i16(reg_val.full) } } return 0 } -set_register_value :: proc(reg: RegisterId, value: u16) { +set_register_value :: proc(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 = u16(value) + CPU.registers[reg.name].full = i16(value) } } @@ -38,12 +39,40 @@ get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue { return &cpu.registers[reg] } -check_zero_flag :: proc(value: u16) { +check_zero_flag :: proc(value: i16) { CPU.flags[.ZF] = value == 0 } -check_sign_flag :: proc(value: u16) { - CPU.flags[.SF] = value >> 15 == 1 +check_sign_flag :: proc(value: i16) { + CPU.flags[.SF] = (value >> 15) & 0x1 == 1 +} + +check_carry_flag :: proc(dst: i16, src: i16, is_add: bool) { + CPU.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst +} + +check_parity_flag :: proc(value: i16) { + val := value + bit_count := 0 + val &= 0x00FF + for val != 0 { + if val & 0x1 == 1 { + bit_count += 1 + } + val >>= 1 + } + CPU.flags[.PF] = val % 2 == 0 +} + +check_auxiliary_carry_flag :: proc(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(value: i16) { + check_zero_flag(value) + check_sign_flag(value) + check_parity_flag(value) } execute_instruction :: proc(inst: Instruction) { @@ -54,21 +83,27 @@ execute_instruction :: proc(inst: Instruction) { set_register_value(reg, src_val) case .ADD: src_val := get_operand_value(inst.src) - val := CPU.registers[reg.name].full + src_val + dst_val := get_operand_value(inst.dst) + val := i16(CPU.registers[reg.name].full) + src_val set_register_value(reg, val) - check_zero_flag(val) - check_sign_flag(val) + check_flags(val) + check_auxiliary_carry_flag(dst_val, src_val, true) + check_carry_flag(dst_val, src_val, true) case .SUB: src_val := get_operand_value(inst.src) - val := CPU.registers[reg.name].full - src_val + dst_val := get_operand_value(inst.dst) + val := i16(CPU.registers[reg.name].full) - src_val set_register_value(reg, val) - check_zero_flag(val) - check_sign_flag(val) + check_flags(val) + check_auxiliary_carry_flag(dst_val, src_val, false) + check_carry_flag(dst_val, src_val, false) case .CMP: src_val := get_operand_value(inst.src) - val := CPU.registers[reg.name].full - src_val - check_zero_flag(val) - check_sign_flag(val) + dst_val := get_operand_value(inst.dst) + val := i16(CPU.registers[reg.name].full) - src_val + check_flags(val) + check_auxiliary_carry_flag(dst_val, src_val, false) + check_carry_flag(dst_val, src_val, false) } } } diff --git a/sim8086.odin b/sim8086.odin index d4131c7..088a1d5 100644 --- a/sim8086.odin +++ b/sim8086.odin @@ -43,33 +43,40 @@ main :: proc() { } if what_to_print == "registers" || what_to_print == "all" { - fmt.println("\nRegisters") + fmt.println("Registers") for reg_val,name in CPU.registers { - full := fmt.aprintf("%s: %d ", name, reg_val.full) - hex := fmt.aprintf("0x%04x ", reg_val.full) + full := fmt.aprintf("%s: %d ", name, i16(reg_val.full)) + hex := fmt.aprintf("0x%04x ", u16(reg_val.full)) fmt.printf("%s %*[1]s %s %*[4]s %08b %08b", 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) + fmt.printf("%c:%d ",reflect.enum_string(flag)[0], state ? 1 : 0) } + fmt.println() + failed_ref: bool path,ok := strings.replace(os.args[1], ".bin", ".txt", 1) 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) + msg := "%s register does not match reference - Expected 0x%04x | Actual 0x%04x" + fmt.printfln(msg, name, reg_val.full, CPU.registers[name].full) + failed_ref = true } } 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]) + msg := "%s flag does not match reference - Expected %d | Actual %d" + fmt.printfln(msg, f, ref_cpu.flags[f] ? 1 : 0, CPU.flags[f] ? 1 : 0) + failed_ref = true } } + if failed_ref { + os.exit(1) + } } if what_to_print == "instructions" || what_to_print == "all" { print_instructions_stdout(instructions_list[:]) diff --git a/test_registers.sh b/test_ref_cpu.sh similarity index 52% rename from test_registers.sh rename to test_ref_cpu.sh index 76549d1..06d868b 100755 --- a/test_registers.sh +++ b/test_ref_cpu.sh @@ -16,13 +16,14 @@ odin build . -out:sim8086 for asm_txt in asm_files/*.txt; do asm_listing=$(basename $asm_txt .txt) - ./sim8086 asm_files/${asm_listing}.bin registers | awk '{print $1 $4}' | sort > temp1 - cat $asm_txt | awk '/Final registers/,0' | tail -n +2 | awk '{print $1 $2}' | sort > temp2 - diff -U0 --color temp1 temp2 + output=$(./sim8086 asm_files/${asm_listing}.bin registers) if [ $? -eq 1 ]; then - echo Listing $asm_listing Failed! + echo -e "Listing $asm_listing ${RED}Failed!${NC}" + echo "$output" | while IFS= read -r line; do + echo "$line" + done + echo -e "" else - echo Listing $asm_listing Succeded! + echo -e "Listing $asm_listing: ${GREEN}Succeded!${NC}" fi done -rm temp1 temp2 diff --git a/testing.odin b/testing.odin index e4d6463..6f44eb5 100644 --- a/testing.odin +++ b/testing.odin @@ -44,7 +44,7 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) { 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) + reg_value^.full = i16(hex_num) } } break diff --git a/types.odin b/types.odin index 9e777a3..bd06784 100644 --- a/types.odin +++ b/types.odin @@ -19,7 +19,7 @@ RegisterValue :: struct #raw_union { using _: struct { low, high: byte, }, - full: u16, + full: i16, } Flag :: enum { @@ -69,7 +69,7 @@ ImmediateSize :: enum { Signed16, } Immediate :: struct { - value: u16, + value: i16, size: ImmediateSize, } MemoryAddr :: struct {