performance-aware/execution.odin

135 lines
4.2 KiB
Odin

package sim_8086
import "core:os"
import "core:fmt"
import "core:math"
import "core:strings"
import "core:reflect"
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]
switch opr.access {
case .Low, .High:
return i16(opr.access == .Low ? reg_val.low : reg_val.high)
case .Full:
return i16(reg_val.full)
}
}
return 0
}
set_register_value :: proc(cpu: ^Cpu, 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 = i16(value)
}
}
get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue {
reg,_ := reflect.enum_from_name(Register, name)
return &cpu.registers[reg]
}
check_zero_flag :: proc(cpu: ^Cpu, value: i16) {
cpu.flags[.ZF] = value == 0
}
check_sign_flag :: proc(cpu: ^Cpu, value: i16) {
cpu.flags[.SF] = (value >> 15) & 0x1 == 1
}
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(cpu: ^Cpu, value: i16) {
val := value
bit_count := 0
val &= 0x00FF
for val != 0 {
if val & 0x1 == 1 {
bit_count += 1
}
val >>= 1
}
cpu.flags[.PF] = bit_count % 2 == 0
}
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
}
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(cpu: ^Cpu, inst: Instruction) {
if reg,ok := inst.dst.(RegisterId); ok {
#partial switch inst.opname {
case .MOV:
src_val := get_operand_value(cpu, inst.src)
set_register_value(cpu, reg, src_val)
case .ADD:
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(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(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)
}
}