CF, OF, and PF flags, keep values as i16, update ref test script
This commit is contained in:
parent
5cf4768b80
commit
731e5bd45d
@ -119,11 +119,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
if inst.has_sign_extension {
|
if inst.has_sign_extension {
|
||||||
word_signed &&= data[0] & 0b0000_0010 == 0
|
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 }
|
operand = Immediate { value = value, size = word_signed ? .Signed16 : .Signed8 }
|
||||||
processed^ += word_signed ? 2 : 1
|
processed^ += word_signed ? 2 : 1
|
||||||
case .ImmediateUnsigned:
|
case .ImmediateUnsigned:
|
||||||
operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 }
|
operand = Immediate { value = i16(data[processed^]), size = .Unsigned8 }
|
||||||
processed^ += 1
|
processed^ += 1
|
||||||
case .Accumulator:
|
case .Accumulator:
|
||||||
operand = RegisterId { name = Register(0), access = word ? .Full : .Low }
|
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
|
processed^ += 1
|
||||||
case .DirectWithinSegment:
|
case .DirectWithinSegment:
|
||||||
value := (int)(get_i16(data[1:])) + CPU.total_bytes_processed + 3
|
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
|
processed^ += 2
|
||||||
case .Intersegment:
|
case .Intersegment:
|
||||||
operand = Intersegment {
|
operand = Intersegment {
|
||||||
|
@ -6,30 +6,31 @@ import "core:math"
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
|
|
||||||
get_operand_value :: proc(operand: Operand) -> u16 {
|
get_operand_value :: proc(operand: Operand) -> i16 {
|
||||||
#partial switch opr in operand {
|
#partial switch opr in operand {
|
||||||
case Immediate:
|
case Immediate:
|
||||||
return opr.value
|
// fmt.printfln("0x%4x %d", i16(opr.value), opr.value)
|
||||||
|
return i16(opr.value)
|
||||||
case RegisterId:
|
case RegisterId:
|
||||||
reg_val := CPU.registers[opr.name]
|
reg_val := CPU.registers[opr.name]
|
||||||
switch opr.access {
|
switch opr.access {
|
||||||
case .Low, .High:
|
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:
|
case .Full:
|
||||||
return reg_val.full
|
return i16(reg_val.full)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
set_register_value :: proc(reg: RegisterId, value: u16) {
|
set_register_value :: proc(reg: RegisterId, value: i16) {
|
||||||
switch reg.access {
|
switch reg.access {
|
||||||
case .Low:
|
case .Low:
|
||||||
CPU.registers[reg.name].low = u8(value)
|
CPU.registers[reg.name].low = u8(value)
|
||||||
case .High:
|
case .High:
|
||||||
CPU.registers[reg.name].high = u8(value)
|
CPU.registers[reg.name].high = u8(value)
|
||||||
case .Full:
|
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]
|
return &cpu.registers[reg]
|
||||||
}
|
}
|
||||||
|
|
||||||
check_zero_flag :: proc(value: u16) {
|
check_zero_flag :: proc(value: i16) {
|
||||||
CPU.flags[.ZF] = value == 0
|
CPU.flags[.ZF] = value == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
check_sign_flag :: proc(value: u16) {
|
check_sign_flag :: proc(value: i16) {
|
||||||
CPU.flags[.SF] = value >> 15 == 1
|
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) {
|
execute_instruction :: proc(inst: Instruction) {
|
||||||
@ -54,21 +83,27 @@ execute_instruction :: proc(inst: Instruction) {
|
|||||||
set_register_value(reg, src_val)
|
set_register_value(reg, src_val)
|
||||||
case .ADD:
|
case .ADD:
|
||||||
src_val := get_operand_value(inst.src)
|
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)
|
set_register_value(reg, val)
|
||||||
check_zero_flag(val)
|
check_flags(val)
|
||||||
check_sign_flag(val)
|
check_auxiliary_carry_flag(dst_val, src_val, true)
|
||||||
|
check_carry_flag(dst_val, src_val, true)
|
||||||
case .SUB:
|
case .SUB:
|
||||||
src_val := get_operand_value(inst.src)
|
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)
|
set_register_value(reg, val)
|
||||||
check_zero_flag(val)
|
check_flags(val)
|
||||||
check_sign_flag(val)
|
check_auxiliary_carry_flag(dst_val, src_val, false)
|
||||||
|
check_carry_flag(dst_val, src_val, false)
|
||||||
case .CMP:
|
case .CMP:
|
||||||
src_val := get_operand_value(inst.src)
|
src_val := get_operand_value(inst.src)
|
||||||
val := CPU.registers[reg.name].full - src_val
|
dst_val := get_operand_value(inst.dst)
|
||||||
check_zero_flag(val)
|
val := i16(CPU.registers[reg.name].full) - src_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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
sim8086.odin
23
sim8086.odin
@ -43,33 +43,40 @@ main :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if what_to_print == "registers" || what_to_print == "all" {
|
if what_to_print == "registers" || what_to_print == "all" {
|
||||||
fmt.println("\nRegisters")
|
fmt.println("Registers")
|
||||||
for reg_val,name in CPU.registers {
|
for reg_val,name in CPU.registers {
|
||||||
full := fmt.aprintf("%s: %d ", name, reg_val.full)
|
full := fmt.aprintf("%s: %d ", name, i16(reg_val.full))
|
||||||
hex := fmt.aprintf("0x%04x ", reg_val.full)
|
hex := fmt.aprintf("0x%04x ", u16(reg_val.full))
|
||||||
fmt.printf("%s %*[1]s %s %*[4]s %08b %08b",
|
fmt.printf("%s %*[1]s %s %*[4]s %08b %08b",
|
||||||
full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg_val.high, reg_val.low)
|
full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg_val.high, reg_val.low)
|
||||||
fmt.println()
|
fmt.println()
|
||||||
}
|
}
|
||||||
fmt.println("\nFlags")
|
fmt.println("\nFlags")
|
||||||
for state,flag in CPU.flags {
|
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)
|
path,ok := strings.replace(os.args[1], ".bin", ".txt", 1)
|
||||||
ref_cpu,_ := extract_reference_cpu_state(path)
|
ref_cpu,_ := extract_reference_cpu_state(path)
|
||||||
for reg_val,name in ref_cpu.registers {
|
for reg_val,name in ref_cpu.registers {
|
||||||
if CPU.registers[name].full != reg_val.full {
|
if CPU.registers[name].full != reg_val.full {
|
||||||
msg := "%s register does not match reference - Expected %04x | Actual %04x"
|
msg := "%s register does not match reference - Expected 0x%04x | Actual 0x%04x"
|
||||||
fmt.eprintfln(msg, name, reg_val.full, CPU.registers[name].full)
|
fmt.printfln(msg, name, reg_val.full, CPU.registers[name].full)
|
||||||
|
failed_ref = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for f in Flag {
|
for f in Flag {
|
||||||
if ref_cpu.flags[f] != CPU.flags[f] {
|
if ref_cpu.flags[f] != CPU.flags[f] {
|
||||||
msg := "%s flag does not match reference - Expected %t | Actual %t"
|
msg := "%s flag does not match reference - Expected %d | Actual %d"
|
||||||
fmt.eprintfln(msg, f, ref_cpu.flags[f], CPU.flags[f])
|
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" {
|
if what_to_print == "instructions" || what_to_print == "all" {
|
||||||
print_instructions_stdout(instructions_list[:])
|
print_instructions_stdout(instructions_list[:])
|
||||||
|
@ -16,13 +16,14 @@ odin build . -out:sim8086
|
|||||||
for asm_txt in asm_files/*.txt;
|
for asm_txt in asm_files/*.txt;
|
||||||
do
|
do
|
||||||
asm_listing=$(basename $asm_txt .txt)
|
asm_listing=$(basename $asm_txt .txt)
|
||||||
./sim8086 asm_files/${asm_listing}.bin registers | awk '{print $1 $4}' | sort > temp1
|
output=$(./sim8086 asm_files/${asm_listing}.bin registers)
|
||||||
cat $asm_txt | awk '/Final registers/,0' | tail -n +2 | awk '{print $1 $2}' | sort > temp2
|
|
||||||
diff -U0 --color temp1 temp2
|
|
||||||
if [ $? -eq 1 ]; then
|
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
|
else
|
||||||
echo Listing $asm_listing Succeded!
|
echo -e "Listing $asm_listing: ${GREEN}Succeded!${NC}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
rm temp1 temp2
|
|
@ -44,7 +44,7 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
|||||||
reg_value := get_cpu_register_by_name(&cpu, reg_name)
|
reg_value := get_cpu_register_by_name(&cpu, reg_name)
|
||||||
hex_string := line[i+6:i+10]
|
hex_string := line[i+6:i+10]
|
||||||
if hex_num,ok := strconv.parse_int(hex_string, 16); ok {
|
if hex_num,ok := strconv.parse_int(hex_string, 16); ok {
|
||||||
reg_value^.full = u16(hex_num)
|
reg_value^.full = i16(hex_num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -19,7 +19,7 @@ RegisterValue :: struct #raw_union {
|
|||||||
using _: struct {
|
using _: struct {
|
||||||
low, high: byte,
|
low, high: byte,
|
||||||
},
|
},
|
||||||
full: u16,
|
full: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
Flag :: enum {
|
Flag :: enum {
|
||||||
@ -69,7 +69,7 @@ ImmediateSize :: enum {
|
|||||||
Signed16,
|
Signed16,
|
||||||
}
|
}
|
||||||
Immediate :: struct {
|
Immediate :: struct {
|
||||||
value: u16,
|
value: i16,
|
||||||
size: ImmediateSize,
|
size: ImmediateSize,
|
||||||
}
|
}
|
||||||
MemoryAddr :: struct {
|
MemoryAddr :: struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user