Added instructions for flag test rom.

This commit is contained in:
2026-05-26 07:50:34 +02:00
parent e604880b4e
commit 6a4ed63138
+127 -16
View File
@@ -1,6 +1,7 @@
package machine
import "core:log"
import "base:intrinsics"
// CPU.odin -> instructions, fetch, decode, execute, run loop
@@ -15,11 +16,11 @@ cycle :: proc(s: ^System) {
// Pre-decode common components of opcodes
first_nibble := (opcode & 0xF000) >> 12
vx_idx := (opcode & 0x0F00) >> 8
vy_idx := (opcode & 0x00F0) >> 4
last_nibble := (opcode & 0x000F)
kk := u8(opcode & 0x00FF)
nnn := (opcode & 0x0FFF)
vx_idx := (opcode & 0x0F00) >> 8 // A 4-bit value, the lower 4 bits of the high byte of the instruction
vy_idx := (opcode & 0x00F0) >> 4 // A 4-bit value, the upper 4 bits of the low byte of the instruction
last_nibble := (opcode & 0x000F) // A 4-bit value, the lowest 4 bits of the instruction
kk := u8(opcode & 0x00FF) // An 8-bit value, the lowest 8 bits of the instruction
nnn := (opcode & 0x0FFF) // A 12-bit value, the lowest 12 bits of the instruction
// Decode + Execute
switch first_nibble {
@@ -38,12 +39,27 @@ cycle :: proc(s: ^System) {
case 0x8:
switch last_nibble {
case 0x0: op_alu_ld(s, vx_idx, vy_idx)
// case 0x1: op_alu_or(s, vx_idx, vy_idx)
case 0x1: op_alu_or(s, vx_idx, vy_idx)
case 0x2: op_alu_and(s, vx_idx, vy_idx)
case 0x3: op_alu_xor(s, vx_idx, vy_idx)
case 0x4: op_alu_add(s, vx_idx, vy_idx)
case 0x5: op_alu_sub(s, vx_idx, vy_idx)
case 0x6: op_alu_shr(s, vx_idx, vy_idx)
case 0x7: op_alu_subn(s, vx_idx, vy_idx)
case 0xE: op_alu_shl(s, vx_idx, vy_idx)
}
case 0x9: op_skip_reg_condition(s, vx_idx, vy_idx, false)
case 0xA: op_set(s, nnn)
case 0xB: op_jp_offset(s, nnn)
case 0xD: op_draw(s, vx_idx, vy_idx, last_nibble)
case 0xF: {
switch kk {
case 0x1E: op_add_i(s, vx_idx)
case 0x33: op_split_i(s, vx_idx)
case 0x55: op_mem_set(s, vx_idx)
case 0x65: op_mem_get(s, vx_idx)
}
}
}
}
@@ -97,13 +113,6 @@ op_skip_reg_condition :: proc(s: ^System, vx_idx, vy_idx: u16, condition: bool)
}
}
// Bnnn - JP V0, addr
// @TODO: add configurable quirk for chip-48
op_jp_offset :: proc(s: ^System, nnn: u16) {
s.pc = nnn
s.pc += u16(s.v[0])
}
// 6xkk - LD Vx, byte
op_ld :: proc(s: ^System, vx_idx: u16, kk: u8) {
s.v[vx_idx] = kk
@@ -119,11 +128,74 @@ op_alu_ld :: proc(s: ^System, vx_idx, vy_idx: u16) {
s.v[vx_idx] = s.v[vy_idx]
}
// 8xy1 - OR Vx, Vy
op_alu_or :: proc(s: ^System, vx_idx, vy_idx: u16) {
s.v[vx_idx] = (s.v[vx_idx] | s.v[vy_idx])
}
// 8xy2 - AND Vx, Vy
op_alu_and :: proc(s: ^System, vx_idx, vy_idx: u16) {
s.v[vx_idx] = (s.v[vx_idx] & s.v[vy_idx])
}
// 8xy3 - XOR Vx, Vy
op_alu_xor :: proc(s: ^System, vx_idx, vy_idx: u16) {
s.v[vx_idx] = (s.v[vx_idx] ~ s.v[vy_idx])
}
// 8xy4 - ADD Vx, Vy
op_alu_add :: proc(s: ^System, vx_idx, vy_idx: u16) {
// Cool feature of odin, intrinsics to check for overflows
result, overflowed := intrinsics.overflow_add(s.v[vx_idx], s.v[vy_idx])
s.v[vx_idx] = result
s.v[0xF] = u8(1) if overflowed else u8(0)
}
// 8xy5 - SUB Vx, Vy
op_alu_sub :: proc(s: ^System, vx_idx, vy_idx: u16) {
no_borrow := s.v[vx_idx] >= s.v[vy_idx]
s.v[vx_idx] = (s.v[vx_idx] - s.v[vy_idx])
s.v[0xF] = u8(1) if no_borrow else u8(0)
}
// 8xy6 - SHR Vx, Vy
op_alu_shr :: proc(s: ^System, vx_idx, vy_idx: u16) {
lsb := s.v[vx_idx] & 0x1
// divide by 2
s.v[vx_idx] = (s.v[vx_idx] >> 1)
// if last bit is 1 set F register
s.v[0xF] = lsb
}
// 8xy7 - SUBN Vx, Vy
op_alu_subn :: proc(s: ^System, vx_idx, vy_idx: u16) {
no_borrow := s.v[vy_idx] >= s.v[vx_idx]
s.v[vx_idx] = (s.v[vy_idx] - s.v[vx_idx])
s.v[0xF] = u8(1) if no_borrow else u8(0)
}
// 8xyE - SHL Vx, Vy
op_alu_shl :: proc(s: ^System, vx_idx, vy_idx: u16) {
msb := (s.v[vx_idx] & 0x80) >> 7
// multiply by 2
s.v[vx_idx] = (s.v[vx_idx] << 1)
s.v[0xF] = msb
}
// Annn - LD I, addr
op_set :: proc(s: ^System, nnn: u16) {
s.i = nnn
}
// Bnnn - JP V0, addr
// @TODO: add configurable quirk for chip-48
op_jp_offset :: proc(s: ^System, nnn: u16) {
s.pc = nnn
s.pc += u16(s.v[0])
}
// Cxkk - RND Vx, byte : @TODO!
// Dxyn - DRW Vx, Vy, nibble
op_draw :: proc(s: ^System, vx_idx, vy_idx, n: u16) {
// Read positions and apply bounds masks (64 wide, 32 tall)
@@ -149,14 +221,53 @@ op_draw :: proc(s: ^System, vx_idx, vy_idx, n: u16) {
pixel := &s.display[pixel_y][pixel_x]
// Toggle pixel state via XOR
pixel^ ~= 1
// If target pixel is already lit, we have a collision (XOR will turn it off)
if pixel^ == 1 {
s.v[0xF] = 1
}
// Toggle pixel state via XOR
pixel^ ~= 1
}
}
}
}
// Ex9E - SKP Vx : @TODO
// ExA1 - SKNP Vx : @TODO
// Fx07 - LD Vx, DT : @TODO
// Fx0A - LD Vx, K : @TODO
// Fx15 - LD DT, Vx : @TODO
// Fx18 - LD ST, Vx : @TODO
// Fx1E - ADD I, Vx
op_add_i :: proc(s: ^System, vx_idx: u16) {
s.i = s.i + u16(s.v[vx_idx])
s.v[0xF] = u8(1) if s.i > 0xFFF else u8(0)
}
// Fx29 - LD F, Vx : @TODO
// Fx33 - LD B, Vx
op_split_i :: proc(s: ^System, vx_idx: u16) {
xreg_val := s.v[vx_idx]
s.memory[s.i] = (xreg_val / 100)
s.memory[s.i + 1] = ((xreg_val / 10) % 10)
s.memory[s.i + 2] = (xreg_val % 10)
}
// Fx55/Fx65: I is used as a base address only, not modified during store/load.
// Modern behavior (CHIP48+): I unchanged after op. Original COSMAC VIP: I += X + 1.
// Fx55 - LD [I], Vx
op_mem_set :: proc(s: ^System, vx_idx: u16) {
for loop_idx in 0..=vx_idx {
s.memory[s.i + loop_idx] = s.v[loop_idx]
}
}
// Fx65 - LD Vx, [I]
op_mem_get :: proc(s: ^System, vx_idx: u16) {
for loop_idx in 0..=vx_idx {
s.v[loop_idx] = s.memory[s.i + loop_idx]
}
}