Compare commits

...
2 Commits
Author SHA1 Message Date
JosephFerano 4ca694f77d Fix keypad and drawing bugs, add drawing refresh wait 2026-10-05 10:35:47 +07:00
JosephFerano de58014d0f Fullscreen mode 2026-10-04 17:07:50 +07:00
+38 -30
View File
@@ -29,6 +29,7 @@ class State
audio-stream: rl/AudioStream audio-stream: rl/AudioStream
font: rl/Font font: rl/Font
page: int page: int
fullscreen: bool
step-mode: bool step-mode: bool
clear-screen: bool clear-screen: bool
pending-key: u8? pending-key: u8?
@@ -122,11 +123,13 @@ fn parse-instruction(pc: u16) -> Instruction
fn reset() -> () fn reset() -> ()
cpu = Cpu{} cpu = Cpu{}
machine = Machine{} machine = Machine{}
;; load-rom("./IBM Logo.ch8") load-rom("./IBM Logo.ch8")
;; load-rom("./vendor/chip8-test-suite/bin/1-chip8-logo.ch8") ;; load-rom("./vendor/chip8-test-suite/bin/1-chip8-logo.ch8")
;; load-rom("./vendor/chip8-test-suite/bin/2-ibm-logo.ch8") ;; load-rom("./vendor/chip8-test-suite/bin/2-ibm-logo.ch8")
;; load-rom("./vendor/chip8-test-suite/bin/3-corax+.ch8") ;; load-rom("./vendor/chip8-test-suite/bin/3-corax+.ch8")
load-rom("./vendor/chip8-test-suite/bin/4-flags.ch8") ;; load-rom("./vendor/chip8-test-suite/bin/4-flags.ch8")
;; load-rom("./roms/Breakout [Carmelo Cortez, 1979].ch8")
load-rom("./roms/Brix [Andreas Gustafsson, 1990].ch8")
cpu.pc = 0x200 cpu.pc = 0x200
fn decode-instruction(inst: Instruction) -> str fn decode-instruction(inst: Instruction) -> str
@@ -182,18 +185,15 @@ fn decode-instruction(inst: Instruction) -> str
;; You should extract the execution from the other stuff here so you can manually ;; You should extract the execution from the other stuff here so you can manually
;; run an instruction when needed, all it needs is a parsed instruction ;; run an instruction when needed, all it needs is a parsed instruction
fn step() -> () fn step(inst: Instruction) -> ()
if cpu.pc >= length(machine.memory) - 2 if cpu.pc >= length(machine.memory) - 2
cpu.pc = 0 cpu.pc = 0
return return
let inst = parse-instruction(cpu.pc)
cpu.pc += 2 cpu.pc += 2
match inst.op match inst.op
0x0 -> 0x0 ->
match inst.nn match inst.nn
;; TODO: Maybe this can be a draw command that clears the screen rather than special cased
0xE0 -> state.clear-screen = true 0xE0 -> state.clear-screen = true
0xEE -> 0xEE ->
cpu.sp -= 1 cpu.sp -= 1
@@ -253,12 +253,12 @@ fn step() -> ()
loop bit in reverse(0..8) loop bit in reverse(0..8)
if byte >> bit & 0x1 == 1 and idx < length(machine.vbuffer) if byte >> bit & 0x1 == 1 and idx < length(machine.vbuffer)
if machine.vbuffer[idx] then collision = true if machine.vbuffer[idx] then collision = true
machine.vbuffer[idx] = true machine.vbuffer[idx] = not machine.vbuffer[idx]
idx += 1 idx += 1
cpu.v[0xF] = if collision then 1 else 0 cpu.v[0xF] = if collision then 1 else 0
0xE -> 0xE ->
let pressed = machine.keypad[inst.x] let pressed = machine.keypad[cpu.v[inst.x]]
match inst.nn match inst.nn
0x9E -> if pressed then cpu.pc += 2 0x9E -> if pressed then cpu.pc += 2
0xA1 -> if not pressed then cpu.pc += 2 0xA1 -> if not pressed then cpu.pc += 2
@@ -295,21 +295,21 @@ fn step() -> ()
fn run-update() -> () fn run-update() -> ()
loop i, key in keypad-keymap loop i, key in keypad-keymap
if rl/is-key-down(key) and not state.step-mode machine.keypad[keypad-mapping[i]] = rl/is-key-down(key)
machine.keypad[keypad-mapping[i]] = true if state.pending-key as reg
if state.pending-key as reg cpu.v[reg] = u8(i)
cpu.v[reg] = u8(i) state.pending-key = nil
state.pending-key = nil
if rl/is-key-pressed(:key-minus) if rl/is-key-pressed(:key-minus)
state.page = max(0, state.page - 1) state.page = max(0, state.page - 1)
if rl/is-key-pressed(:key-equal) if rl/is-key-pressed(:key-equal)
state.page = min(5, state.page + 1) state.page = min(5, state.page + 1)
if rl/is-key-pressed(:key-r) if rl/is-key-pressed(:key-period)
reset() reset()
return return
if rl/is-key-pressed(:key-enter)
state.fullscreen = not state.fullscreen
if rl/is-key-pressed(:key-space) if rl/is-key-pressed(:key-space)
state.step-mode = not state.step-mode state.step-mode = not state.step-mode
@@ -325,23 +325,28 @@ fn run-update() -> ()
return return
if state.step-mode if state.step-mode
if rl/is-key-pressed(:key-n) if rl/is-key-pressed(:key-n) or rl/is-key-pressed-repeat(:key-n)
step() let inst = parse-instruction(cpu.pc)
if rl/is-key-pressed-repeat(:key-n) step(inst)
step()
else else
cycles += f64(rl/get-frame-time()) * ips cycles += f64(rl/get-frame-time()) * ips
loop cycles >= 1.0 loop cycles >= 1.0
step() let inst = parse-instruction(cpu.pc)
step(inst)
;; Apparently on the COSMAC VIP and other devices, drawing would wait for the next scree
;; refresh which ran at 60hz
if inst.op == 0xD
cycles = 0.0
break
cycles -= 1.0 cycles -= 1.0
fill(state.screen-pixels, rl/black)
if state.clear-screen if state.clear-screen
state.clear-screen = false state.clear-screen = false
fill(state.screen-pixels, rl/black)
else else
loop i,pixel in machine.vbuffer loop i,pixel in machine.vbuffer
state.screen-pixels[i] = if pixel then rl/white else rl/black if pixel
state.screen-pixels[i] = rl/white
;; loop i,pixel in machine.vbuffer ;; loop i,pixel in machine.vbuffer
;; if i % 2 == 0 and i / pixels-width % 2 == 0 or i % 2 == 1 and i / pixels-width % 2 == 1 ;; if i % 2 == 0 and i / pixels-width % 2 == 0 or i % 2 == 1 and i / pixels-width % 2 == 1
@@ -357,6 +362,16 @@ fn draw-text-normal(text: str, x: $t, y: $t, color: rl/Color) -> () where is-num
fn run-drawing() -> () fn run-drawing() -> ()
rl/clear-background(rl/black) rl/clear-background(rl/black)
;; Texture
let scale: f32 = if state.fullscreen then 14 else 5
src = rl/Rectangle(0, 0, f32(pixels-width), f32(pixels-height))
dst = rl/Rectangle(10, 10, f32(pixels-width) * scale, f32(pixels-height) * scale)
rl/draw-texture-pro(state.screen-texture, src, dst, rl/Vector2(0.0, 0.0), 0.0, rl/white)
rl/draw-rectangle-lines(i32(dst.x), i32(dst.y), i32(dst.width), i32(dst.height), rl/white)
if state.fullscreen
return
draw-text-normal("Keypad", 10, 200, rl/white) draw-text-normal("Keypad", 10, 200, rl/white)
let key-size = 25 let key-size = 25
loop i, key in keypad-keymap loop i, key in keypad-keymap
@@ -394,13 +409,6 @@ fn run-drawing() -> ()
let reg-val = format("V{:X}=0x{:02X}", i, cpu.v[i]) let reg-val = format("V{:X}=0x{:02X}", i, cpu.v[i])
draw-text-normal(reg-val, reg-x, 200 + 21 * i, rl/white) draw-text-normal(reg-val, reg-x, 200 + 21 * i, rl/white)
;; Texture
let scale: f32 = 5.5
src = rl/Rectangle(0, 0, f32(pixels-width), f32(pixels-height))
dst = rl/Rectangle(10, 10, f32(pixels-width) * scale, f32(pixels-height) * scale)
rl/draw-texture-pro(state.screen-texture, src, dst, rl/Vector2(0.0, 0.0), 0.0, rl/white)
rl/draw-rectangle-lines(i32(dst.x), i32(dst.y), i32(dst.width), i32(dst.height), rl/white)
;; Stack ;; Stack
loop i in 0..16 loop i in 0..16
let row = i % 8 let row = i % 8