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}