rust_chip8_opengl/
processor.rs

1use crate::errors::OpcodeError;
2use rand::Rng;
3
4const SCREEN_WIDTH: usize = 64;
5const SCREEN_HEIGHT: usize = 32;
6
7/// The sprites for the digits 0-F, as bytes
8pub const SPRITES: [[u8; 5]; 16] = [
9    [0xF0, 0x90, 0x90, 0x90, 0xF0],
10    [0x00, 0x60, 0x20, 0x20, 0x70],
11    [0xF0, 0x10, 0xF0, 0x80, 0xF0],
12    [0xF0, 0x10, 0xF0, 0x10, 0xF0],
13    [0x90, 0x90, 0xF0, 0x10, 0x10],
14    [0xF0, 0x80, 0xF0, 0x10, 0xF0],
15    [0xF0, 0x80, 0xF0, 0x90, 0xF0],
16    [0xF0, 0x10, 0x20, 0x40, 0x40],
17    [0xF0, 0x90, 0xF0, 0x90, 0xF0],
18    [0xF0, 0x90, 0xF0, 0x10, 0xF0],
19    [0xF0, 0x90, 0xF0, 0x90, 0x90],
20    [0xE0, 0x90, 0xE0, 0x90, 0xE0],
21    [0xF0, 0x80, 0x80, 0x80, 0xF0],
22    [0xE0, 0x90, 0x90, 0x90, 0xE0],
23    [0xF0, 0x80, 0xF0, 0x80, 0xF0],
24    [0xF0, 0x80, 0xF0, 0x80, 0x80],
25];
26
27/**
28 * The actual CHIP-8 processor.
29 * Decodes and runs any opcodes, stores memory, stores screen.
30 * Needs to be paired with an interface to allow the user to actually interact with the program.
31 */
32pub struct Processor {
33    // Buffer for the screen
34    screen_buffer: [bool; SCREEN_WIDTH * SCREEN_HEIGHT],
35    // Registers (1 through F)
36    registers: [u8; 0x10],
37    // Program Counter
38    pc: usize,
39    // Stack and stack pointer
40    stack: [u16; 0x10],
41    sp: usize,
42    // Memory
43    mem: [u8; 0x1000],
44    // I (index register)
45    i: u16,
46    // Delay timer
47    dt: u8,
48    // Sound timer
49    st: u8,
50    // Keys that are currently pressed
51    input_state: [bool; 0x10],
52    // debug print mode used in tests
53    debug_print: bool,
54    // Key that was just released, should be set by the interface
55    // Used for LD X KP and nothing else
56    // Reset every execution
57    last_key_released: Option<u8>,
58
59    vblank: bool,
60}
61
62impl Processor {
63    pub fn new() -> Processor {
64        let mut c = Processor {
65            screen_buffer: [false; SCREEN_WIDTH * SCREEN_HEIGHT],
66            registers: [0x00; 0x10],
67            pc: 0x200,
68            stack: [0x00; 0x10],
69            sp: 0,
70            mem: [0x00; 0x1000],
71            i: 0,
72            dt: 0,
73            st: 0,
74            input_state: [false; 0x10],
75            debug_print: false,
76            last_key_released: None,
77            vblank: false,
78        };
79        (0..0x10).for_each(|i| c.mem[(6 * i)..(6 * i + 5)].copy_from_slice(&SPRITES[i]));
80
81        return c;
82    }
83    /**
84     * Load a program into memory
85     **/
86    pub fn load_program(&mut self, program: &[u8]) {
87        program
88            .iter()
89            .enumerate()
90            .for_each(|(i, v)| self.mem[0x200 + i] = *v);
91    }
92    /**
93     * Load a program as u16s instead of u8s.
94     * Mostly just a convenience function.
95     **/
96    #[allow(dead_code)]
97    pub fn load_program_u16(&mut self, program: &[u16]) {
98        (0..program.len()).for_each(|i| {
99            self.mem[0x200 + 2 * i] = (program[i] >> 8) as u8;
100            self.mem[0x200 + 2 * i + 1] = program[i] as u8;
101        });
102    }
103    /**
104     * Perform the next step in whatever program has been loaded into memory.
105     * Equivalent to just calling `execute` and incrementing `PC` by 2
106     **/
107    pub fn step(&mut self) -> Result<(), OpcodeError> {
108        let r = self.execute(((self.mem[self.pc] as u16) << 8) | self.mem[self.pc + 1] as u16);
109        self.pc += 2;
110        r
111    }
112    /**
113     * Function that decrements both timer registers.
114     * Should be called at a rate of 60Hz.
115     **/
116    pub fn on_tick(&mut self) {
117        self.dt = self.dt.saturating_sub(1);
118        self.st = self.st.saturating_sub(1);
119    }
120    /**
121     * Update the current input states to the inputs given.
122     **/
123    pub fn update_inputs(&mut self, inputs: [bool; 0x10]) {
124        inputs.iter().enumerate().for_each(|(i, v)| {
125            if !v && self.input_state[i] {
126                self.last_key_released = Some(i as u8);
127            }
128        });
129        self.input_state = inputs;
130    }
131    /**
132     * Executes a single given instruction.
133     * Does not increment PC or affect DT or ST.
134     **/
135    pub fn execute(&mut self, inst: u16) -> Result<(), OpcodeError> {
136        if self.debug_print {
137            println!("Inst is {:X}", inst);
138            println!("Initial state:");
139            self.dump_state();
140        }
141        /*
142         * Match opcode
143         * Naming convention for all these functions
144         * FUNC_[ARGS...]
145         * FUNC: Description of what the function does (i.e. ld for load, add for adding)
146         * ARGS: The arguments
147         *       The first arg is the one being saved to, so ld_rx_ry will load Ry into Rx
148         *       r[x]: Register index
149         *       kk: Constant
150         *       addr: Address
151         *       kp: Key press
152         */
153        match inst & 0xF000 {
154            0x0000 => match inst & 0x0FFF {
155                0x0E0 => self.clr(),
156                0x0EE => self.ret(),
157                _ => {}
158            },
159            0x1000 => self.jmp(inst & 0x0FFF),
160            0x2000 => self.call(inst & 0xFFF),
161            0x3000 => self.se_r_kk(reg_at(inst, 1), (inst & 0xFF) as u8),
162            0x4000 => self.sne_r_kk(inst),
163            0x5000 => {
164                if inst & 0xF != 0 {
165                    return Err(OpcodeError::new(inst, self.pc as u8));
166                }
167                self.se_rx_ry(reg_at(inst, 1), reg_at(inst, 2));
168            }
169            0x6000 => self.ld_r_kk(reg_at(inst, 1), (inst & 0xFF) as u8),
170            0x7000 => self.add_r_kk(reg_at(inst, 1), (inst & 0xFF) as u8),
171            0x8000 => {
172                let rx = reg_at(inst, 1);
173                let ry = reg_at(inst, 2);
174                match inst & 0x000F {
175                    0 => self.ld_rx_ry(rx, ry),
176                    1 => self.or_rx_ry(rx, ry),
177                    2 => self.and_rx_ry(rx, ry),
178                    3 => self.xor_rx_ry(rx, ry),
179                    4 => self.add_rx_ry(rx, ry),
180                    5 => self.sub_rx_ry(rx, ry),
181                    6 => self.shr(rx, ry),
182                    7 => self.subn(rx, ry),
183                    0xE => self.shl(rx, ry),
184                    _ => return Err(OpcodeError::new(inst, self.pc as u8)),
185                }
186            }
187            0x9000 => {
188                if inst & 0xF != 0 {
189                    return Err(OpcodeError::new(inst, self.pc as u8));
190                }
191                self.sne_rx_ry(reg_at(inst, 1), reg_at(inst, 2));
192            }
193            0xA000 => self.ld_i(inst & 0x0FFF),
194            0xB000 => self.jmp_r0(inst & 0x0FFF),
195            0xC000 => self.rand(reg_at(inst, 1), inst & 0xFF),
196            0xD000 => self.draw(reg_at(inst, 1), reg_at(inst, 2), reg_at(inst, 3)),
197            0xE000 => match inst & 0x00FF {
198                0x9E => self.skp_r(reg_at(inst, 1)),
199                0xA1 => self.sknp_r(reg_at(inst, 1)),
200                _ => return Err(OpcodeError::new(inst, self.pc as u8)),
201            },
202            0xF000 => match inst & 0x00FF {
203                0x07 => self.ld_r_dt(reg_at(inst, 1)),
204                0x0A => self.ld_r_kp(reg_at(inst, 1)),
205                0x15 => self.ld_dt_r(reg_at(inst, 1)),
206                0x18 => self.ld_st_r(reg_at(inst, 1)),
207                0x1E => self.add_i_r(reg_at(inst, 1)),
208                0x29 => self.ld_i_spr_x(reg_at(inst, 1)),
209                0x33 => self.ld_bcd_r(reg_at(inst, 1)),
210                0x55 => self.store_at_i(reg_at(inst, 1)),
211                0x65 => self.load_from_i(reg_at(inst, 1)),
212                _ => return Err(OpcodeError::new(inst, self.pc as u8)),
213            },
214            _ => return Err(OpcodeError::new(inst, self.pc as u8)),
215        }
216        if self.debug_print {
217            println!("Post state:");
218            self.dump_state();
219        }
220        self.last_key_released = None;
221
222        return Ok(());
223    }
224
225    /**
226     * Print the state of the machine (all registers, stack, PC, etc) in the console.
227     */
228    pub fn dump_state(&self) {
229        for i in 0..16 {
230            print!("V{:X} = {:#2X}, ", i, self.registers[i])
231        }
232        println!("");
233        println!("Stack: {:X?}", self.stack);
234        println!(
235            "PC = {:X?}, SP = {:X?}, I = {:X?}, DT = {:X?}, ST = {:X?}",
236            self.pc, self.sp, self.i, self.dt, self.st
237        );
238        print!("Memory:");
239        self.mem.iter().enumerate().for_each(|(i, v)| {
240            if i % 0x40 == 0 {
241                print!("\n{:03X}: ", i);
242            }
243            print!("{:02X?}", v);
244        });
245        println!("");
246        print!("Screen:");
247        self.screen_buffer.iter().enumerate().for_each(|(i, s)| {
248            if i % 64 == 0 {
249                println!("");
250            }
251            print!("{}", if *s { 1 } else { 0 });
252        });
253        println!("");
254    }
255
256    fn ld_r_kk(&mut self, r: usize, kk: u8) {
257        self.registers[r] = kk;
258    }
259
260    fn ld_rx_ry(&mut self, rx: usize, ry: usize) {
261        self.registers[rx] = self.registers[ry];
262    }
263
264    fn or_rx_ry(&mut self, rx: usize, ry: usize) {
265        self.registers[rx] = self.registers[rx] | self.registers[ry];
266        self.registers[0xF] = 0x0;
267    }
268
269    fn and_rx_ry(&mut self, rx: usize, ry: usize) {
270        self.registers[rx] = self.registers[rx] & self.registers[ry];
271        self.registers[0xF] = 0x0;
272    }
273
274    fn xor_rx_ry(&mut self, rx: usize, ry: usize) {
275        self.registers[rx] = self.registers[rx] ^ self.registers[ry];
276        self.registers[0xF] = 0x0;
277    }
278
279    fn add_rx_ry(&mut self, rx: usize, ry: usize) {
280        let v = self.registers[rx] as u16 + self.registers[ry] as u16;
281        self.registers[rx] = v as u8;
282        self.registers[0xF] = if v > 0xFF { 1 } else { 0 };
283    }
284    fn sub_rx_ry(&mut self, rx: usize, ry: usize) {
285        let vf = if self.registers[ry] > self.registers[rx] {
286            0
287        } else {
288            1
289        };
290        self.registers[rx] = self.registers[rx].wrapping_sub(self.registers[ry]);
291        self.registers[0xF] = vf;
292    }
293    fn shr(&mut self, rx: usize, ry: usize) {
294        let vf = if self.registers[ry] & 0x01 == 1 { 1 } else { 0 };
295        self.registers[rx] = self.registers[ry] >> 1;
296        self.registers[0xF] = vf;
297    }
298    fn subn(&mut self, rx: usize, ry: usize) {
299        let vf = if self.registers[rx] > self.registers[ry] {
300            0
301        } else {
302            1
303        };
304        self.registers[rx] = self.registers[ry].wrapping_sub(self.registers[rx]);
305        self.registers[0xF] = vf;
306    }
307    fn shl(&mut self, rx: usize, ry: usize) {
308        let vf = if self.registers[ry] & 0x80 == 0x80 {
309            1
310        } else {
311            0
312        };
313        self.registers[rx] = self.registers[ry] << 1;
314        self.registers[0xF] = vf;
315    }
316    fn clr(&mut self) {
317        self.screen_buffer = [false; SCREEN_WIDTH * SCREEN_HEIGHT];
318    }
319    fn ret(&mut self) {
320        self.sp -= 1;
321        self.pc = self.stack[self.sp] as usize;
322    }
323    fn jmp(&mut self, addr: u16) {
324        // addr - 2 since we are about to add 2
325        self.pc = (addr - 2) as usize;
326    }
327    fn jmp_r0(&mut self, addr: u16) {
328        self.pc = (addr + self.registers[0] as u16) as usize - 2;
329    }
330    fn call(&mut self, addr: u16) {
331        self.stack[self.sp] = self.pc as u16;
332        self.sp += 1;
333        self.pc = addr as usize - 2;
334    }
335    fn se_r_kk(&mut self, r: usize, kk: u8) {
336        if self.registers[r] == kk {
337            self.pc += 2;
338        }
339    }
340    fn se_rx_ry(&mut self, rx: usize, ry: usize) {
341        if self.registers[rx] == self.registers[ry] {
342            self.pc += 2;
343        }
344    }
345    fn sne_r_kk(&mut self, inst: u16) {
346        if self.registers[reg_at(inst, 1)] as u16 != inst & 0xFF {
347            self.pc += 2;
348        }
349    }
350    fn sne_rx_ry(&mut self, x: usize, y: usize) {
351        if self.registers[x] != self.registers[y] {
352            self.pc += 2;
353        }
354    }
355    fn skp_r(&mut self, x: usize) {
356        if self.input_state[self.registers[x] as usize] {
357            self.pc += 2;
358        }
359    }
360    fn sknp_r(&mut self, x: usize) {
361        if !self.input_state[self.registers[x] as usize] {
362            self.pc += 2;
363        }
364    }
365    fn ld_r_kp(&mut self, r: usize) {
366        match self.last_key_released {
367            Some(i) => self.registers[r] = i as u8,
368            // Sneaky hack - in order to "wait" we just decrement PC so that we reach this addr again
369            // In retrospect this probably isn't that sneaky
370            None => self.pc -= 2,
371        }
372    }
373    fn ld_i(&mut self, addr: u16) {
374        self.i = addr;
375    }
376    fn load_from_i(&mut self, n: usize) {
377        for j in 0..(n + 1) {
378            self.registers[j as usize] = self.mem[(self.i + j as u16) as usize];
379        }
380        self.i += (n + 1) as u16;
381    }
382    fn store_at_i(&mut self, n: usize) {
383        for j in 0..(n + 1) {
384            self.mem[(self.i + j as u16) as usize] = self.registers[j as usize];
385        }
386        self.i += (n + 1) as u16;
387    }
388    fn add_i_r(&mut self, r: usize) {
389        self.i = self.i.wrapping_add(self.registers[r] as u16);
390    }
391    fn add_r_kk(&mut self, r: usize, kk: u8) {
392        self.registers[r] = self.registers[r].wrapping_add(kk);
393    }
394    fn rand(&mut self, r: usize, mask: u16) {
395        self.registers[r] = (rand::thread_rng().gen_range(0..0xFF) & mask) as u8;
396    }
397    fn ld_st_r(&mut self, r: usize) {
398        self.st = self.registers[r];
399    }
400    fn ld_dt_r(&mut self, x: usize) {
401        self.dt = self.registers[x];
402    }
403    fn ld_r_dt(&mut self, x: usize) {
404        self.registers[x] = self.dt;
405    }
406    fn draw(&mut self, rx: usize, ry: usize, n: usize) {
407        if !self.vblank {
408            self.pc -= 2;
409            return;
410        }
411        self.vblank = false;
412        self.registers[0xF] = 0;
413        let x = self.registers[rx] % SCREEN_WIDTH as u8;
414        let y = self.registers[ry] % SCREEN_HEIGHT as u8;
415        // XOR data onto screen
416        for j in 0..n {
417            let mut val = self.mem[self.i as usize + j];
418            for k in 0..8 {
419                if x as usize + k >= SCREEN_WIDTH || y as usize + j >= SCREEN_HEIGHT {
420                    continue;
421                }
422                let coord: usize = (y as usize + j) * SCREEN_WIDTH + x as usize + k;
423                let p = (val & 0x80) != 0; // Get MSB
424                if p && self.screen_buffer[coord] {
425                    self.registers[0xF] = 1;
426                }
427                self.screen_buffer[coord] = p ^ self.screen_buffer[coord];
428                val = val << 1;
429            }
430        }
431    }
432    // Only loads the sprite for the LSByte of Vr
433    fn ld_i_spr_x(&mut self, r: usize) {
434        // 6 because that's the length of each sprite
435        // 5 + 1 buffer row
436        self.i = (self.registers[r] as u16 & 0xF) * 0x6;
437    }
438    fn ld_bcd_r(&mut self, r: usize) {
439        self.mem[self.i as usize] = self.registers[r] / 100;
440        self.mem[self.i as usize + 1] = (self.registers[r] / 10) % 10;
441        self.mem[self.i as usize + 2] = self.registers[r] % 10;
442    }
443
444    /// Get the value of an R register
445    pub fn get_register_value(&self, register: u8) -> u8 {
446        return self.registers[register as usize];
447    }
448    /// Get the value of the program counter
449    pub fn get_program_counter(&self) -> usize {
450        return self.pc;
451    }
452    /// Get the value of the I register
453    pub fn get_i(&self) -> u16 {
454        return self.i;
455    }
456    /// Get a single byte of memory at the address given
457    pub fn get_mem_at(&self, addr: usize) -> u8 {
458        return self.mem[addr];
459    }
460    /**
461     * Get a pixel at the given `x, y` position on the screen.
462     * Accounts for screen wrapping.
463     */
464    pub fn get_pixel_at(&self, x: u8, y: u8) -> bool {
465        return self.screen_buffer
466            [((x as usize % 64) + y as usize * 64) % self.screen_buffer.len()];
467    }
468    /**
469     * Return whether the processor has the `i` key currently being pressed.
470     * This should be set by calls to `update_inputs`.
471     */
472    pub fn get_input_state(&self, i: usize) -> bool {
473        return self.input_state[i];
474    }
475    /// Get the D timer register.
476    pub fn get_dt(&self) -> u8 {
477        return self.dt;
478    }
479    /// Get the S (sound) time register.
480    pub fn get_st(&self) -> u8 {
481        return self.st;
482    }
483    /// Callback on VBlank (vertical interrupt)
484    /// Used to ensure display waiting
485    /// To ignore, just call every tick
486    pub fn on_v_blank(&mut self) {
487        self.vblank = true;
488    }
489}
490
491// Get index of the register given the instruction
492// and the position of the byte from the left in the instruction
493// i.e. inst = 0xABCD, pos = 3, res = 0x000C
494fn reg_at(inst: u16, pos: u8) -> usize {
495    return ((inst >> (12 - pos * 4)) & 0x000F) as usize;
496}