Continued adding next instructions
This commit is contained in:
+91
-16
@@ -30,10 +30,19 @@ cycle :: proc(s: ^System) {
|
||||
case 0x0:
|
||||
switch opcode {
|
||||
case 0x00E0: op_cls(s)
|
||||
case 0x00EE: op_ret(s, opcode)
|
||||
}
|
||||
case 0x1: op_jp_add(s, opcode)
|
||||
case 0x2: op_call_add(s, opcode)
|
||||
case 0x3: op_skip_on_condition(s, opcode, true)
|
||||
case 0x4: op_skip_on_condition(s, opcode, false)
|
||||
case 0x5: op_skip_reg_condition(s, opcode, true)
|
||||
case 0x6: op_ld(s, opcode)
|
||||
case 0x7: op_add(s, opcode)
|
||||
|
||||
// todo 0x8 with last bit for ALU
|
||||
|
||||
case 0x9: op_skip_reg_condition(s, opcode, false)
|
||||
case 0xA: op_set(s, opcode)
|
||||
case 0xD: op_draw(s, opcode)
|
||||
}
|
||||
@@ -54,12 +63,68 @@ op_cls :: proc(s: ^System) {
|
||||
s.display = {}
|
||||
}
|
||||
|
||||
// RET: Return from a subroutine.
|
||||
op_ret :: proc(s: ^System, opcode: u16) {
|
||||
// decrement sp to the previous used slot
|
||||
// current sp will always be empty
|
||||
s.sp -= 1
|
||||
|
||||
// restore the pc from where we called from
|
||||
s.pc = s.stack[s.sp]
|
||||
}
|
||||
|
||||
// JP addr: Jump to location nnn
|
||||
op_jp_add :: proc(s: ^System, opcode: u16) {
|
||||
addr := (opcode & 0x0FFF)
|
||||
s.pc = addr
|
||||
}
|
||||
|
||||
// Call addr: Call subroutine at nnn.
|
||||
op_call_add :: proc(s: ^System, opcode: u16) {
|
||||
addr := (opcode & 0x0FFF)
|
||||
|
||||
// Save current pc to stack for return location
|
||||
s.stack[s.sp] = s.pc
|
||||
// advance sp for next free slot
|
||||
s.sp += 1
|
||||
|
||||
s.pc = addr
|
||||
}
|
||||
|
||||
// SE: Skip next instruction if Vx = kk.
|
||||
// SNE: Skip next instruction if Vx != kk.
|
||||
op_skip_on_condition :: proc(s: ^System, opcode: u16, condition: bool) {
|
||||
// Extract the register indices to get values
|
||||
vx_index := (opcode & 0x0F00) >> 8
|
||||
register_val := s.v[vx_index]
|
||||
|
||||
// Extract value to compare
|
||||
op_val := u8(opcode & 0x00FF)
|
||||
|
||||
if (register_val == op_val) == condition {
|
||||
s.pc += 2
|
||||
}
|
||||
}
|
||||
|
||||
// SE Vx, Vy: Skip next instruction if Vx = Vy
|
||||
// SNE Vx, Vy: Skip next instruction if Vx != Vy.
|
||||
op_skip_reg_condition :: proc(s: ^System, opcode: u16, condition: bool) {
|
||||
// Extract the register indices
|
||||
vx_index := (opcode & 0x0F00) >> 8
|
||||
vy_index := (opcode & 0x00F0) >> 4
|
||||
|
||||
// Read the actual value from the registers.
|
||||
rx := s.v[vx_index]
|
||||
ry := s.v[vy_index]
|
||||
|
||||
if (rx == ry) == condition {
|
||||
s.pc += 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// continue Bnnn
|
||||
|
||||
// LD: Load value kk into x register
|
||||
op_ld :: proc(s: ^System, opcode: u16) {
|
||||
register := (opcode & 0x0F00) >> 8
|
||||
@@ -83,34 +148,44 @@ op_set :: proc(s: ^System, opcode: u16) {
|
||||
|
||||
// DRAW: Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
|
||||
op_draw :: proc(s: ^System, opcode: u16) {
|
||||
x_register := (opcode & 0x0F00) >> 8
|
||||
y_register := (opcode & 0x00F0) >> 4
|
||||
// Extract the register indices that hold the sprite's X and Y screen position
|
||||
vx_index := (opcode & 0x0F00) >> 8
|
||||
vy_index := (opcode & 0x00F0) >> 4
|
||||
|
||||
// Handle wrapping
|
||||
x_coord := (s.v[x_register] & 0x03F)
|
||||
y_coord := (s.v[y_register] & 0x1F)
|
||||
// Read the actual coordinates from those registers.
|
||||
// Wrap to screen bounds: 64 wide (0x3F mask), 32 tall (0x1F mask)
|
||||
screen_x := s.v[vx_index] & 0x3F
|
||||
screen_y := s.v[vy_index] & 0x1F
|
||||
|
||||
// VF is the collision flag — clear it before drawing
|
||||
s.v[0xF] = 0
|
||||
|
||||
n_bytes := (opcode & 0x000F)
|
||||
// Number of rows in the sprite (1 byte per row, each byte = 8 pixels)
|
||||
sprite_height := opcode & 0x000F
|
||||
|
||||
for row in 0..<n_bytes {
|
||||
sprite_row := s.memory[s.i + row]
|
||||
// log.debugf("sr: 0x%X", sprite_row)
|
||||
for row in 0..<sprite_height {
|
||||
// Each byte in memory is one row of 8 pixels, MSB is the leftmost pixel
|
||||
sprite_row_bits := s.memory[s.i + row]
|
||||
|
||||
for col in 0..<8 {
|
||||
mask := u8(0x80) >> u8(col)
|
||||
// Shift a 1 from the MSB right to isolate the bit at this column
|
||||
bit_mask := u8(0x80) >> u8(col)
|
||||
|
||||
if sprite_row & mask != 0 {
|
||||
// this bit is set Xor the pixel
|
||||
y_pos := ((y_coord + u8(row)) % 32)
|
||||
x_pos := ((x_coord + u8(col)) % 64)
|
||||
// Only draw if this bit in the sprite is set
|
||||
if sprite_row_bits & bit_mask != 0 {
|
||||
// Wrap the draw position if the sprite goes off-screen
|
||||
pixel_x := (screen_x + u8(col)) % 64
|
||||
pixel_y := (screen_y + u8(row)) % 32
|
||||
|
||||
pixel := &s.display[y_pos][x_pos]
|
||||
pixel := &s.display[pixel_y][pixel_x]
|
||||
|
||||
// CHIP-8 XORs pixels — if we're about to unset a lit pixel, that's a collision
|
||||
if pixel^ == 1 {
|
||||
s.v[0xF] = 1
|
||||
}
|
||||
pixel^ = pixel^ ~ 1
|
||||
|
||||
// XOR the pixel: toggles it on if off, off if on
|
||||
pixel^ ~= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user