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}