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    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                // TODO
150            }
151            0x9000 => {
152                // 9XY0 Skips the next instruction if VX doesn't equal VY.
153                // (Usually the next instruction is a jump to skip a code
154                // block)
155                if self.v[x] != self.v[y] {
156                    self.pc += 2;
157                }
158                self.pc += 2;
159            }
160            0xA000 => {
161                // ANNN set index to NNN position
162                self.index = nnn;
163                self.pc += 2;
164            }
165            0xB000 => {
166                // BNNN Jumps to the address NNN plus V0
167                self.pc = nnn + self.v[0] as u16;
168                self.pc += 2;
169            }
170            0xC000 => {
171                // CXNN Sets VX to the result of a bitwise and operation on a
172                // random number (Typically: 0 to 255) and NN
173                let mut rng = rand::thread_rng();
174                let r: u8 = rng.gen_range(0, 255);
175                self.v[x] = r & nn;
176                self.pc += 2;
177            }
178            0xD000 => {
179                // TODO
180
181                self.draw_flag = true;
182                self.pc += 2;
183            }
184            0xE000 => {
185                match self.opcode & 0x00FF {
186                    0x009E => {
187                        // EX9E Skips the next instruction if the key stored in
188                        // VX is pressed. (Usually the next instruction is a
189                        // jump to skip a code block)
190                        if self.key[self.v[x] as usize] != 0 {
191                            self.pc += 2;
192                        }
193                        self.pc += 2;
194                    }
195                    0x00A1 => {
196                        // EXA1 Skips the next instruction if the key stored in
197                        // VX isn't pressed. (Usually the next instruction is a
198                        // jump to skip a code block)
199                        if self.key[self.v[x] as usize] != 1 {
200                            self.pc += 2;
201                        }
202                        self.pc += 2;
203                    }
204                    _ => println!("unk {:x}", self.opcode),
205                }
206            }
207            0xF000 => {
208                match self.opcode & 0x00FF {
209                    0x0007 => {
210                        // FX07 Sets VX to the value of the delay timer
211                        self.v[x] = self.delay_timer;
212                        self.pc += 2;
213                    }
214                    0x000A => {
215                        // FX0A A key press is awaited, and then stored in VX.
216                        // (Blocking Operation. All instruction halted until
217                        // next key event)
218                        let mut pressed: bool = false;
219                        for i in 0..16 {
220                            if self.key[i] == 1 {
221                                self.v[x] = i as u8;
222                                pressed = true;
223                            }
224                        }
225                        if pressed {
226                            self.pc += 2;
227                        }
228                    }
229                    0x0015 => {
230                        // FX15 Sets the delay timer to VX
231                        self.delay_timer = self.v[x];
232                        self.pc += 2;
233                    }
234                    0x0018 => {
235                        // FX18 Sets the sound timer to VX
236                        self.sound_timer = self.v[x];
237                        self.pc += 2;
238                    }
239                    0x001E => {
240                        // FX1E Adds VX to I. VF is not affected
241                        self.index += self.v[x] as u16;
242                        self.pc += 2;
243                    }
244                    0x0029 => {
245                        // FX29 Sets I to the location of the sprite for the
246                        // character in VX. Characters 0-F (in hexadecimal) are
247                        // represented by a 4x5 font
248                        self.index = self.v[x] as u16 * 5;
249                        self.pc += 2;
250                    }
251                    0x0033 => {
252                        self.memory[self.index as usize] = self.v[x] / 100;
253                        self.memory[self.index as usize + 1] = (self.v[x] / 10) % 10;
254                        self.memory[self.index as usize + 2] = (self.v[x] / 100) % 10;
255                        self.pc += 2;
256                    }
257                    0x0055 => {
258                        // FX55 Stores V0 to VX (including VX) in memory
259                        // starting at address I. The offset from I is
260                        // increased by 1 for each value written, but I itself
261                        // is left unmodified
262                        for i in 0..(x + 1) {
263                            self.memory[self.index as usize + i] = self.v[i];
264                        }
265                        self.pc += 2;
266                    }
267                    0x0064 => {
268                        // 0xFX65 Fills V0 to VX (including VX) with values
269                        // from memory starting at address I. The offset from I
270                        // is increased by 1 for each value written, but I
271                        // itself is left unmodified
272                        for i in 0..(x + 1) {
273                            self.v[i] = self.memory[self.index as usize + i];
274                        }
275                        self.pc += 2;
276                    }
277                    _ => println!("unk {:x}", self.opcode),
278                }
279            }
280            _ => println!("opc {:x}", self.opcode),
281        }
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288    #[test]
289    fn test_load_game() {
290        let mut c = Chip8::new();
291        c.load_game("Cargo.toml");
292        c.emulate_cycle();
293    }
294}