Use an enum to represent registers, parse txt files, check CPU state against expected
This commit is contained in:
parent
305ac557fa
commit
08874c4533
@ -66,7 +66,7 @@ get_op :: proc(inst: Instruction) -> (Op, bool) {
|
|||||||
}
|
}
|
||||||
return op, interseg
|
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{}
|
operand: Operand = None{}
|
||||||
switch opinfo {
|
switch opinfo {
|
||||||
case .None:
|
case .None:
|
||||||
@ -80,11 +80,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
case .SecondByteLast3: reg = data[1] & 0b111
|
case .SecondByteLast3: reg = data[1] & 0b111
|
||||||
}
|
}
|
||||||
if opinfo == .SegmentRegister {
|
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 {
|
} else if word {
|
||||||
operand = RegisterId { id = (int)(reg), access = .Full }
|
operand = RegisterId { idx = (int)(reg), access = .Full }
|
||||||
} else {
|
} 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:
|
case .RegisterMemory:
|
||||||
mod := data[1] >> 6
|
mod := data[1] >> 6
|
||||||
@ -107,9 +107,9 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
processed^ += 2
|
processed^ += 2
|
||||||
} else if mod == 3 {
|
} else if mod == 3 {
|
||||||
if word {
|
if word {
|
||||||
op = RegisterId { id = (int)(rm), access = .Full }
|
op = RegisterId { idx = (int)(rm), access = .Full }
|
||||||
} else {
|
} 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
|
operand = op
|
||||||
@ -126,7 +126,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 }
|
operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 }
|
||||||
processed^ += 1
|
processed^ += 1
|
||||||
case .Accumulator:
|
case .Accumulator:
|
||||||
operand = RegisterId { id = 0, access = word ? .Full : .Low }
|
operand = RegisterId { idx = 0, access = word ? .Full : .Low }
|
||||||
case .DirectAddress:
|
case .DirectAddress:
|
||||||
// operand = DirectAddress { value = get_i16(data[1:]) }
|
// operand = DirectAddress { value = get_i16(data[1:]) }
|
||||||
operand = (DirectAddress)(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
|
// 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)
|
operand = (Jump)((i8)(data[1]) + 2)
|
||||||
case .VariablePort:
|
case .VariablePort:
|
||||||
operand = RegisterId { id = (int)(variable_port.code), access = .Full }
|
operand = RegisterId { idx = (int)(Register.dx), access = .Full }
|
||||||
case .ShiftRotate:
|
case .ShiftRotate:
|
||||||
v_flag := data[0] & 0b10 != 0
|
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:
|
case .Repeat:
|
||||||
bits := (data[1] & 0b1110) >> 1
|
bits := (data[1] & 0b1110) >> 1
|
||||||
w := (data[1] & 0b1) == 1 ? "w" : "b"
|
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) {
|
decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) {
|
||||||
idx := 0
|
idx := 0
|
||||||
has_segment: Maybe(Register)
|
has_segment: Maybe(RegisterId)
|
||||||
has_lock: bool
|
has_lock: bool
|
||||||
for idx < bytes_to_read {
|
for idx < bytes_to_read {
|
||||||
instruction: Instruction
|
instruction: Instruction
|
||||||
@ -203,7 +203,7 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
|||||||
continue
|
continue
|
||||||
} else if inst.opname == .SEGMENT {
|
} else if inst.opname == .SEGMENT {
|
||||||
reg := (curr_byte & 0b11000) >> 3
|
reg := (curr_byte & 0b11000) >> 3
|
||||||
has_segment = CPU.registers[SEGMENT_REGISTER_START+reg]
|
has_segment = RegisterId { idx = int(SEGMENT_REGISTER_START+reg) }
|
||||||
idx += 1
|
idx += 1
|
||||||
continue
|
continue
|
||||||
} else if inst.opname == .AAM {
|
} else if inst.opname == .AAM {
|
||||||
|
@ -4,34 +4,40 @@ import "core:os"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
get_operand_value :: proc(operand: Operand) -> u16 {
|
get_operand_value :: proc(operand: Operand) -> u16 {
|
||||||
#partial switch opr in operand {
|
#partial switch opr in operand {
|
||||||
case Immediate:
|
case Immediate:
|
||||||
return opr.value
|
return opr.value
|
||||||
case RegisterId:
|
case RegisterId:
|
||||||
|
reg_val := CPU.registers[opr.idx]
|
||||||
switch opr.access {
|
switch opr.access {
|
||||||
case .Low, .High:
|
case .Low, .High:
|
||||||
val := opr.access == .Low ? CPU.registers[opr.id].value.low : CPU.registers[opr.id].value.high
|
return u16(opr.access == .Low ? reg_val.low : reg_val.high)
|
||||||
return u16(val)
|
|
||||||
case .Full:
|
case .Full:
|
||||||
return CPU.registers[opr.id].value.full
|
return reg_val.full
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
set_register_value :: proc(reg_id: RegisterId, value: u16) {
|
set_register_value :: proc(reg: RegisterId, value: u16) {
|
||||||
switch reg_id.access {
|
switch reg.access {
|
||||||
case .Low:
|
case .Low:
|
||||||
CPU.registers[reg_id.id].value.low = u8(value)
|
CPU.registers[reg.idx].low = u8(value)
|
||||||
case .High:
|
case .High:
|
||||||
CPU.registers[reg_id.id].value.high = u8(value)
|
CPU.registers[reg.idx].high = u8(value)
|
||||||
case .Full:
|
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) {
|
check_zero_flag :: proc(value: u16) {
|
||||||
CPU.flags.ZF = value == 0
|
CPU.flags.ZF = value == 0
|
||||||
}
|
}
|
||||||
@ -48,19 +54,19 @@ 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.id].value.full + src_val
|
val := CPU.registers[reg.idx].full + src_val
|
||||||
set_register_value(reg, val)
|
set_register_value(reg, val)
|
||||||
check_zero_flag(val)
|
check_zero_flag(val)
|
||||||
check_sign_flag(val)
|
check_sign_flag(val)
|
||||||
case .SUB:
|
case .SUB:
|
||||||
src_val := get_operand_value(inst.src)
|
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)
|
set_register_value(reg, val)
|
||||||
check_zero_flag(val)
|
check_zero_flag(val)
|
||||||
check_sign_flag(val)
|
check_sign_flag(val)
|
||||||
case .CMP:
|
case .CMP:
|
||||||
src_val := get_operand_value(inst.src)
|
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_zero_flag(val)
|
||||||
check_sign_flag(val)
|
check_sign_flag(val)
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ calculate_effective_address :: proc(r_m: u8) -> string {
|
|||||||
return val
|
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
|
disp: string
|
||||||
switch value in memoryAddr.displacement {
|
switch value in memoryAddr.displacement {
|
||||||
case None:
|
case None:
|
||||||
@ -53,7 +53,7 @@ get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(Register))
|
|||||||
}
|
}
|
||||||
seg_string: string
|
seg_string: string
|
||||||
if segreg, ok := has_segment.?; ok {
|
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)
|
text := fmt.aprintf("%s[%s%s]", seg_string, calculate_effective_address(memoryAddr.addr_id), disp)
|
||||||
return text
|
return text
|
||||||
@ -78,14 +78,14 @@ get_register_name :: proc(reg_id: RegisterId) -> string {
|
|||||||
low_names := [?]string{"al", "cl", "dl", "bl"}
|
low_names := [?]string{"al", "cl", "dl", "bl"}
|
||||||
high_names := [?]string{"ah", "ch", "dh", "bh"}
|
high_names := [?]string{"ah", "ch", "dh", "bh"}
|
||||||
switch reg_id.access {
|
switch reg_id.access {
|
||||||
case .Full: return CPU.registers[reg_id.id].fullname
|
case .Full: return reflect.enum_string(Register(reg_id.idx))
|
||||||
case .Low: return low_names[reg_id.id]
|
case .Low: return low_names[reg_id.idx]
|
||||||
case .High: return high_names[reg_id.id % 4]
|
case .High: return high_names[reg_id.idx % 4]
|
||||||
}
|
}
|
||||||
return ""
|
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
|
string_val: string
|
||||||
switch val in operand {
|
switch val in operand {
|
||||||
case None:
|
case None:
|
||||||
@ -106,7 +106,7 @@ get_operand_string :: proc(operand: Operand, has_segment: Maybe(Register)) -> st
|
|||||||
case DirectAddress:
|
case DirectAddress:
|
||||||
seg_string: string
|
seg_string: string
|
||||||
if segreg, ok := has_segment.?; ok {
|
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)
|
string_val = fmt.aprintf("%s[%d]", seg_string, val)
|
||||||
case Jump:
|
case Jump:
|
||||||
|
44
sim8086.odin
44
sim8086.odin
@ -1,32 +1,15 @@
|
|||||||
package sim_8086
|
package sim_8086
|
||||||
|
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:path"
|
import path "core:path/filepath"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
CPU := Cpu {
|
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),
|
memory = make([dynamic]u8, 65536),
|
||||||
}
|
}
|
||||||
|
|
||||||
variable_port := CPU.registers[2]
|
|
||||||
|
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
f,err := os.open(os.args[1])
|
f,err := os.open(os.args[1])
|
||||||
if err != os.ERROR_NONE {
|
if err != os.ERROR_NONE {
|
||||||
@ -59,22 +42,23 @@ main :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if what_to_print == "registers" || what_to_print == "all" {
|
if what_to_print == "registers" || what_to_print == "all" {
|
||||||
print_reg :: proc(reg: Register) {
|
for reg,i in CPU.registers {
|
||||||
full := fmt.aprintf("%s: %d ", reg.fullname, reg.value.full)
|
full := fmt.aprintf("%s: %d ", get_register_name(RegisterId{idx=i}), reg.full)
|
||||||
hex := fmt.aprintf("0x%04x ", reg.value.full)
|
hex := fmt.aprintf("0x%04x ", reg.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.value.high, reg.value.low)
|
full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg.high, reg.low)
|
||||||
fmt.println()
|
fmt.println()
|
||||||
}
|
}
|
||||||
for reg in CPU.registers {
|
|
||||||
print_reg(reg)
|
|
||||||
}
|
|
||||||
// fmt.println("Checking Against Expected State")
|
|
||||||
|
|
||||||
// fmt.println(os.args[1])
|
path,ok := strings.replace(os.args[1], ".bin", ".txt", 1)
|
||||||
// path := path.base_no_ext(os.args[1])
|
expected_cpu,_ := extract_expected_cpu_state(path)
|
||||||
// fmt.println(path)
|
for reg,i in expected_cpu.registers {
|
||||||
// extract_expected_cpu_state(path)
|
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" {
|
if what_to_print == "instructions" || what_to_print == "all" {
|
||||||
print_instructions_stdout(instructions_list[:])
|
print_instructions_stdout(instructions_list[:])
|
||||||
|
54
testing.odin
54
testing.odin
@ -4,35 +4,41 @@ import "core:os"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
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
|
cpu: Cpu
|
||||||
|
|
||||||
// filename := fmt.aprintf("./asm_files/list-%04d.txt", listing_num)
|
data,ok := os.read_entire_file(filename)
|
||||||
// fmt.println(filename)
|
if !ok {
|
||||||
// data,ok := os.read_entire_file(filename)
|
return cpu, false
|
||||||
// if !ok {
|
}
|
||||||
// return cpu, false
|
defer delete(data)
|
||||||
// }
|
|
||||||
// defer delete(data)
|
|
||||||
|
|
||||||
// content := string(data)
|
content := string(data)
|
||||||
// lines := strings.split(content, "\n")
|
lines := strings.split(content, "\n")
|
||||||
// defer delete(lines)
|
defer delete(lines)
|
||||||
|
|
||||||
// for line in lines {
|
for line in lines {
|
||||||
// for c in line {
|
space_count := 0
|
||||||
// if c != ' ' {
|
for c,i in line {
|
||||||
// continue
|
if space_count == 0 && c != ' ' {
|
||||||
// } else {
|
break
|
||||||
// fmt.print(c)
|
} else if c == ' ' {
|
||||||
// }
|
space_count += 1
|
||||||
// }
|
} else {
|
||||||
// fmt.println()
|
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
|
return cpu, true
|
||||||
}
|
}
|
||||||
|
29
types.odin
29
types.odin
@ -1,14 +1,25 @@
|
|||||||
package sim_8086
|
package sim_8086
|
||||||
|
|
||||||
Register :: struct {
|
Register :: enum {
|
||||||
fullname: string,
|
ax,
|
||||||
value: struct #raw_union {
|
cx,
|
||||||
|
dx,
|
||||||
|
bx,
|
||||||
|
sp,
|
||||||
|
bp,
|
||||||
|
si,
|
||||||
|
di,
|
||||||
|
es,
|
||||||
|
cs,
|
||||||
|
ss,
|
||||||
|
ds,
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterValue :: struct #raw_union {
|
||||||
using _: struct {
|
using _: struct {
|
||||||
low, high: byte,
|
low, high: byte,
|
||||||
},
|
},
|
||||||
full: u16,
|
full: u16,
|
||||||
},
|
|
||||||
code: u8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Flags :: struct {
|
Flags :: struct {
|
||||||
@ -34,14 +45,14 @@ Displacement :: union {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RegisterAccess :: enum {
|
RegisterAccess :: enum {
|
||||||
|
Full,
|
||||||
Low,
|
Low,
|
||||||
High,
|
High,
|
||||||
Full,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterId :: struct {
|
RegisterId :: struct {
|
||||||
|
idx: int,
|
||||||
access: RegisterAccess,
|
access: RegisterAccess,
|
||||||
id: int,
|
|
||||||
}
|
}
|
||||||
ImmediateSize :: enum {
|
ImmediateSize :: enum {
|
||||||
Signed8,
|
Signed8,
|
||||||
@ -125,7 +136,7 @@ Instruction :: struct {
|
|||||||
indirect_intersegment: bool,
|
indirect_intersegment: bool,
|
||||||
// TODO: This is trickier than I thought, it's more than just the one instruction
|
// TODO: This is trickier than I thought, it's more than just the one instruction
|
||||||
// that uses it
|
// that uses it
|
||||||
has_segment: Maybe(Register),
|
has_segment: Maybe(RegisterId),
|
||||||
has_lock: bool,
|
has_lock: bool,
|
||||||
bytes_read: int,
|
bytes_read: int,
|
||||||
raw_data: []u8,
|
raw_data: []u8,
|
||||||
@ -134,7 +145,7 @@ Instruction :: struct {
|
|||||||
|
|
||||||
Cpu :: struct {
|
Cpu :: struct {
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
registers: [12]Register,
|
registers: [12]RegisterValue,
|
||||||
memory: [dynamic]u8,
|
memory: [dynamic]u8,
|
||||||
total_bytes_processed: int
|
total_bytes_processed: int
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user