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    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!("{:?} {:?}", 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 as usize];
 99                        self.pc += 2;
100                    }
101                    _ => println!("unk {: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 as usize] = 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] += 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[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[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] >> 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] - 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] << 1;
224                        self.pc += 2;
225                    }
226                    _ => println!("unk {: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                            } else {
279                                self.gfx[pos] ^= 1;
280                            }
281                        }
282                    }
283                }
284
285                self.draw_flag = true;
286                self.pc += 2;
287            }
288            0xE000 => {
289                match self.opcode & 0x00FF {
290                    0x009E => {
291                        // EX9E Skips the next instruction if the key stored in
292                        // VX is pressed. (Usually the next instruction is a
293                        // jump to skip a code block)
294                        if self.key[self.v[x] as usize] != 0 {
295                            self.pc += 2;
296                        }
297                        self.pc += 2;
298                    }
299                    0x00A1 => {
300                        // EXA1 Skips the next instruction if the key stored in
301                        // VX isn't pressed. (Usually the next instruction is a
302                        // jump to skip a code block)
303                        if self.key[self.v[x] as usize] != 1 {
304                            self.pc += 2;
305                        }
306                        self.pc += 2;
307                    }
308                    _ => println!("unk {:x}", self.opcode),
309                }
310            }
311            0xF000 => {
312                match self.opcode & 0x00FF {
313                    0x0007 => {
314                        // FX07 Sets VX to the value of the delay timer
315                        self.v[x] = self.delay_timer;
316                        self.pc += 2;
317                    }
318                    0x000A => {
319                        // FX0A A key press is awaited, and then stored in VX.
320                        // (Blocking Operation. All instruction halted until
321                        // next key event)
322                        let mut pressed: bool = false;
323                        for i in 0..16 {
324                            if self.key[i] == 1 {
325                                self.v[x] = i as u8;
326                                pressed = true;
327                            }
328                        }
329                        if pressed {
330                            self.pc += 2;
331                        }
332                    }
333                    0x0015 => {
334                        // FX15 Sets the delay timer to VX
335                        self.delay_timer = self.v[x];
336                        self.pc += 2;
337                    }
338                    0x0018 => {
339                        // FX18 Sets the sound timer to VX
340                        self.sound_timer = self.v[x];
341                        self.pc += 2;
342                    }
343                    0x001E => {
344                        // FX1E Adds VX to I. VF is not affected
345                        self.index += self.v[x] as u16;
346                        self.pc += 2;
347                    }
348                    0x0029 => {
349                        // FX29 Sets I to the location of the sprite for the
350                        // character in VX. Characters 0-F (in hexadecimal) are
351                        // represented by a 4x5 font
352                        self.index = self.v[x] as u16 * 5;
353                        self.pc += 2;
354                    }
355                    0x0033 => {
356                        self.memory[self.index as usize] = self.v[x] / 100;
357                        self.memory[self.index as usize + 1] = (self.v[x] / 10) % 10;
358                        self.memory[self.index as usize + 2] = (self.v[x] / 100) % 10;
359                        self.pc += 2;
360                    }
361                    0x0055 => {
362                        // FX55 Stores V0 to VX (including VX) in memory
363                        // starting at address I. The offset from I is
364                        // increased by 1 for each value written, but I itself
365                        // is left unmodified
366                        for i in 0..(x + 1) {
367                            self.memory[self.index as usize + i] = self.v[i];
368                        }
369                        self.pc += 2;
370                    }
371                    0x0065 => {
372                        // 0xFX65 Fills V0 to VX (including VX) with values
373                        // from memory starting at address I. The offset from I
374                        // is increased by 1 for each value written, but I
375                        // itself is left unmodified
376                        for i in 0..(x + 1) {
377                            self.v[i] = self.memory[self.index as usize + i];
378                        }
379                        self.pc += 2;
380                    }
381                    _ => println!("unk {:x}", self.opcode),
382                }
383            }
384            _ => println!("opc {:x}", self.opcode),
385        }
386        if self.delay_timer > 0 {
387            self.delay_timer -= 1;
388        }
389        if self.sound_timer > 0 {
390            if self.sound_timer == 1 {
391                println!("Beep");
392            }
393            self.sound_timer -= 1;
394        }
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401    #[test]
402    fn test_load_game() {
403        let mut c = Chip8::new();
404        c.load_game("Cargo.toml");
405        c.emulate_cycle();
406    }
407}