Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 11ef572634 | |||
| 380dffbf83 | |||
| b5d9811209 |
@@ -5,6 +5,7 @@ import "core:math/rand"
|
|||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// CPU.odin -> instructions, fetch, decode, execute, run loop
|
// CPU.odin -> instructions, fetch, decode, execute, run loop
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
cycle :: proc(s: ^System) {
|
cycle :: proc(s: ^System) {
|
||||||
// Fetch
|
// Fetch
|
||||||
@@ -273,20 +274,20 @@ op_ld_dt :: proc(s: ^System, vx_idx: u16) {
|
|||||||
|
|
||||||
// Fx0A - LD Vx, K
|
// Fx0A - LD Vx, K
|
||||||
op_get_key :: proc(s: ^System, vx_idx: u16) {
|
op_get_key :: proc(s: ^System, vx_idx: u16) {
|
||||||
for val, kp_idx in s.keypad {
|
if s.current_key != -1 {
|
||||||
if val == true {
|
key := s.current_key
|
||||||
s.v[vx_idx] = u8(kp_idx)
|
|
||||||
|
|
||||||
// return here so it does not decrement
|
released := check_if_key_released(key)
|
||||||
|
if released {
|
||||||
|
s.v[vx_idx] = u8(key)
|
||||||
|
s.current_key = -1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if loop does not return decrement go into loop
|
|
||||||
s.pc -= 2
|
s.pc -= 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Fx15 - LD DT, Vx
|
// Fx15 - LD DT, Vx
|
||||||
op_set_dt :: proc(s: ^System, vx_idx: u16) {
|
op_set_dt :: proc(s: ^System, vx_idx: u16) {
|
||||||
// set delay timer to value of x register
|
// set delay timer to value of x register
|
||||||
@@ -333,3 +334,4 @@ op_mem_get :: proc(s: ^System, vx_idx: u16) {
|
|||||||
s.v[loop_idx] = s.memory[s.i + loop_idx]
|
s.v[loop_idx] = s.memory[s.i + loop_idx]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ run_system :: proc(s: ^System) {
|
|||||||
// Do a cpu cycle
|
// Do a cpu cycle
|
||||||
// Cap at 60hz/60fps
|
// Cap at 60hz/60fps
|
||||||
for _ in 0..<CYCLES_PER_FRAME {
|
for _ in 0..<CYCLES_PER_FRAME {
|
||||||
|
handle_input(s)
|
||||||
cycle(s)
|
cycle(s)
|
||||||
}
|
}
|
||||||
|
if s.delay_timer > 0 { s.delay_timer -= 1 }
|
||||||
|
if s.sound_timer > 0 { s.sound_timer -= 1 }
|
||||||
|
|
||||||
render_display_buffer(&s.display)
|
render_display_buffer(&s.display)
|
||||||
|
|
||||||
@@ -34,6 +37,7 @@ run_system :: proc(s: ^System) {
|
|||||||
rl.CloseWindow()
|
rl.CloseWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// update display with display buffer bits
|
// update display with display buffer bits
|
||||||
render_display_buffer :: proc(display_buffer: ^[32][64]u8) {
|
render_display_buffer :: proc(display_buffer: ^[32][64]u8) {
|
||||||
// get row
|
// get row
|
||||||
|
|||||||
+87
-1
@@ -1,3 +1,89 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
// keypad mapping, input state update
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
// @IDEA: Maybe add controller support?
|
||||||
|
|
||||||
|
// Input handling for CHIP-8 keypad (0x0-0xF) mapped to a standard keyboard.
|
||||||
|
// CHIP-8 uses a polling model, not hardware interrupts - we check key state each cycle.
|
||||||
|
// Standard CHIP-8 -> keyboard mapping:
|
||||||
|
// 1 2 3 C → 1 2 3 4
|
||||||
|
// 4 5 6 D → Q W E R
|
||||||
|
// 7 8 9 E → A S D F
|
||||||
|
// A 0 B F → Z X C V
|
||||||
|
|
||||||
|
handle_input :: proc(s: ^System) {
|
||||||
|
// Row 1
|
||||||
|
s.keypad[0x1] = rl.IsKeyDown(.ONE)
|
||||||
|
s.keypad[0x2] = rl.IsKeyDown(.TWO)
|
||||||
|
s.keypad[0x3] = rl.IsKeyDown(.THREE)
|
||||||
|
s.keypad[0xC] = rl.IsKeyDown(.FOUR)
|
||||||
|
// Row 2
|
||||||
|
s.keypad[0x4] = rl.IsKeyDown(.Q)
|
||||||
|
s.keypad[0x5] = rl.IsKeyDown(.W)
|
||||||
|
s.keypad[0x6] = rl.IsKeyDown(.E)
|
||||||
|
s.keypad[0xD] = rl.IsKeyDown(.R)
|
||||||
|
// Row 3
|
||||||
|
s.keypad[0x7] = rl.IsKeyDown(.A)
|
||||||
|
s.keypad[0x8] = rl.IsKeyDown(.S)
|
||||||
|
s.keypad[0x9] = rl.IsKeyDown(.D)
|
||||||
|
s.keypad[0xE] = rl.IsKeyDown(.F)
|
||||||
|
// Row 4
|
||||||
|
s.keypad[0xA] = rl.IsKeyDown(.Z)
|
||||||
|
s.keypad[0x0] = rl.IsKeyDown(.X)
|
||||||
|
s.keypad[0xB] = rl.IsKeyDown(.C)
|
||||||
|
s.keypad[0xF] = rl.IsKeyDown(.V)
|
||||||
|
|
||||||
|
// Track the last pressed key as a CHIP-8 index (0x0-0xF).
|
||||||
|
// Used by Fx0A which blocks until a key is pressed AND released.
|
||||||
|
// current_key holds the index until release is confirmed, then resets to -1.
|
||||||
|
key := rl.GetKeyPressed()
|
||||||
|
|
||||||
|
#partial switch key {
|
||||||
|
case .ONE: s.current_key = 0x1
|
||||||
|
case .TWO: s.current_key = 0x2
|
||||||
|
case .THREE: s.current_key = 0x3
|
||||||
|
case .FOUR: s.current_key = 0xC
|
||||||
|
case .Q: s.current_key = 0x4
|
||||||
|
case .W: s.current_key = 0x5
|
||||||
|
case .E: s.current_key = 0x6
|
||||||
|
case .R: s.current_key = 0xD
|
||||||
|
case .A: s.current_key = 0x7
|
||||||
|
case .S: s.current_key = 0x8
|
||||||
|
case .D: s.current_key = 0x9
|
||||||
|
case .F: s.current_key = 0xE
|
||||||
|
case .Z: s.current_key = 0xA
|
||||||
|
case .X: s.current_key = 0x0
|
||||||
|
case .C: s.current_key = 0xB
|
||||||
|
case .V: s.current_key = 0xF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true only when the tracked key is released.
|
||||||
|
// Used by Fx0A to check for a press-and-release.
|
||||||
|
check_if_key_released :: proc(key: i16) -> bool {
|
||||||
|
return rl.IsKeyReleased(chip8_key_to_raylib(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps a CHIP-8 key index (0x0-0xF) back to its Raylib equivalent.
|
||||||
|
chip8_key_to_raylib :: proc(key: i16) -> rl.KeyboardKey {
|
||||||
|
switch key {
|
||||||
|
case 0x1: return .ONE
|
||||||
|
case 0x2: return .TWO
|
||||||
|
case 0x3: return .THREE
|
||||||
|
case 0xC: return .FOUR
|
||||||
|
case 0x4: return .Q
|
||||||
|
case 0x5: return .W
|
||||||
|
case 0x6: return .E
|
||||||
|
case 0xD: return .R
|
||||||
|
case 0x7: return .A
|
||||||
|
case 0x8: return .S
|
||||||
|
case 0x9: return .D
|
||||||
|
case 0xE: return .F
|
||||||
|
case 0xA: return .Z
|
||||||
|
case 0x0: return .X
|
||||||
|
case 0xB: return .C
|
||||||
|
case 0xF: return .V
|
||||||
|
case: return .KEY_NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ System :: struct {
|
|||||||
pc: u16,
|
pc: u16,
|
||||||
// 64x32-pixel monochrome display
|
// 64x32-pixel monochrome display
|
||||||
display: [32][64]u8,
|
display: [32][64]u8,
|
||||||
|
// Keypad
|
||||||
keypad: [16]bool,
|
keypad: [16]bool,
|
||||||
|
// -1 = no key pressed, 0-15 = key index
|
||||||
|
current_key: i16,
|
||||||
|
// Timers
|
||||||
delay_timer: u8,
|
delay_timer: u8,
|
||||||
sound_timer: u8,
|
sound_timer: u8,
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -25,7 +25,8 @@ main :: proc() {
|
|||||||
// load rom, hardcoded for now, will eventually be cli or gui
|
// load rom, hardcoded for now, will eventually be cli or gui
|
||||||
// err := chip.load_rom(&system, "./test_roms/2-ibm-logo.ch8")
|
// err := chip.load_rom(&system, "./test_roms/2-ibm-logo.ch8")
|
||||||
// err := chip.load_rom(&system, "./test_roms/1-chip8-logo.ch8")
|
// err := chip.load_rom(&system, "./test_roms/1-chip8-logo.ch8")
|
||||||
err := chip.load_rom(&system, "./test_roms/4-flags.ch8")
|
// err := chip.load_rom(&system, "./test_roms/4-flags.ch8")
|
||||||
|
err := chip.load_rom(&system, "./test_roms/6-keypad.ch8")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to load rom!")
|
panic("failed to load rom!")
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user