performance-aware/execution.odin

166 lines
5.6 KiB
Odin

package sim_8086
import "core:os"
import "core:fmt"
import "core:math"
import "core:strings"
import "core:reflect"
import "core:strconv"
get_ip :: proc(cpu: ^Cpu) -> int { return int(cpu.registers[.ip].full) }
get_effective_address_value :: proc(cpu: ^Cpu, id: u8) -> i16 {
switch id {
case 0: return cpu.registers[.bx].full + cpu.registers[.si].full
case 1: return cpu.registers[.bx].full + cpu.registers[.di].full
case 2: return cpu.registers[.bp].full + cpu.registers[.si].full
case 3: return cpu.registers[.bx].full + cpu.registers[.di].full
case 4: return cpu.registers[.si].full
case 5: return cpu.registers[.di].full
case 6: return cpu.registers[.bp].full
case 7: return cpu.registers[.bx].full
}
return -1
}
perform_load :: proc(cpu: ^Cpu, operand: Operand, is_word: bool) -> i16 {
#partial switch opr in operand {
case Immediate:
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)
}
case DirectAddress:
value: i16
if is_word {
value = i16(u16(cpu.memory[opr+1] << 8) | u16(cpu.memory[opr]))
} else {
value = i16(cpu.memory[opr])
}
return value
case MemoryAddr:
value: i16
idx := get_effective_address_value(cpu, opr.addr_id) + opr.displacement
if is_word {
value = i16(u16(cpu.memory[idx+1] << 8) | u16(cpu.memory[idx]))
} else {
value = i16(cpu.memory[idx])
}
return value
}
return 0
}
check_flags :: proc(cpu: ^Cpu, flags: bit_set[Flag], dst: i16, src: i16, result: i16, is_add: bool) {
if .ZF in flags do cpu.flags[.ZF] = result == 0
if .SF in flags do cpu.flags[.SF] = (result >> 15) & 0x1 == 1
if .CF in flags {
cpu.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst
}
if .AF in flags {
lhs, rhs := dst & 0xF, src & 0xF
cpu.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs
}
if .PF in flags {
val := result
bit_count := 0
val &= 0x00FF
for val != 0 {
if val & 0x1 == 1 {
bit_count += 1
}
val >>= 1
}
cpu.flags[.PF] = bit_count % 2 == 0
}
}
perform_store :: proc(cpu: ^Cpu, value: i16, operand: Operand, is_word: bool) {
#partial switch opr in operand {
case RegisterId:
switch opr.access {
case .Low:
cpu.registers[opr.name].low = u8(value)
case .High:
cpu.registers[opr.name].high = u8(value)
case .Full:
cpu.registers[opr.name].full = i16(value)
}
case DirectAddress:
cpu.memory[opr] = u8(value)
if is_word do cpu.memory[opr+1] = u8((u16(value) & 0xFF00) >> 8)
case MemoryAddr:
effective_addr_val := get_effective_address_value(cpu, opr.addr_id)
addr := effective_addr_val + opr.displacement
cpu.memory[addr] = u8(value)
if is_word do cpu.memory[addr+1] = u8((u16(value) & 0xFF00) >> 8)
}
}
execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) {
if jmp_offset,ok := inst.src.(Jump); ok {
jump: bool
#partial switch inst.opname {
case .JNZ, .JNE: jump = !cpu.flags[.ZF]
case .JE, .JZ: jump = cpu.flags[.ZF]
case .JP, .JPE: jump = cpu.flags[.PF]
case .JB, .JNAE: jump = cpu.flags[.CF]
case .LOOP:
cpu.registers[.cx].full -= 1
jump = cpu.registers[.cx].full != 0
case .LOOPNZ, .LOOPNE:
cpu.registers[.cx].full -= 1
// According to this resource, the loopnz inst does not change any flags
// https://yassinebridi.github.io/asm-docs/8086_instruction_set.html#LOOPNZ
// check_zero_flag(cpu, cpu.registers[.cx].full)
jump = cpu.registers[.cx].full != 0 && !cpu.flags[.ZF]
}
if jump {
cpu.registers[.ip].full += i16(jmp_offset)
cpu.registers[.ip].full -= i16(inst.bytes_read)
}
return
}
status_flags := bit_set[Flag]{.OF, .SF, .ZF, .AF, .PF, .CF}
#partial switch inst.opname {
case .MOV:
src_val := perform_load(cpu, inst.src, inst.is_word)
perform_store(cpu, src_val, inst.dst, inst.is_word)
case .ADD:
src_val := perform_load(cpu, inst.src, inst.is_word)
dst_val := perform_load(cpu, inst.dst, inst.is_word)
val := dst_val + src_val
perform_store(cpu, val, inst.dst, inst.is_word)
check_flags(cpu, status_flags, dst_val, src_val, val, true)
case .SUB:
src_val := perform_load(cpu, inst.src, inst.is_word)
dst_val := perform_load(cpu, inst.dst, inst.is_word)
val := dst_val - src_val
perform_store(cpu, val, inst.dst, inst.is_word)
check_flags(cpu, status_flags, dst_val, src_val, val, false)
case .CMP:
src_val := perform_load(cpu, inst.src, inst.is_word)
dst_val := perform_load(cpu, inst.dst, inst.is_word)
val := dst_val - src_val
check_flags(cpu, status_flags, dst_val, src_val, val, false)
}
}
execute_instructions :: proc(cpu: ^Cpu, instructions: DecodedInstructions) {
halt := false
for !halt && get_ip(cpu) < instructions.total_bytes_decoded {
inst,ok := instructions.inst_map[get_ip(cpu)]
if !ok {
panic("Something went wrong with a jump, most likely")
}
cpu.registers[.ip].full += i16(inst.bytes_read)
execute_instruction(cpu, inst)
}
}