arnaucube
arnaucube/chip8-rsPublic Self-hosted repository
chip8-rs/chip8/src/lib.rs
chip8/src/lib.rsRaw
  1use rand::Rng;
  2use std::fs;
  3use std::io::Read;
  4
  5const w: usize = 64;
  6const h: usize = 32;
  7
  8pub struct Chip8 {
  9    opcode: u16,
 10    memory: [u8; 4096],
 11    v: [u8; 16],
 12    index: u16,
 13    pc: u16,
 14    pub gfx: [u8; w * h],
 15    delay_timer: u8,
 16    sound_timer: u8,
 17    stack: [u16; 16],
 18    sp: usize,
 19    pub key: [u8; 16],
 20    pub draw_flag: bool,
 21}
 22
 23const font_set: [u8; 80] = [
 24    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
 25    0x20, 0x60, 0x20, 0x20, 0x70, // 1
 26    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
 27    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
 28    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
 29    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
 30    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
 31    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
 32    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
 33    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
 34    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
 35    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
 36    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
 37    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
 38    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
 39    0xF0, 0x80, 0xF0, 0x80, 0x80, // F
 40];
 41
 42impl Chip8 {
 43    pub fn new() -> Chip8 {
 44        let mut c = Chip8 {
 45            opcode: 0,
 46            memory: [0; 4096],
 47            v: [0; 16],
 48            index: 0,
 49            pc: 0x200,
 50            gfx: [0; w * h],
 51            delay_timer: 0,
 52            sound_timer: 0,
 53            stack: [0; 16],
 54            sp: 0,
 55            key: [0; 16],
 56            draw_flag: false,
 57        };
 58
 59        for i in 0..font_set.len() {
 60            c.memory[i] = font_set[i];
 61        }
 62        c
 63    }
 64    pub fn load_game(&mut self, filepath: &str) {
 65        let mut f = fs::File::open(filepath).expect("can not load rom file");
 66        let metadata = fs::metadata(filepath).expect("unable to read metadata");
 67        let mut b = vec![0; metadata.len() as usize];
 68        f.read(&mut b).expect("buffer overflow");
 69        for i in 0..b.len() {
 70            self.memory[512 + i] = b[i];
 71        }
 72    }
 73    pub fn emulate_cycle(&mut self) {
 74        self.opcode = ((self.memory[self.pc as usize] as u16) << 8)
 75            | (self.memory[(self.pc + 1) as usize] as u16);
 76        let x: usize = ((self.opcode & 0x0F00) >> 8) as usize;
 77        let y: usize = ((self.opcode & 0x00F0) >> 4) as usize;
 78        let nn: u8 = (self.opcode & 0x00FF) as u8;
 79        let nnn: u16 = (self.opcode & 0x0FFF) as u16;
 80        // println!("{:x} {:?}", self.opcode, self.pc);
 81
 82        // Decode Opcode
 83        // https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
 84        match self.opcode & 0xF000 {
 85            0x0000 => {
 86                match self.opcode & 0x000F {
 87                    0x0000 => {
 88                        // 00E0 Clear screen
 89                        for i in 0..self.gfx.len() {
 90                            self.gfx[i] = 0;
 91                        }
 92                        self.pc += 2;
 93                        self.draw_flag = true;
 94                    }
 95                    0x000E => {
 96                        // 00EE Returns from a subroutine
 97                        self.sp -= 1;
 98                        self.pc = self.stack[self.sp];
 99                        self.pc += 2;
100                    }
101                    _ => println!("Unknown opcode: {:x}", self.opcode),
102                }
103            }
104            0x1000 => {
105                // 1NNN Jumps to address NNN
106                self.pc = nnn;
107            }
108            0x2000 => {
109                // 2NNN Calls subroutine at NNN
110                self.stack[self.sp] = self.pc;
111                self.sp += 1;
112                self.pc = nnn;
113            }
114            0x3000 => {
115                // 3XNN Skips the next instruction if VX equals NN. (Usually
116                // the next instruction is a jump to skip a code block)
117                if self.v[x] == nn {
118                    self.pc += 2;
119                }
120                self.pc += 2;
121            }
122            0x4000 => {
123                // 4XNN Skips the next instruction if VX doesn't equal NN.
124                // (Usually the next instruction is a jump to skip a code
125                // block)
126                if self.v[x] != nn {
127                    self.pc += 2;
128                }
129                self.pc += 2;
130            }
131            0x5000 => {
132                // 5XY0 Skips the next instruction if VX equals VY. (Usually
133                // the next instruction is a jump to skip a code block)
134                if self.v[x] == self.v[y] {
135                    self.pc += 2;
136                }
137                self.pc += 2;
138            }
139            0x6000 => {
140                // 6XNN Sets VX to NN
141                self.v[x] = nn;
142                self.pc += 2;
143            }
144            0x7000 => {
145                // 7XNN Adds NN to VX. (Carry flag is not changed)
146                self.v[x] = self.v[x].wrapping_add(nn);
147                self.pc += 2;
148            }
149            0x8000 => {
150                match self.opcode & 0x000F {
151                    0x0000 => {
152                        // 0x8XY0 Sets VX to the value of VY
153                        self.v[x] = self.v[y];
154                        self.pc += 2;
155                    }
156                    0x0001 => {
157                        // 0x8XY1 Sets VX to VX or VY. (Bitwise OR operation)
158                        self.v[x] = (self.v[x] | self.v[y]);
159                        self.pc += 2;
160                    }
161                    0x0002 => {
162                        // 0x8XY2 Sets VX to VX and VY. (Bitwise AND operation)
163                        self.v[x] = (self.v[x] & self.v[y]);
164                        self.pc += 2;
165                    }
166                    0x0003 => {
167                        // 0x8XY3 Sets VX to VX xor VY
168                        self.v[x] = (self.v[x] ^ self.v[y]);
169                        self.pc += 2;
170                    }
171                    0x0004 => {
172                        // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a
173                        // carry, and to 0 when there isn't
174                        if self.v[y] > (0xFF - self.v[x]) {
175                            self.v[0xF] = 1;
176                        } else {
177                            self.v[0xF] = 0;
178                        }
179                        self.v[x] = self.v[x].wrapping_add(self.v[y]);
180                        self.pc += 2;
181                    }
182                    0x0005 => {
183                        // 0x8XY5 VY is subtracted from VX. VF is set to 0 when
184                        // there's a borrow, and 1 when there isn't
185                        if self.v[x] > self.v[y] {
186                            self.v[0xF] = 1;
187                        } else {
188                            self.v[0xF] = 0;
189                        }
190                        self.v[x] = self.v[x].wrapping_sub(self.v[y]);
191                        self.pc += 2;
192                    }
193                    0x0006 => {
194                        // 0x8XY6 Stores the least significant bit of VX in VF
195                        // and then shifts VX to the right by 1
196                        if self.opcode & 0x1 >= 1 {
197                            self.v[0xF] = 1;
198                        } else {
199                            self.v[0xF] = 0;
200                        }
201                        self.v[x] = self.v[x].wrapping_shr(1);
202                        self.pc += 2;
203                    }
204                    0x0007 => {
205                        // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when
206                        // there's a borrow, and 1 when there isn't
207                        if self.v[y] > self.v[x] {
208                            self.v[0xF] = 1;
209                        } else {
210                            self.v[0xF] = 0;
211                        }
212                        self.v[x] = self.v[y].wrapping_sub(self.v[x]);
213                        self.pc += 2;
214                    }
215                    0x000E => {
216                        // 0x8XYE Stores the most significant bit of VX in VF
217                        // and then shifts VX to the left by 1
218                        if self.opcode & 0x80 == 0x80 {
219                            self.v[0xF] = 1;
220                        } else {
221                            self.v[0xF] = 0;
222                        }
223                        self.v[x] = self.v[x].wrapping_shl(1);
224                        self.pc += 2;
225                    }
226                    _ => println!("Unknown opcode: {:x}", self.opcode),
227                }
228            }
229            0x9000 => {
230                // 9XY0 Skips the next instruction if VX doesn't equal VY.
231                // (Usually the next instruction is a jump to skip a code
232                // block)
233                if self.v[x] != self.v[y] {
234                    self.pc += 2;
235                }
236                self.pc += 2;
237            }
238            0xA000 => {
239                // ANNN set index to NNN position
240                self.index = nnn;
241                self.pc += 2;
242            }
243            0xB000 => {
244                // BNNN Jumps to the address NNN plus V0
245                self.pc = nnn + self.v[0] as u16;
246                // self.pc += 2;
247            }
248            0xC000 => {
249                // CXNN Sets VX to the result of a bitwise and operation on a
250                // random number (Typically: 0 to 255) and NN
251                let mut rng = rand::thread_rng();
252                let r: u8 = rng.gen_range(0, 255);
253                self.v[x] = r & nn;
254                self.pc += 2;
255            }
256            0xD000 => {
257                // DXYN Draws a sprite at coordinate (VX, VY) that has a width
258                // of 8 pixels and a height of N+1 pixels. Each row of 8 pixels
259                // is read as bit-coded starting from memory location I; I
260                // value doesn’t change after the execution of this
261                // instruction. As described above, VF is set to 1 if any
262                // screen pixels are flipped from set to unset when the sprite
263                // is drawn, and to 0 if that doesn’t happen
264                let heigh = self.opcode & 0x000F;
265                let mut pixel: u8;
266                self.v[0xF] = 0;
267                for yline in 0..heigh {
268                    pixel = self.memory[(self.index + yline) as usize];
269                    for xline in 0..8 {
270                        if (pixel & (0x80 >> xline)) != 0 {
271                            let pos = (self.v[x] as u16 + xline) as usize
272                                + (self.v[y] as u16 + yline) as usize * w;
273                            if pos >= 2048 {
274                                break;
275                            }
276                            if self.gfx[pos] == 1 {
277                                self.v[0xF] = 1;
278                            }
279                            self.gfx[pos] ^= 1;
280                        }
281                    }
282                }
283
284                self.draw_flag = true;
285                self.pc += 2;
286            }
287            0xE000 => {
288                match self.opcode & 0x00FF {
289                    0x009E => {
290                        // EX9E Skips the next instruction if the key stored in
291                        // VX is pressed. (Usually the next instruction is a
292                        // jump to skip a code block)
293                        if self.key[self.v[x] as usize] != 0 {
294                            self.pc += 2;
295                        }
296                        self.pc += 2;
297                    }
298                    0x00A1 => {
299                        // EXA1 Skips the next instruction if the key stored in
300                        // VX isn't pressed. (Usually the next instruction is a
301                        // jump to skip a code block)
302                        if self.key[self.v[x] as usize] != 1 {
303                            self.pc += 2;
304                        }
305                        self.pc += 2;
306                    }
307                    _ => println!("Unknown opcode: {:x}", self.opcode),
308                }
309            }
310            0xF000 => {
311                match self.opcode & 0x00FF {
312                    0x0007 => {
313                        // FX07 Sets VX to the value of the delay timer
314                        self.v[x] = self.delay_timer;
315                        self.pc += 2;
316                    }
317                    0x000A => {
318                        // FX0A A key press is awaited, and then stored in VX.
319                        // (Blocking Operation. All instruction halted until
320                        // next key event)
321                        let mut pressed: bool = false;
322                        for i in 0..16 {
323                            if self.key[i] == 1 {
324                                self.v[x] = i as u8;
325                                pressed = true;
326                            }
327                        }
328                        if pressed {
329                            self.pc += 2;
330                        }
331                    }
332                    0x0015 => {
333                        // FX15 Sets the delay timer to VX
334                        self.delay_timer = self.v[x];
335                        self.pc += 2;
336                    }
337                    0x0018 => {
338                        // FX18 Sets the sound timer to VX
339                        self.sound_timer = self.v[x];
340                        self.pc += 2;
341                    }
342                    0x001E => {
343                        // FX1E Adds VX to I. VF is not affected
344                        self.index += self.v[x] as u16;
345                        self.pc += 2;
346                    }
347                    0x0029 => {
348                        // FX29 Sets I to the location of the sprite for the
349                        // character in VX. Characters 0-F (in hexadecimal) are
350                        // represented by a 4x5 font
351                        self.index = self.v[x] as u16 * 5;
352                        self.pc += 2;
353                    }
354                    0x0033 => {
355                        self.memory[self.index as usize] = self.v[x] / 100;
356                        self.memory[self.index as usize + 1] = (self.v[x] / 10) % 10;
357                        self.memory[self.index as usize + 2] = (self.v[x] / 100) % 10;
358                        self.pc += 2;
359                    }
360                    0x0055 => {
361                        // FX55 Stores V0 to VX (including VX) in memory
362                        // starting at address I. The offset from I is
363                        // increased by 1 for each value written, but I itself
364                        // is left unmodified
365                        for i in 0..(x + 1) {
366                            self.memory[self.index as usize + i] = self.v[i];
367                        }
368                        self.pc += 2;
369                    }
370                    0x0065 => {
371                        // 0xFX65 Fills V0 to VX (including VX) with values
372                        // from memory starting at address I. The offset from I
373                        // is increased by 1 for each value written, but I
374                        // itself is left unmodified
375                        for i in 0..(x + 1) {
376                            self.v[i] = self.memory[self.index as usize + i];
377                        }
378                        self.pc += 2;
379                    }
380                    _ => println!("Unknown opcode: {:x}", self.opcode),
381                }
382            }
383            _ => println!("Unknown opcode: {:x}", self.opcode),
384        }
385        if self.delay_timer > 0 {
386            self.delay_timer -= 1;
387        }
388        if self.sound_timer > 0 {
389            if self.sound_timer == 1 {
390                println!("Beep");
391            }
392            self.sound_timer -= 1;
393        }
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400    #[test]
401    fn test_load_game() {
402        let mut c = Chip8::new();
403        c.load_game("Cargo.toml");
404        c.emulate_cycle();
405    }
406}