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: isize,
 19    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
 81        // Decode Opcode
 82        // https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
 83        match self.opcode & 0xF000 {
 84            0x0000 => {
 85                match self.opcode & 0x000F {
 86                    0x0000 => {
 87                        // 00E0 Clear screen
 88                        for i in 0..self.gfx.len() {
 89                            self.gfx[i] = 0;
 90                        }
 91                        self.pc += 2;
 92                        self.draw_flag = true;
 93                    }
 94                    0x000E => {
 95                        // 00EE Returns from a subroutine
 96                        self.sp -= 1;
 97                        self.pc = self.stack[self.sp as usize];
 98                        self.pc += 2;
 99                    }
100                    _ => println!("unk {:x}", self.opcode),
101                }
102            }
103            0x1000 => {
104                // 1NNN Jumps to address NNN
105                self.pc = nnn;
106            }
107            0x2000 => {
108                // 2NNN Calls subroutine at NNN
109                self.stack[self.sp as usize] = self.pc;
110                self.sp += 1;
111                self.pc = nnn;
112            }
113            0x3000 => {
114                // 3XNN Skips the next instruction if VX equals NN. (Usually
115                // the next instruction is a jump to skip a code block)
116                if self.v[x] == nn {
117                    self.pc += 2;
118                }
119                self.pc += 2;
120            }
121            0x4000 => {
122                // 4XNN Skips the next instruction if VX doesn't equal NN.
123                // (Usually the next instruction is a jump to skip a code
124                // block)
125                if self.v[x] != nn {
126                    self.pc += 2;
127                }
128                self.pc += 2;
129            }
130            0x5000 => {
131                // 5XY0 Skips the next instruction if VX equals VY. (Usually
132                // the next instruction is a jump to skip a code block)
133                if self.v[x] == self.v[y] {
134                    self.pc += 2;
135                }
136                self.pc += 2;
137            }
138            0x6000 => {
139                // 6XNN Sets VX to NN
140                self.v[x] = nn;
141                self.pc += 2;
142            }
143            0x7000 => {
144                // 7XNN Adds NN to VX. (Carry flag is not changed)
145                self.v[x] += nn;
146                self.pc += 2;
147            }
148            0x8000 => {
149                match self.opcode & 0x000F {
150                    0x0000 => {
151                        // 0x8XY0 Sets VX to the value of VY
152                        self.v[x] = self.v[y];
153                        self.pc += 2;
154                    }
155                    0x0001 => {
156                        // 0x8XY1 Sets VX to VX or VY. (Bitwise OR operation)
157                        self.v[x] = (self.v[x] | self.v[y]);
158                        self.pc += 2;
159                    }
160                    0x0002 => {
161                        // 0x8XY2 Sets VX to VX and VY. (Bitwise AND operation)
162                        self.v[x] = (self.v[x] & self.v[y]);
163                        self.pc += 2;
164                    }
165                    0x0003 => {
166                        // 0x8XY3 Sets VX to VX xor VY
167                        self.v[x] = (self.v[x] ^ self.v[y]);
168                        self.pc += 2;
169                    }
170                    0x0004 => {
171                        // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a
172                        // carry, and to 0 when there isn't
173                        if self.v[y] > (0xFF - self.v[x]) {
174                            self.v[0xF] = 1;
175                        } else {
176                            self.v[0xF] = 0;
177                        }
178                        self.v[x] += self.v[y];
179                        self.pc += 2;
180                    }
181                    0x0005 => {
182                        // 0x8XY5 VY is subtracted from VX. VF is set to 0 when
183                        // there's a borrow, and 1 when there isn't
184                        if self.v[x] > self.v[y] {
185                            self.v[0xF] = 1;
186                        } else {
187                            self.v[0xF] = 0;
188                        }
189                        self.v[x] -= self.v[y];
190                        self.pc += 2;
191                    }
192                    0x0006 => {
193                        // 0x8XY6 Stores the least significant bit of VX in VF
194                        // and then shifts VX to the right by 1
195                        if self.opcode & 0x1 >= 1 {
196                            self.v[0xF] = 1;
197                        } else {
198                            self.v[0xF] = 0;
199                        }
200                        self.v[x] = self.v[x] >> 1;
201                        self.pc += 2;
202                    }
203                    0x0007 => {
204                        // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when
205                        // there's a borrow, and 1 when there isn't
206                        if self.v[y] > self.v[x] {
207                            self.v[0xF] = 1;
208                        } else {
209                            self.v[0xF] = 0;
210                        }
211                        self.v[x] = self.v[y] - self.v[x];
212                        self.pc += 2;
213                    }
214                    0x000E => {
215                        // 0x8XYE Stores the most significant bit of VX in VF
216                        // and then shifts VX to the left by 1
217                        if self.opcode & 0x80 == 0x80 {
218                            self.v[0xF] = 1;
219                        } else {
220                            self.v[0xF] = 0;
221                        }
222                        self.v[x] = self.v[x] << 1;
223                        self.pc += 2;
224                    }
225                    _ => println!("unk {:x}", self.opcode),
226                }
227            }
228            0x9000 => {
229                // 9XY0 Skips the next instruction if VX doesn't equal VY.
230                // (Usually the next instruction is a jump to skip a code
231                // block)
232                if self.v[x] != self.v[y] {
233                    self.pc += 2;
234                }
235                self.pc += 2;
236            }
237            0xA000 => {
238                // ANNN set index to NNN position
239                self.index = nnn;
240                self.pc += 2;
241            }
242            0xB000 => {
243                // BNNN Jumps to the address NNN plus V0
244                self.pc = nnn + self.v[0] as u16;
245                self.pc += 2;
246            }
247            0xC000 => {
248                // CXNN Sets VX to the result of a bitwise and operation on a
249                // random number (Typically: 0 to 255) and NN
250                let mut rng = rand::thread_rng();
251                let r: u8 = rng.gen_range(0, 255);
252                self.v[x] = r & nn;
253                self.pc += 2;
254            }
255            0xD000 => {
256                // DXYN Draws a sprite at coordinate (VX, VY) that has a width
257                // of 8 pixels and a height of N+1 pixels. Each row of 8 pixels
258                // is read as bit-coded starting from memory location I; I
259                // value doesn’t change after the execution of this
260                // instruction. As described above, VF is set to 1 if any
261                // screen pixels are flipped from set to unset when the sprite
262                // is drawn, and to 0 if that doesn’t happen
263                let heigh = self.opcode & 0x000F;
264                let mut pixel: u8;
265                self.v[0xF] = 0;
266                for yline in 0..heigh {
267                    pixel = self.memory[(self.index + yline) as usize];
268                    for xline in 0..8 {
269                        if (pixel & (0x80 >> xline)) != 0 {
270                            let pos = (self.v[x] as u16 + xline) as usize
271                                + (self.v[y] as u16 + yline) as usize * w;
272                            if pos >= 2048 {
273                                break;
274                            }
275                            if self.gfx[pos] == 1 {
276                                self.v[0xF] = 1;
277                            } else {
278                                self.v[0xF] ^= 1;
279                            }
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!("unk {: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                    0x0064 => {
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!("unk {:x}", self.opcode),
381                }
382            }
383            _ => println!("opc {:x}", self.opcode),
384        }
385    }
386}
387
388#[cfg(test)]
389mod tests {
390    use super::*;
391    #[test]
392    fn test_load_game() {
393        let mut c = Chip8::new();
394        c.load_game("Cargo.toml");
395        c.emulate_cycle();
396    }
397}