Compare commits
2 Commits
5c77429374
...
2789742dfd
Author | SHA1 | Date |
---|---|---|
Michael Kohl | 2789742dfd | |
Michael Kohl | 0b75270984 |
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
MIT License Copyright (c) <year> <copyright holders>
|
||||
MIT License Copyright (c) 2021 Michael Kohl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
36
README.md
36
README.md
|
@ -1,3 +1,37 @@
|
|||
# rust-chip-8
|
||||
|
||||
A CHIP-8 emulator written in Rust
|
||||
A CHIP-8 emulator written in Rust
|
||||
|
||||
## Keyboard mapping
|
||||
|
||||
CHIP-8 systems used a hexidecimal keyboard with the following layout:
|
||||
|
||||
|---|---|---|---|
|
||||
| 1 | 2 | 3 | C |
|
||||
|---|---|---|---|
|
||||
| 4 | 5 | 6 | D |
|
||||
|---|---|---|---|
|
||||
| 7 | 8 | 0 | E |
|
||||
|---|---|---|---|
|
||||
| A | 0 | B | F |
|
||||
|---|---|---|---|
|
||||
|
||||
In this emulator the following keys are mapped to the above layout:
|
||||
|
||||
|---|---|---|---|
|
||||
| 1 | 2 | 3 | 4 |
|
||||
|---|---|---|---|
|
||||
| Q | W | E | R |
|
||||
|---|---|---|---|
|
||||
| A | S | D | F |
|
||||
|---|---|---|---|
|
||||
| Z | X | C | V |
|
||||
|---|---|---|---|
|
||||
|
||||
So to get "deadbeef" inside the emulator you'd have to type "rfzrcffv".
|
||||
|
||||
## License
|
||||
|
||||
MIT License Copyright (c) 2021 Michael Kohl
|
||||
|
||||
For the full license text see [LICENSE](./LICENSE).
|
||||
|
|
58
src/chip8.rs
58
src/chip8.rs
|
@ -1,8 +1,11 @@
|
|||
use sdl2::keyboard::Keycode;
|
||||
|
||||
const MEMORY_SIZE: usize = 4096;
|
||||
const WIDTH: u32 = 64;
|
||||
const HEIGHT: u32 = 32;
|
||||
const DATA_REGISTERS: usize = 16;
|
||||
const STACK_DEPTH: usize = 16;
|
||||
const KEYS: usize = 16;
|
||||
|
||||
pub const SCALE_FACTOR: u32 = 10;
|
||||
pub const WINDOW_WIDTH: u32 = WIDTH * SCALE_FACTOR;
|
||||
|
@ -45,10 +48,13 @@ struct Registers {
|
|||
|
||||
type Stack = [u16; STACK_DEPTH];
|
||||
|
||||
type Keyboard = [bool; KEYS];
|
||||
|
||||
pub struct Chip8 {
|
||||
memory: Memory,
|
||||
registers: Registers,
|
||||
stack: Stack,
|
||||
keyboard: Keyboard,
|
||||
}
|
||||
|
||||
fn data_register_to_index(register: Register) -> usize {
|
||||
|
@ -86,6 +92,7 @@ impl Chip8 {
|
|||
sp: 0,
|
||||
},
|
||||
stack: [0; STACK_DEPTH],
|
||||
keyboard: [false; KEYS],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +138,40 @@ impl Chip8 {
|
|||
assert!((self.registers.sp as usize) < STACK_DEPTH, "stack overflow");
|
||||
self.stack[self.registers.sp as usize]
|
||||
}
|
||||
|
||||
pub fn keyboard_map(&self, key: Keycode) -> Option<usize> {
|
||||
match key {
|
||||
Keycode::Num1 => Some(1),
|
||||
Keycode::Num2 => Some(2),
|
||||
Keycode::Num3 => Some(3),
|
||||
Keycode::Num4 => Some(12),
|
||||
Keycode::Q => Some(4),
|
||||
Keycode::W => Some(5),
|
||||
Keycode::E => Some(6),
|
||||
Keycode::R => Some(13),
|
||||
Keycode::A => Some(7),
|
||||
Keycode::S => Some(8),
|
||||
Keycode::D => Some(9),
|
||||
Keycode::F => Some(14),
|
||||
Keycode::Z => Some(10),
|
||||
Keycode::X => Some(0),
|
||||
Keycode::C => Some(11),
|
||||
Keycode::V => Some(15),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_down(&mut self, key: usize) -> () {
|
||||
self.keyboard[key] = true
|
||||
}
|
||||
|
||||
pub fn key_up(&mut self, key: usize) -> () {
|
||||
self.keyboard[key] = false
|
||||
}
|
||||
|
||||
pub fn is_key_down(&self, key: usize) -> bool {
|
||||
self.keyboard[key]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -202,4 +243,21 @@ mod tests {
|
|||
assert_eq!(chip8.stack_pop(), 255);
|
||||
assert_eq!(chip8.register_get(Register::SP), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_maps_physical_keys_to_virtual_ones() {
|
||||
let mut chip8 = Chip8::new();
|
||||
assert_eq!(chip8.keyboard_map(Keycode::A), Some(7));
|
||||
assert_eq!(chip8.keyboard_map(Keycode::X), Some(0));
|
||||
assert_eq!(chip8.keyboard_map(Keycode::M), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_press_and_release_keys() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.key_down(1);
|
||||
assert!(chip8.is_key_down(1));
|
||||
chip8.key_up(1);
|
||||
assert!(!chip8.is_key_down(1));
|
||||
}
|
||||
}
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -1,5 +1,5 @@
|
|||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::keyboard::{Keycode, Scancode};
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
|
||||
|
@ -8,6 +8,8 @@ mod chip8;
|
|||
const EMULATOR_WINDOW_TITLE: &str = "Rust CHIP-8";
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let mut chip8 = chip8::Chip8::new();
|
||||
|
||||
let sdl_context = sdl2::init()?;
|
||||
let video_subsystem = sdl_context.video()?;
|
||||
|
||||
|
@ -46,6 +48,22 @@ fn main() -> Result<(), String> {
|
|||
} => {
|
||||
break 'mainloop;
|
||||
}
|
||||
Event::KeyDown {
|
||||
keycode: Some(key), ..
|
||||
} => {
|
||||
if let Some(vkey) = chip8.keyboard_map(key) {
|
||||
chip8.key_down(vkey);
|
||||
println!("key down: {}", vkey);
|
||||
}
|
||||
}
|
||||
Event::KeyUp {
|
||||
keycode: Some(key), ..
|
||||
} => {
|
||||
if let Some(vkey) = chip8.keyboard_map(key) {
|
||||
chip8.key_up(vkey);
|
||||
println!("key up: {}", vkey);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue