chiprust_emu/
lib.rs

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, // Program counter
16    pub i: usize,  // I-register
17    pub sp: usize, // Stack pointer
18    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, // Program counter
27    i: usize,  // I-register
28    sp: usize, // Stack pointer
29    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    /// The at parameter should almost always be 0x200. It's here for compatability with ETI 660 programs (starting with 0x600).
112    /// Panics if at is less than 240, where the default font lies.
113    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        // if self.debug {eprintln!("{:04x?}:{:04x?}", self.pc, opcode)};
155        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            // Instructions that mess with the program counter are returning after that so it wouldn't be incremented after.
163            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}