1pub mod display;
2
3use rand::{thread_rng, Rng};
4use std::hint::unreachable_unchecked;
5
6#[inline(always)]
7pub fn get_opcode(mem: &[u8; 4096], addr: usize) -> u16 {
8 (mem[addr] as u16) << 8 | mem[addr + 1] as u16
9}
10
11pub struct Chip8State {
12 pub mem: Box<[u8; 4096]>,
13 pub regs: [u8; 16],
14 pub stack: [usize; 16],
15 pub pc: usize, pub i: usize, pub sp: usize, pub sound_timer: u8,
19 pub delay_timer: u8
20}
21
22pub struct Chip8 {
23 mem: Box<[u8; 4096]>,
24 regs: [u8; 16],
25 stack: [usize; 16],
26 pc: usize, i: usize, sp: usize, sound_timer: u8,
30 delay_timer: u8,
31 pub display: display::Display,
32 key_wait_handler: &'static (dyn Fn() -> u8 + Send + Sync + 'static),
33 key_state_handler: &'static (dyn Fn(u8) -> bool + Send + Sync + 'static),
34}
35
36impl Chip8 {
37 pub fn new<T, G>(
38 key_wait_handler: &'static (dyn Fn() -> u8 + Send + Sync + 'static),
39 key_state_handler: &'static (dyn Fn(u8) -> bool + Send + Sync + 'static)
40 ) -> Chip8
41 {
42 Chip8 {
43 mem: Box::new([0; 4096]),
44 regs: [0; 16],
45 stack: [0; 16],
46 pc: 0,
47 i: 0,
48 sp: 0,
49 sound_timer: 0,
50 delay_timer: 0,
51 display: display::Display::new(),
52 key_wait_handler,
53 key_state_handler,
54 }
55 }
56
57 pub fn to_state(&self) -> Chip8State {
58 Chip8State {
59 mem: self.mem.clone(),
60 regs: self.regs,
61 stack: self.stack,
62 pc: self.pc,
63 i: self.i,
64 sp: self.sp,
65 sound_timer: self.sound_timer,
66 delay_timer: self.delay_timer
67 }
68 }
69
70 pub fn set_handlers(
71 &mut self,
72 key_wait_handler: &'static (dyn Fn() -> u8 + Send + Sync + 'static),
73 key_state_handler: &'static (dyn std::ops::Fn(u8) -> bool + Send + Sync + 'static)
74 ) {
75 self.key_wait_handler = key_wait_handler;
76 self.key_state_handler = key_state_handler
77 }
78
79 pub fn get_regs(&self) -> [u8; 16] {
80 self.regs
81 }
82
83 pub fn get_i(&self) -> usize {
84 self.i
85 }
86
87 pub fn get_sound_timer(&self) -> u8 {
88 self.sound_timer
89 }
90
91 pub fn get_delay_timer(&self) -> u8 {
92 self.delay_timer
93 }
94
95 pub fn is_sound_playing(&self) -> bool {
96 self.sound_timer > 0
97 }
98
99 pub fn get_memory(&self, addr: usize) -> u8 {
100 self.mem[addr]
101 }
102
103 pub fn get_opcode(&self, addr: usize) -> u16 {
104 get_opcode(&self.mem, addr)
105 }
106
107 pub fn get_pc(&self) -> usize {
108 self.pc
109 }
110
111 pub fn load(&mut self, at: usize, program: &[u8], font: Option<[u8; 240]>) {
114 if at < 240 {
115 panic!("First 240 bytes are the default font, so can't load here.")
116 }
117 for (i, b) in program.iter().enumerate() {
118 self.mem[at + i] = *b;
119 }
120 let font = match font {
121 None => display::DEFAULT_FONT,
122 Some(f) => f,
123 };
124 for (i, c) in font.iter().enumerate() {
125 self.mem[i] = *c
126 }
127 self.pc = at;
128 }
129
130 fn stack_push(&mut self, v: usize) {
131 self.sp += 1;
132 self.stack[self.sp] = v
133 }
134
135 fn stack_pop(&mut self) -> usize {
136 self.sp -= 1;
137 self.stack[self.sp + 1]
138 }
139
140 pub fn timers_tick(&mut self) {
141 if self.delay_timer > 0 {
142 self.delay_timer -= 1
143 }
144 if self.sound_timer > 0 {
145 self.sound_timer -= 1
146 }
147 }
148
149 pub fn cpu_tick(&mut self) -> Result<(), &'static str> {
150 self.run_opcode((self.mem[self.pc] as u16) << 8 | self.mem[self.pc + 1] as u16)
151 }
152
153 fn run_opcode(&mut self, opcode: u16) -> Result<(), &'static str> {
154 let x = || ((opcode & 0x0F00) >> 8) as usize;
156 let y = || ((opcode & 0x00F0) >> 4) as usize;
157 let n = || opcode & 0x000F;
158 let kk = || opcode & 0x00FF;
159 let nnn = || opcode & 0x0FFF;
160
161 match (opcode & 0xF000) >> 12 {
162 0x0 => match opcode {
164 0x00C0..=0x00CF => self.display.scroll_down(n() as u32),
165 0x00E0 => self.display.clear(),
166 0x00EE => self.pc = self.stack_pop(),
167 0x00FB => self.display.scroll_side(4),
168 0x00FC => self.display.scroll_side(-4),
169 0x00FD => return Err("Program exited"),
170 0x00FE => self.display.low_res_mode(),
171 0x00FF => self.display.hi_res_mode(),
172 _ => {}
173 },
174 0x1 => {
175 self.pc = nnn() as usize;
176 return Ok(());
177 }
178 0x2 => {
179 self.stack_push(self.pc);
180 self.pc = nnn() as usize;
181 return Ok(());
182 }
183 0x3 => {
184 if self.regs[x()] == kk() as u8 {
185 self.pc += 4;
186 return Ok(());
187 }
188 }
189 0x4 => {
190 if self.regs[x()] != kk() as u8 {
191 self.pc += 4;
192 return Ok(());
193 }
194 }
195 0x5 => {
196 if self.regs[x()] == self.regs[y()] as u8 {
197 self.pc += 4;
198 return Ok(());
199 }
200 }
201 0x6 => self.regs[x()] = kk() as u8,
202 0x7 => {
203 let (v, _) = self.regs[x()].overflowing_add(kk() as u8);
204 self.regs[x()] = v
205 }
206 0x8 => match opcode & 0x000F {
207 0x0 => self.regs[x()] = self.regs[y()],
208 0x1 => self.regs[x()] |= self.regs[y()],
209 0x2 => self.regs[x()] &= self.regs[y()],
210 0x3 => self.regs[x()] ^= self.regs[y()],
211 0x4 => {
212 let (v, carry) = self.regs[x()].overflowing_add(self.regs[y()]);
213 self.regs[0xF] = carry as u8;
214 self.regs[x()] = v;
215 }
216 0x5 => {
217 let (v, borrow) = self.regs[x()].overflowing_sub(self.regs[y()]);
218 self.regs[0xF] = !borrow as u8;
219 self.regs[x()] = v;
220 }
221 0x6 => {
222 let (v, carry) = self.regs[y()].overflowing_shr(1);
223 self.regs[x()] = v;
224 self.regs[0xF] = carry as u8;
225 }
226 0x7 => {
227 let (v, borrow) = self.regs[y()].overflowing_add(self.regs[x()]);
228 self.regs[0xF] = !borrow as u8;
229 self.regs[x()] = v;
230 }
231 0xE => {
232 let (v, carry) = self.regs[y()].overflowing_shl(1);
233 self.regs[x()] = v;
234 self.regs[0xF] = carry as u8;
235 }
236 _ => return Err("Invalid opcode"),
237 },
238 0x9 => {
239 if self.regs[x()] != self.regs[y()] as u8 {
240 self.pc += 4;
241 return Ok(());
242 }
243 }
244 0xA => self.i = nnn() as usize,
245 0xB => {
246 self.pc = nnn() as usize + self.regs[0] as usize;
247 return Ok(());
248 }
249 0xC => self.regs[x()] = thread_rng().gen::<u8>() & kk() as u8,
250 0xD => {
251 let mut erased = false;
252 if n() == 0 && self.display.hi_res() {
253 for j in 0..16 {
254 erased |= self.display.write(
255 self.mem[self.i + j * 2],
256 self.regs[x()] as usize,
257 self.regs[y()] as usize + j as usize,
258 );
259 erased |= self.display.write(
260 self.mem[self.i + j * 2 + 1],
261 self.regs[x()] as usize + 8,
262 self.regs[y()] as usize + j as usize,
263 )
264 }
265 } else {
266 for j in 0..n() {
267 erased |= self.display.write(
268 self.mem[self.i + j as usize],
269 self.regs[x()] as usize,
270 self.regs[y()] as usize + j as usize,
271 )
272 }
273 }
274 self.regs[0xF] = erased as u8
275 }
276 0xE => match opcode & 0x00FF {
277 0x9E => {
278 if (self.key_state_handler)(self.regs[x()]) {
279 self.pc += 4;
280 return Ok(());
281 }
282 }
283 0xA1 => {
284 if !(self.key_state_handler)(self.regs[x()]) {
285 self.pc += 4;
286 return Ok(());
287 }
288 }
289 _ => return Err("Invalid opcode"),
290 },
291 0xF => match opcode & 0x00FF {
292 0x07 => self.regs[x()] = self.delay_timer,
293 0x0A => self.regs[x()] = (self.key_wait_handler)(),
294 0x15 => self.delay_timer = self.regs[x()],
295 0x18 => self.sound_timer = self.regs[x()],
296 0x1E => {
297 let (v, _) = self.i.overflowing_add(self.regs[x()] as usize);
298 self.i = v
299 }
300 0x29 => self.i = self.regs[x()] as usize * 5,
301 0x30 => self.i = self.regs[x()] as usize * 10 + 40,
302 0x33 => {
303 let vx = self.regs[x()];
304 self.mem[self.i] = vx / 100;
305 self.mem[self.i + 1] = vx % 100 / 10;
306 self.mem[self.i + 2] = vx % 100 % 10;
307 }
308 0x55 => {
309 for j in 0..=x() {
310 self.mem[self.i + j] = self.regs[j]
311 }
312 }
313 0x65 => {
314 for j in 0..=x() {
315 self.regs[j] = self.mem[self.i + j]
316 }
317 }
318 _ => return Err("Invalid opcode"),
319 },
320 _ => unsafe { unreachable_unchecked() },
321 }
322 self.pc += 2;
323 Ok(())
324 }
325}