Newer
Older
use chip_ahoyto::chip8::{Chip8, SCREEN_PIXEL_HEIGHT, SCREEN_PIXEL_WIDTH};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use sdl2::{event::Event, keyboard::Keycode, pixels::PixelFormatEnum};
use std::{fs::File, io::Read};
const PIXEL_SET: [u8; 3] = [80, 203, 147];
const SYSTEM_HZ: u32 = 240;
const SCREEN_SCALE: f32 = 15.0;
const TITLE: &str = "Drag and drop the ROM file to play";
fn main() {
let sdl = sdl2::init().unwrap();
let video_subsystem = sdl.video().unwrap();
let mut timer_subsystem = sdl.timer().unwrap();
let window = video_subsystem
.window(
TITLE,
SCREEN_SCALE as u32 * SCREEN_PIXEL_WIDTH as u32,
SCREEN_SCALE as u32 * SCREEN_PIXEL_HEIGHT as u32,
)
.resizable()
.position_centered()
.build()
.unwrap();
let mut canvas = window.into_canvas().build().unwrap();
canvas.set_scale(SCREEN_SCALE, SCREEN_SCALE).unwrap();
canvas.clear();
canvas.present();
let texture_creator = canvas.texture_creator();
let mut texture = texture_creator
.create_texture_streaming(
PixelFormatEnum::RGB24,
SCREEN_PIXEL_WIDTH as u32,
SCREEN_PIXEL_HEIGHT as u32,
)
.unwrap();
let mut chip8 = Chip8::new();
let mut game_loaded = false;
let tick_interval = 1000 / SYSTEM_HZ;
let mut last_update_time = 0;
let mut event_pump = sdl.event_pump().unwrap();
'main: loop {
while let Some(event) = event_pump.poll_event() {
match event {
Event::Quit { .. } => break 'main,
Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => break 'main,
Event::DropFile { filename, .. } => {
let rom = read_file(&filename);
chip8 = Chip8::new();
chip8.load_rom(&rom);
chip8.reset();
game_loaded = true;
canvas
.window_mut()
.set_title(&format!("{} [Currently playing: {}]", TITLE, filename))
.unwrap();
None
}
Event::KeyDown {
keycode: Some(keycode),
..
} if game_loaded => key_to_btn(keycode).map(|btn| chip8.key_press(btn)),
Event::KeyUp {
keycode: Some(keycode),
..
} if game_loaded => key_to_btn(keycode).map(|btn| chip8.key_lift(btn)),
_ => None,
};
}
let current_time = timer_subsystem.ticks();
let delta_t = current_time - last_update_time;
if game_loaded && tick_interval > delta_t {
// runs the tick operation in the CHIP-8 system,
// effectively changing the logic state of the machine
chip8.clock();
chip8.clock_dt();
chip8.clock_st();
timer_subsystem.delay(tick_interval - delta_t);
// @todo this looks to be very slow!
// we should use a callback on pixel buffer change
// to make this a faster thing
let mut rgb_pixels = vec![];
for p in chip8.pixels() {
rgb_pixels.extend_from_slice(&[
p * PIXEL_SET[0],
p * PIXEL_SET[1],
p * PIXEL_SET[2],
])
}
// creates a texture based on the RGB pixel buffer
// and copies that to the canvas for presentation
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
texture
.update(None, &rgb_pixels, SCREEN_PIXEL_WIDTH as usize * 3)
.unwrap();
canvas.copy(&texture, None, None).unwrap();
canvas.present();
}
last_update_time = current_time;
}
}
fn key_to_btn(keycode: Keycode) -> Option<u8> {
match keycode {
Keycode::Num1 => Some(0x01),
Keycode::Num2 => Some(0x02),
Keycode::Num3 => Some(0x03),
Keycode::Num4 => Some(0x0C),
Keycode::Q => Some(0x04),
Keycode::W => Some(0x05),
Keycode::E => Some(0x06),
Keycode::R => Some(0x0D),
Keycode::A => Some(0x07),
Keycode::S => Some(0x08),
Keycode::D => Some(0x09),
Keycode::F => Some(0x0E),
Keycode::Z => Some(0x0A),
Keycode::X => Some(0x00),
Keycode::C => Some(0x0B),
Keycode::V => Some(0x0F),
_ => None,
}
}
fn read_file(path: &str) -> Vec<u8> {
let mut file = File::open(path).unwrap();
let mut rom = Vec::new();
file.read_to_end(&mut rom).unwrap();
rom
}