Add inst decoded type for better modeling, handle jumps correctly with IP register
This commit is contained in:
parent
d0f91f15f8
commit
99fd7fabd7
@ -66,7 +66,9 @@ get_op :: proc(inst: Instruction) -> (Op, bool) {
|
||||
}
|
||||
return op, interseg
|
||||
}
|
||||
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand {
|
||||
|
||||
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int,
|
||||
total_bytes_processed: int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand {
|
||||
operand: Operand = None{}
|
||||
switch opinfo {
|
||||
case .None:
|
||||
@ -154,7 +156,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
||||
operand = Repeat(fmt.aprintf("%s%s", rep, w))
|
||||
processed^ += 1
|
||||
case .DirectWithinSegment:
|
||||
value := (int)(get_i16(data[1:])) + CPU.total_bytes_processed + 3
|
||||
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
|
||||
operand = Immediate { value = i16(value), size = .Signed16 }
|
||||
processed^ += 2
|
||||
case .Intersegment:
|
||||
@ -167,10 +169,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
||||
return operand
|
||||
}
|
||||
|
||||
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) -> int {
|
||||
idx := 0
|
||||
has_segment: Maybe(RegisterId)
|
||||
has_lock: bool
|
||||
total_bytes_processed: int
|
||||
for idx < bytes_to_read {
|
||||
instruction: Instruction
|
||||
processed := 1
|
||||
@ -234,8 +237,8 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
||||
case .Always16: word = true
|
||||
}
|
||||
|
||||
dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, word, has_segment)
|
||||
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, word, has_segment)
|
||||
dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, total_bytes_processed, word, has_segment)
|
||||
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, total_bytes_processed, word, has_segment)
|
||||
|
||||
if flip {
|
||||
src_opr, dst_opr = dst_opr, src_opr
|
||||
@ -252,6 +255,7 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
||||
instruction.info = inst
|
||||
instruction.has_lock = has_lock
|
||||
instruction.has_segment = has_segment
|
||||
instruction.segment_offset += total_bytes_processed
|
||||
instruction.opname = inst.opname
|
||||
instruction.opname,instruction.indirect_intersegment = get_op(instruction)
|
||||
|
||||
@ -262,6 +266,7 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
||||
|
||||
has_lock = false
|
||||
has_segment = nil
|
||||
CPU.total_bytes_processed = idx
|
||||
total_bytes_processed = idx
|
||||
}
|
||||
return total_bytes_processed
|
||||
}
|
||||
|
111
execution.odin
111
execution.odin
@ -6,13 +6,15 @@ import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
get_operand_value :: proc(operand: Operand) -> i16 {
|
||||
get_ip :: proc(cpu: ^Cpu) -> int { return int(cpu.registers[.ip].full) }
|
||||
|
||||
get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 {
|
||||
#partial switch opr in operand {
|
||||
case Immediate:
|
||||
// fmt.printfln("0x%4x %d", i16(opr.value), opr.value)
|
||||
return i16(opr.value)
|
||||
case RegisterId:
|
||||
reg_val := CPU.registers[opr.name]
|
||||
reg_val := cpu.registers[opr.name]
|
||||
switch opr.access {
|
||||
case .Low, .High:
|
||||
return i16(opr.access == .Low ? reg_val.low : reg_val.high)
|
||||
@ -23,14 +25,14 @@ get_operand_value :: proc(operand: Operand) -> i16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
set_register_value :: proc(reg: RegisterId, value: i16) {
|
||||
set_register_value :: proc(cpu: ^Cpu, reg: RegisterId, value: i16) {
|
||||
switch reg.access {
|
||||
case .Low:
|
||||
CPU.registers[reg.name].low = u8(value)
|
||||
cpu.registers[reg.name].low = u8(value)
|
||||
case .High:
|
||||
CPU.registers[reg.name].high = u8(value)
|
||||
cpu.registers[reg.name].high = u8(value)
|
||||
case .Full:
|
||||
CPU.registers[reg.name].full = i16(value)
|
||||
cpu.registers[reg.name].full = i16(value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,19 +41,19 @@ get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue {
|
||||
return &cpu.registers[reg]
|
||||
}
|
||||
|
||||
check_zero_flag :: proc(value: i16) {
|
||||
CPU.flags[.ZF] = value == 0
|
||||
check_zero_flag :: proc(cpu: ^Cpu, value: i16) {
|
||||
cpu.flags[.ZF] = value == 0
|
||||
}
|
||||
|
||||
check_sign_flag :: proc(value: i16) {
|
||||
CPU.flags[.SF] = (value >> 15) & 0x1 == 1
|
||||
check_sign_flag :: proc(cpu: ^Cpu, 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_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(value: i16) {
|
||||
check_parity_flag :: proc(cpu: ^Cpu, value: i16) {
|
||||
val := value
|
||||
bit_count := 0
|
||||
val &= 0x00FF
|
||||
@ -61,49 +63,72 @@ check_parity_flag :: proc(value: i16) {
|
||||
}
|
||||
val >>= 1
|
||||
}
|
||||
CPU.flags[.PF] = val % 2 == 0
|
||||
cpu.flags[.PF] = bit_count % 2 == 0
|
||||
}
|
||||
|
||||
check_auxiliary_carry_flag :: proc(dst: i16, src: i16, is_add: bool) {
|
||||
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
|
||||
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)
|
||||
check_flags :: proc(cpu: ^Cpu, value: i16) {
|
||||
check_zero_flag(cpu, value)
|
||||
check_sign_flag(cpu, value)
|
||||
check_parity_flag(cpu, value)
|
||||
}
|
||||
|
||||
execute_instruction :: proc(inst: Instruction) {
|
||||
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(inst.src)
|
||||
set_register_value(reg, src_val)
|
||||
src_val := get_operand_value(cpu, inst.src)
|
||||
set_register_value(cpu, reg, src_val)
|
||||
case .ADD:
|
||||
src_val := get_operand_value(inst.src)
|
||||
dst_val := get_operand_value(inst.dst)
|
||||
val := i16(CPU.registers[reg.name].full) + src_val
|
||||
set_register_value(reg, val)
|
||||
check_flags(val)
|
||||
check_auxiliary_carry_flag(dst_val, src_val, true)
|
||||
check_carry_flag(dst_val, src_val, true)
|
||||
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(inst.src)
|
||||
dst_val := get_operand_value(inst.dst)
|
||||
val := i16(CPU.registers[reg.name].full) - src_val
|
||||
set_register_value(reg, val)
|
||||
check_flags(val)
|
||||
check_auxiliary_carry_flag(dst_val, src_val, false)
|
||||
check_carry_flag(dst_val, src_val, false)
|
||||
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(inst.src)
|
||||
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)
|
||||
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 jmp_offset,ok := inst.src.(Jump); ok {
|
||||
#partial switch inst.opname {
|
||||
case .JNZ, .JNE:
|
||||
if !cpu.flags[.ZF] {
|
||||
cpu.registers[.ip].full += i16(jmp_offset)
|
||||
if jmp_offset < 0 {
|
||||
cpu.registers[.ip].full -= i16(inst.bytes_read)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execute_instructions :: proc(cpu: ^Cpu, instructions: DecodedInstructions) {
|
||||
halt := false
|
||||
fmt.println(instructions.total_bytes_decoded)
|
||||
for !halt && get_ip(cpu) < instructions.total_bytes_decoded {
|
||||
inst,ok := instructions.inst_map[get_ip(cpu)]
|
||||
if !ok {
|
||||
// Something went wrong with a jump, most likely
|
||||
}
|
||||
cpu.registers[.ip].full += i16(inst.bytes_read)
|
||||
execute_instruction(cpu, inst)
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ Op :: enum {
|
||||
CALL,
|
||||
JMP,
|
||||
JNZ,
|
||||
JNE,
|
||||
JNGE,
|
||||
JE,
|
||||
JZ,
|
||||
@ -90,7 +91,6 @@ Op :: enum {
|
||||
JBE,
|
||||
JO,
|
||||
JS,
|
||||
JNE,
|
||||
JNL,
|
||||
JGE,
|
||||
JNLE,
|
||||
|
33
sim8086.odin
33
sim8086.odin
@ -7,10 +7,6 @@ import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
CPU := Cpu {
|
||||
memory = make([dynamic]u8, 65536),
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
f,err := os.open(os.args[1])
|
||||
if err != os.ERROR_NONE {
|
||||
@ -36,15 +32,26 @@ main :: proc() {
|
||||
instruction_list := make([dynamic]string, 0, 512)
|
||||
instructions_list := make([dynamic]Instruction, 0, 512)
|
||||
|
||||
decode_data(&instructions_list, data[:], bytes_read)
|
||||
|
||||
total_bytes := decode_data(&instructions_list, data[:], bytes_read)
|
||||
inst_map := make(map[int]Instruction)
|
||||
for inst in instructions_list {
|
||||
execute_instruction(inst)
|
||||
inst_map[inst.segment_offset] = inst
|
||||
}
|
||||
decoded_insts := DecodedInstructions {
|
||||
inst_list = instructions_list,
|
||||
inst_map = inst_map,
|
||||
total_bytes_decoded = total_bytes
|
||||
}
|
||||
|
||||
cpu := Cpu {
|
||||
memory = make([dynamic]u8, 65536),
|
||||
}
|
||||
|
||||
execute_instructions(&cpu, decoded_insts)
|
||||
|
||||
if what_to_print == "registers" || what_to_print == "all" {
|
||||
fmt.println("Registers")
|
||||
for reg_val,name in CPU.registers {
|
||||
for reg_val,name in cpu.registers {
|
||||
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",
|
||||
@ -52,7 +59,7 @@ main :: proc() {
|
||||
fmt.println()
|
||||
}
|
||||
fmt.println("\nFlags")
|
||||
for state,flag in CPU.flags {
|
||||
for state,flag in cpu.flags {
|
||||
fmt.printf("%c:%d ",reflect.enum_string(flag)[0], state ? 1 : 0)
|
||||
}
|
||||
fmt.println()
|
||||
@ -61,16 +68,16 @@ main :: proc() {
|
||||
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 {
|
||||
if cpu.registers[name].full != reg_val.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)
|
||||
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] {
|
||||
if 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)
|
||||
fmt.printfln(msg, f, ref_cpu.flags[f] ? 1 : 0, cpu.flags[f] ? 1 : 0)
|
||||
failed_ref = true
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
lines := strings.split(content, "\n")
|
||||
defer delete(lines)
|
||||
|
||||
ignore_ip_reg := true
|
||||
for line in lines {
|
||||
space_count := 0
|
||||
for c,i in line {
|
||||
@ -41,6 +42,9 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
}
|
||||
} else {
|
||||
reg_name := line[i:i+2]
|
||||
if reg_name == "ip" {
|
||||
ignore_ip_reg = false
|
||||
}
|
||||
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 {
|
||||
@ -52,5 +56,9 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if ignore_ip_reg {
|
||||
cpu.registers[.ip] = cpu.registers[.ip]
|
||||
}
|
||||
|
||||
return cpu, true
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ Register :: enum {
|
||||
cs,
|
||||
ss,
|
||||
ds,
|
||||
ip,
|
||||
}
|
||||
|
||||
RegisterValue :: struct #raw_union {
|
||||
@ -148,13 +149,19 @@ Instruction :: struct {
|
||||
has_segment: Maybe(RegisterId),
|
||||
has_lock: bool,
|
||||
bytes_read: int,
|
||||
segment_offset: int,
|
||||
raw_data: []u8,
|
||||
debug_msg: string,
|
||||
}
|
||||
|
||||
DecodedInstructions :: struct {
|
||||
inst_list: [dynamic]Instruction,
|
||||
inst_map: map[int]Instruction,
|
||||
total_bytes_decoded: int
|
||||
}
|
||||
|
||||
Cpu :: struct {
|
||||
flags: [Flag]bool,
|
||||
registers: [Register]RegisterValue,
|
||||
memory: [dynamic]u8,
|
||||
total_bytes_processed: int
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user