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
|
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{}
|
operand: Operand = None{}
|
||||||
switch opinfo {
|
switch opinfo {
|
||||||
case .None:
|
case .None:
|
||||||
@ -154,7 +156,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
operand = Repeat(fmt.aprintf("%s%s", rep, w))
|
operand = Repeat(fmt.aprintf("%s%s", rep, w))
|
||||||
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:])) + total_bytes_processed + 3
|
||||||
operand = Immediate { value = i16(value), size = .Signed16 }
|
operand = Immediate { value = i16(value), size = .Signed16 }
|
||||||
processed^ += 2
|
processed^ += 2
|
||||||
case .Intersegment:
|
case .Intersegment:
|
||||||
@ -167,10 +169,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
return operand
|
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
|
idx := 0
|
||||||
has_segment: Maybe(RegisterId)
|
has_segment: Maybe(RegisterId)
|
||||||
has_lock: bool
|
has_lock: bool
|
||||||
|
total_bytes_processed: int
|
||||||
for idx < bytes_to_read {
|
for idx < bytes_to_read {
|
||||||
instruction: Instruction
|
instruction: Instruction
|
||||||
processed := 1
|
processed := 1
|
||||||
@ -234,8 +237,8 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
|||||||
case .Always16: word = true
|
case .Always16: word = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dst_opr = parse_operand(inst, inst.dst, 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, word, has_segment)
|
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, total_bytes_processed, word, has_segment)
|
||||||
|
|
||||||
if flip {
|
if flip {
|
||||||
src_opr, dst_opr = dst_opr, src_opr
|
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.info = inst
|
||||||
instruction.has_lock = has_lock
|
instruction.has_lock = has_lock
|
||||||
instruction.has_segment = has_segment
|
instruction.has_segment = has_segment
|
||||||
|
instruction.segment_offset += total_bytes_processed
|
||||||
instruction.opname = inst.opname
|
instruction.opname = inst.opname
|
||||||
instruction.opname,instruction.indirect_intersegment = get_op(instruction)
|
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_lock = false
|
||||||
has_segment = nil
|
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:strings"
|
||||||
import "core:reflect"
|
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 {
|
#partial switch opr in operand {
|
||||||
case Immediate:
|
case Immediate:
|
||||||
// fmt.printfln("0x%4x %d", i16(opr.value), opr.value)
|
// fmt.printfln("0x%4x %d", i16(opr.value), opr.value)
|
||||||
return i16(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 i16(opr.access == .Low ? reg_val.low : reg_val.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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
set_register_value :: proc(reg: RegisterId, value: i16) {
|
set_register_value :: proc(cpu: ^Cpu, 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 = 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]
|
return &cpu.registers[reg]
|
||||||
}
|
}
|
||||||
|
|
||||||
check_zero_flag :: proc(value: i16) {
|
check_zero_flag :: proc(cpu: ^Cpu, value: i16) {
|
||||||
CPU.flags[.ZF] = value == 0
|
cpu.flags[.ZF] = value == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
check_sign_flag :: proc(value: i16) {
|
check_sign_flag :: proc(cpu: ^Cpu, value: i16) {
|
||||||
CPU.flags[.SF] = (value >> 15) & 0x1 == 1
|
cpu.flags[.SF] = (value >> 15) & 0x1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
check_carry_flag :: proc(dst: i16, src: i16, is_add: bool) {
|
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
|
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
|
val := value
|
||||||
bit_count := 0
|
bit_count := 0
|
||||||
val &= 0x00FF
|
val &= 0x00FF
|
||||||
@ -61,49 +63,72 @@ check_parity_flag :: proc(value: i16) {
|
|||||||
}
|
}
|
||||||
val >>= 1
|
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
|
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_flags :: proc(cpu: ^Cpu, value: i16) {
|
||||||
check_zero_flag(value)
|
check_zero_flag(cpu, value)
|
||||||
check_sign_flag(value)
|
check_sign_flag(cpu, value)
|
||||||
check_parity_flag(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 {
|
if reg,ok := inst.dst.(RegisterId); ok {
|
||||||
#partial switch inst.opname {
|
#partial switch inst.opname {
|
||||||
case .MOV:
|
case .MOV:
|
||||||
src_val := get_operand_value(inst.src)
|
src_val := get_operand_value(cpu, inst.src)
|
||||||
set_register_value(reg, src_val)
|
set_register_value(cpu, reg, src_val)
|
||||||
case .ADD:
|
case .ADD:
|
||||||
src_val := get_operand_value(inst.src)
|
src_val := get_operand_value(cpu, inst.src)
|
||||||
dst_val := get_operand_value(inst.dst)
|
dst_val := get_operand_value(cpu, inst.dst)
|
||||||
val := i16(CPU.registers[reg.name].full) + src_val
|
val := i16(cpu.registers[reg.name].full) + src_val
|
||||||
set_register_value(reg, val)
|
set_register_value(cpu, reg, val)
|
||||||
check_flags(val)
|
check_flags(cpu, val)
|
||||||
check_auxiliary_carry_flag(dst_val, src_val, true)
|
check_auxiliary_carry_flag(cpu, dst_val, src_val, true)
|
||||||
check_carry_flag(dst_val, src_val, true)
|
check_carry_flag(cpu, dst_val, src_val, true)
|
||||||
case .SUB:
|
case .SUB:
|
||||||
src_val := get_operand_value(inst.src)
|
src_val := get_operand_value(cpu, inst.src)
|
||||||
dst_val := get_operand_value(inst.dst)
|
dst_val := get_operand_value(cpu, inst.dst)
|
||||||
val := i16(CPU.registers[reg.name].full) - src_val
|
val := i16(cpu.registers[reg.name].full) - src_val
|
||||||
set_register_value(reg, val)
|
set_register_value(cpu, reg, val)
|
||||||
check_flags(val)
|
check_flags(cpu, val)
|
||||||
check_auxiliary_carry_flag(dst_val, src_val, false)
|
check_auxiliary_carry_flag(cpu, dst_val, src_val, false)
|
||||||
check_carry_flag(dst_val, src_val, false)
|
check_carry_flag(cpu, dst_val, src_val, false)
|
||||||
case .CMP:
|
case .CMP:
|
||||||
src_val := get_operand_value(inst.src)
|
src_val := get_operand_value(cpu, inst.src)
|
||||||
dst_val := get_operand_value(inst.dst)
|
dst_val := get_operand_value(cpu, inst.dst)
|
||||||
val := i16(CPU.registers[reg.name].full) - src_val
|
val := i16(cpu.registers[reg.name].full) - src_val
|
||||||
check_flags(val)
|
check_flags(cpu, val)
|
||||||
check_auxiliary_carry_flag(dst_val, src_val, false)
|
check_auxiliary_carry_flag(cpu, dst_val, src_val, false)
|
||||||
check_carry_flag(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,
|
CALL,
|
||||||
JMP,
|
JMP,
|
||||||
JNZ,
|
JNZ,
|
||||||
|
JNE,
|
||||||
JNGE,
|
JNGE,
|
||||||
JE,
|
JE,
|
||||||
JZ,
|
JZ,
|
||||||
@ -90,7 +91,6 @@ Op :: enum {
|
|||||||
JBE,
|
JBE,
|
||||||
JO,
|
JO,
|
||||||
JS,
|
JS,
|
||||||
JNE,
|
|
||||||
JNL,
|
JNL,
|
||||||
JGE,
|
JGE,
|
||||||
JNLE,
|
JNLE,
|
||||||
|
33
sim8086.odin
33
sim8086.odin
@ -7,10 +7,6 @@ import "core:math"
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
|
|
||||||
CPU := Cpu {
|
|
||||||
memory = make([dynamic]u8, 65536),
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
@ -36,15 +32,26 @@ main :: proc() {
|
|||||||
instruction_list := make([dynamic]string, 0, 512)
|
instruction_list := make([dynamic]string, 0, 512)
|
||||||
instructions_list := make([dynamic]Instruction, 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 {
|
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" {
|
if what_to_print == "registers" || what_to_print == "all" {
|
||||||
fmt.println("Registers")
|
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))
|
full := fmt.aprintf("%s: %d ", name, i16(reg_val.full))
|
||||||
hex := fmt.aprintf("0x%04x ", u16(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",
|
||||||
@ -52,7 +59,7 @@ main :: proc() {
|
|||||||
fmt.println()
|
fmt.println()
|
||||||
}
|
}
|
||||||
fmt.println("\nFlags")
|
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.printf("%c:%d ",reflect.enum_string(flag)[0], state ? 1 : 0)
|
||||||
}
|
}
|
||||||
fmt.println()
|
fmt.println()
|
||||||
@ -61,16 +68,16 @@ main :: proc() {
|
|||||||
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 0x%04x | Actual 0x%04x"
|
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
|
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 %d | Actual %d"
|
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
|
failed_ref = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
|||||||
lines := strings.split(content, "\n")
|
lines := strings.split(content, "\n")
|
||||||
defer delete(lines)
|
defer delete(lines)
|
||||||
|
|
||||||
|
ignore_ip_reg := true
|
||||||
for line in lines {
|
for line in lines {
|
||||||
space_count := 0
|
space_count := 0
|
||||||
for c,i in line {
|
for c,i in line {
|
||||||
@ -41,6 +42,9 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reg_name := line[i:i+2]
|
reg_name := line[i:i+2]
|
||||||
|
if reg_name == "ip" {
|
||||||
|
ignore_ip_reg = false
|
||||||
|
}
|
||||||
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 {
|
||||||
@ -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
|
return cpu, true
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ Register :: enum {
|
|||||||
cs,
|
cs,
|
||||||
ss,
|
ss,
|
||||||
ds,
|
ds,
|
||||||
|
ip,
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterValue :: struct #raw_union {
|
RegisterValue :: struct #raw_union {
|
||||||
@ -148,13 +149,19 @@ Instruction :: struct {
|
|||||||
has_segment: Maybe(RegisterId),
|
has_segment: Maybe(RegisterId),
|
||||||
has_lock: bool,
|
has_lock: bool,
|
||||||
bytes_read: int,
|
bytes_read: int,
|
||||||
|
segment_offset: int,
|
||||||
raw_data: []u8,
|
raw_data: []u8,
|
||||||
debug_msg: string,
|
debug_msg: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DecodedInstructions :: struct {
|
||||||
|
inst_list: [dynamic]Instruction,
|
||||||
|
inst_map: map[int]Instruction,
|
||||||
|
total_bytes_decoded: int
|
||||||
|
}
|
||||||
|
|
||||||
Cpu :: struct {
|
Cpu :: struct {
|
||||||
flags: [Flag]bool,
|
flags: [Flag]bool,
|
||||||
registers: [Register]RegisterValue,
|
registers: [Register]RegisterValue,
|
||||||
memory: [dynamic]u8,
|
memory: [dynamic]u8,
|
||||||
total_bytes_processed: int
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user