Generalize with perform_load and perform_store, use bit_set for cpu flags

This commit is contained in:
Joseph Ferano 2025-03-22 18:54:36 +07:00
parent 831a307975
commit df105ea3cf
4 changed files with 85 additions and 119 deletions

View File

@ -23,7 +23,7 @@ get_effective_address_value :: proc(cpu: ^Cpu, id: u8) -> i16 {
return -1
}
get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 {
perform_load :: proc(cpu: ^Cpu, operand: Operand, is_word: bool) -> i16 {
#partial switch opr in operand {
case Immediate:
return i16(opr.value)
@ -36,51 +36,38 @@ get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 {
return i16(reg_val.full)
}
case DirectAddress:
value := i16(u16(cpu.memory[opr+1] << 8) | u16(cpu.memory[opr]))
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
value := i16(u16(cpu.memory[idx+1] << 8) | u16(cpu.memory[idx]))
// fmt.println("Checking", idx)
// for i in 0..<6 {
// fmt.printf("%04x ", cpu.memory[int(idx)-3+i])
// }
// fmt.println()
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
}
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) {
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
}
check_parity_flag :: proc(cpu: ^Cpu, value: i16) {
val := value
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 {
@ -91,84 +78,32 @@ check_parity_flag :: proc(cpu: ^Cpu, value: i16) {
}
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)
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 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 addr,ok := inst.dst.(DirectAddress); ok {
#partial switch inst.opname {
case .MOV:
if imm,ok := inst.src.(Immediate); ok {
if imm.size == ._16 {
cpu.memory[addr] = u8(imm.value & 0x00FF)
cpu.memory[addr+1] = u8((u16(imm.value) & 0xFF00) >> 8)
} else {
cpu.memory[addr] = u8(imm.value)
}
}
}
} else if mem_addr,ok := inst.dst.(MemoryAddr); ok {
#partial switch inst.opname {
case .MOV:
value := get_operand_value(cpu, inst.src)
effective_addr_val := get_effective_address_value(cpu, mem_addr.addr_id)
addr := effective_addr_val + mem_addr.displacement
cpu.memory[addr] = u8(value & 0x00FF)
cpu.memory[addr+1] = u8((u16(value) & 0xFF00) >> 8)
// TODO: We need a way to detect if it's a byte or a word
// if imm,ok := inst.src.(Immediate); ok {
// // TODO: We need a function that returns the registers to check out
// effective_addr_val := get_effective_address_value(cpu, mem_addr.addr_id)
// addr := effective_addr_val + mem_addr.displacement_value
// if imm.size == .Signed16 {
// cpu.memory[addr] = u8(imm.value & 0x00FF)
// cpu.memory[addr+1] = u8((u16(imm.value) & 0xFF00) >> 8)
// } else {
// cpu.memory[addr] = u8(imm.value)
// }
// }
}
} else if jmp_offset,ok := inst.src.(Jump); ok {
if jmp_offset,ok := inst.src.(Jump); ok {
jump: bool
#partial switch inst.opname {
case .JNZ, .JNE: jump = !cpu.flags[.ZF]
@ -187,6 +122,30 @@ execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) {
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)
}
}

View File

@ -70,7 +70,7 @@ get_operand_string :: proc(operand: Operand, has_segment: Maybe(RegisterId)) ->
case RegisterId:
string_val = get_register_name(val)
case Immediate:
string_val = fmt.aprintf("%d", i16(val.value))
string_val = fmt.aprintf("%d", val.value)
case MemoryAddr:
string_val = get_memory_string(val, has_segment)
case DirectAddress:

View File

@ -7,6 +7,11 @@ import "core:strings"
import "core:strconv"
import "core:reflect"
get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue {
reg,_ := reflect.enum_from_name(Register, name)
return &cpu.registers[reg]
}
parse_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
cpu: Cpu

View File

@ -68,6 +68,7 @@ Immediate :: struct {
MemoryAddr :: struct {
addr_id: u8,
displacement: i16,
disp_size: ImmediateSize,
}
DirectAddress :: distinct i16
Jump :: distinct i8
@ -119,6 +120,7 @@ InstructionInfo :: struct {
desc: string,
src: OperandInfo,
dst: OperandInfo,
flags: bit_set[Flag],
word_size: WordSize,
reg_info: RegisterEncodingBits,
has_flip: bool,