go-chip8/chip8/chip8.go
chip8/chip8.goRaw
1package chip8
2
3import (
4 "fmt"
5 "io/ioutil"
6 "math/rand"
7)
8
9// W represents the width of the screen
10const W = 64
11
12// H represents the height of the screen
13const H = 32
14
15// Chip8 contains all the data and methods for the Chip8 emulator
16type Chip8 struct {
17 opcode uint16
18 memory [4096]byte
19
20 // register
21 v [16]byte
22
23 index uint16
24 pc uint16
25
26 Gfx [W * H]byte
27
28 delayTimer byte
29 soundTimer byte
30
31 stack [16]uint16
32 sp int
33
34 Key [16]byte
35
36 DrawFlag bool
37}
38
39// Initialize registers and memory
40func NewChip8() Chip8 {
41 c := Chip8{}
42 c.pc = 0x200
43 c.opcode = 0
44 c.index = 0
45 c.sp = 0
46
47 for i := 0; i < len(fontSet); i++ {
48 c.memory[i] = fontSet[i]
49 }
50 return c
51}
52
53// EmulateCycle emulates the chip8 cycle
54func (c *Chip8) EmulateCycle() {
55 // Fetch Opcode
56 c.opcode = uint16(c.memory[c.pc])<<8 | uint16(c.memory[c.pc+1])
57 x := byte((c.opcode & 0x0F00) >> 8)
58 y := byte((c.opcode & 0x00F0) >> 4)
59 nn := byte(c.opcode & 0x00FF)
60 nnn := uint16(c.opcode & 0x0FFF)
61
62 // Decode Opcode
63 // https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
64 switch c.opcode & 0xF000 {
65 case 0x0000:
66 switch c.opcode & 0x000F {
67 case 0x0000:
68 // 00E0 Clear screen
69 for i := 0; i < len(c.Gfx); i++ {
70 c.Gfx[i] = 0
71 }
72 c.pc += 2
73 c.DrawFlag = true
74 case 0x000E:
75 // 00EE Returns from a subroutine
76 c.sp--
77 c.pc = c.stack[c.sp]
78 c.pc += 2
79 default:
80 fmt.Printf("Unknown opcode [0x0000]: 0x%X\n", c.opcode)
81 }
82 case 0x1000:
83 // 1NNN Jumps to address NNN
84 c.pc = nnn
85 case 0x2000:
86 // 2NNN Calls subroutine at NNN
87 c.stack[c.sp] = c.pc
88 c.sp++
89 c.pc = nnn
90 case 0x3000:
91 // 3XNN Skips the next instruction if VX equals NN. (Usually
92 // the next instruction is a jump to skip a code block)
93 if c.v[x] == nn {
94 c.pc += 2
95 }
96 c.pc += 2
97 case 0x4000:
98 // 4XNN Skips the next instruction if VX doesn't equal NN.
99 // (Usually the next instruction is a jump to skip a code
100 // block)
101 if c.v[x] != nn {
102 c.pc += 2
103 }
104 c.pc += 2
105 case 0x5000:
106 // 5XY0 Skips the next instruction if VX equals VY. (Usually
107 // the next instruction is a jump to skip a code block)
108 if c.v[x] == c.v[y] {
109 c.pc += 2
110 }
111 c.pc += 2
112 case 0x6000:
113 // 6XNN Sets VX to NN
114 c.v[x] = nn
115 c.pc += 2
116 case 0x7000:
117 // 7XNN Adds NN to VX. (Carry flag is not changed)
118 c.v[x] += nn
119 c.pc += 2
120 case 0x8000:
121 switch c.opcode & 0x000F {
122 case 0x0000:
123 // 0x8XY0 Sets VX to the value of VY
124 c.v[x] = c.v[y]
125 c.pc += 2
126 case 0x0001:
127 // 0x8XY1 Sets VX to VX or VY. (Bitwise OR operation)
128 c.v[x] = (c.v[x] | c.v[y])
129 c.pc += 2
130 case 0x0002:
131 // 0x8XY2 Sets VX to VX and VY. (Bitwise AND operation)
132 c.v[x] = (c.v[x] & c.v[y])
133 c.pc += 2
134 case 0x0003:
135 // 0x8XY3 Sets VX to VX xor VY
136 c.v[x] = (c.v[x] ^ c.v[y])
137 c.pc += 2
138 case 0x0004:
139 // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a
140 // carry, and to 0 when there isn't
141 if c.v[y] > (0xFF - c.v[x]) {
142 c.v[0xF] = 1
143 } else {
144 c.v[0xF] = 0
145 }
146 c.v[x] += c.v[y]
147 c.pc += 2
148 case 0x0005:
149 // 0x8XY5 VY is subtracted from VX. VF is set to 0 when
150 // there's a borrow, and 1 when there isn't
151 if c.v[x] > c.v[y] {
152 c.v[0xF] = 1
153 } else {
154 c.v[0xF] = 0
155 }
156 c.v[x] -= c.v[y]
157 c.pc += 2
158 case 0x0006:
159 // 0x8XY6 Stores the least significant bit of VX in VF
160 // and then shifts VX to the right by 1
161 if c.opcode&0x1 >= 1 {
162 c.v[0xF] = 1
163 } else {
164 c.v[0xF] = 0
165 }
166 c.v[x] = c.v[x] >> 1
167 c.pc += 2
168 case 0x0007:
169 // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when
170 // there's a borrow, and 1 when there isn't
171 if c.v[y] > c.v[x] {
172 c.v[0xF] = 1
173 } else {
174 c.v[0xF] = 0
175 }
176 c.v[x] = c.v[y] - c.v[x]
177 c.pc += 2
178 case 0x000E:
179 // 0x8XYE Stores the most significant bit of VX in VF
180 // and then shifts VX to the left by 1
181 if c.opcode&0x80 == 0x80 {
182 c.v[0xF] = 1
183 } else {
184 c.v[0xF] = 0
185 }
186 c.v[x] = c.v[x] << 1
187 c.pc += 2
188 default:
189 fmt.Printf("Unknown opcode [0x8000]: 0x%X\n", c.opcode)
190 }
191 case 0x9000:
192 // 9XY0 Skips the next instruction if VX doesn't equal VY.
193 // (Usually the next instruction is a jump to skip a code
194 // block)
195 if c.v[x] != c.v[y] {
196 c.pc += 2
197 }
198 c.pc += 2
199 case 0xA000:
200 // ANNN set index to NNN position
201 c.index = nnn
202 c.pc += 2
203 case 0xB000:
204 // BNNN Jumps to the address NNN plus V0
205 c.pc = nnn + uint16(c.v[0])
206 case 0xC000:
207 // CXNN Sets VX to the result of a bitwise and operation on a
208 // random number (Typically: 0 to 255) and NN
209 r := byte(rand.Intn(255))
210 c.v[x] = r & nn
211 c.pc += 2
212 case 0xD000:
213 // DXYN Draws a sprite at coordinate (VX, VY) that has a width
214 // of 8 pixels and a height of N+1 pixels. Each row of 8 pixels
215 // is read as bit-coded starting from memory location I; I
216 // value doesn’t change after the execution of this
217 // instruction. As described above, VF is set to 1 if any
218 // screen pixels are flipped from set to unset when the sprite
219 // is drawn, and to 0 if that doesn’t happen
220
221 height := c.opcode & 0x000F
222
223 var pixel byte
224 c.v[0xF] = 0
225
226 for yline := uint16(0); yline < height; yline++ {
227 pixel = c.memory[c.index+yline]
228 for xline := uint16(0); xline < 8; xline++ {
229 if (pixel & (0x80 >> xline)) != 0 {
230 pos := (uint16(c.v[x]) + xline) + ((uint16(c.v[y]) + yline) * W)
231 if pos >= 2048 {
232 break
233 }
234 if c.Gfx[pos] == 1 {
235 c.v[0xF] = 1
236 }
237 c.Gfx[pos] ^= 1
238 }
239 }
240
241 }
242
243 c.DrawFlag = true
244 c.pc += 2
245 case 0xE000:
246 switch c.opcode & 0x00FF {
247 case 0x009E:
248 // EX9E Skips the next instruction if the key stored in
249 // VX is pressed. (Usually the next instruction is a
250 // jump to skip a code block)
251 if c.Key[c.v[x]] != 0 {
252 c.pc += 2
253 }
254 c.pc += 2
255 case 0x00A1:
256 // EXA1 Skips the next instruction if the key stored in
257 // VX isn't pressed. (Usually the next instruction is a
258 // jump to skip a code block)
259 if c.Key[c.v[x]] != 1 {
260 c.pc += 2
261 }
262 c.pc += 2
263 default:
264 fmt.Printf("Unknown opcode [0xE000]: 0x%X\n", c.opcode)
265 }
266 case 0xF000:
267 switch c.opcode & 0x00FF {
268 case 0x0007:
269 // FX07 Sets VX to the value of the delay timer
270 c.v[x] = c.delayTimer
271 c.pc += 2
272 case 0x000A:
273 // FX0A A key press is awaited, and then stored in VX.
274 // (Blocking Operation. All instruction halted until
275 // next key event)
276 pressed := false
277 for i := 0; i < 16; i++ {
278 if c.Key[i] == 1 {
279 c.v[x] = byte(i)
280 pressed = true
281 }
282 }
283 if !pressed {
284 return
285 }
286 c.pc += 2
287 case 0x0015:
288 // FX15 Sets the delay timer to VX
289 c.delayTimer = c.v[x]
290 c.pc += 2
291 case 0x0018:
292 // FX18 Sets the sound timer to VX
293 c.soundTimer = c.v[x]
294 c.pc += 2
295 case 0x001E:
296 // FX1E Adds VX to I. VF is not affected
297 c.index += uint16(c.v[x])
298 c.pc += 2
299 case 0x0029:
300 // FX29 Sets I to the location of the sprite for the
301 // character in VX. Characters 0-F (in hexadecimal) are
302 // represented by a 4x5 font
303 c.index = uint16(c.v[x]) * 5
304 c.pc += 2
305 case 0x0033:
306 c.memory[c.index] = c.v[x] / 100
307 c.memory[c.index+1] = (c.v[x] / 10) % 10
308 c.memory[c.index+2] = (c.v[x] / 100) % 10
309 c.pc += 2
310 case 0x0055:
311 // FX55 Stores V0 to VX (including VX) in memory
312 // starting at address I. The offset from I is
313 // increased by 1 for each value written, but I itself
314 // is left unmodified
315 for i := uint16(0); i <= uint16(x); i++ {
316 c.memory[c.index+i] = c.v[i]
317 }
318 c.pc += 2
319 case 0x0065:
320 // 0xFX65 Fills V0 to VX (including VX) with values
321 // from memory starting at address I. The offset from I
322 // is increased by 1 for each value written, but I
323 // itself is left unmodified
324 for i := uint16(0); i <= uint16(x); i++ {
325 c.v[i] = c.memory[c.index+i]
326 }
327 c.pc += 2
328 default:
329 fmt.Printf("Unknown opcode [0xF000]: 0x%X\n", c.opcode)
330 }
331 default:
332 fmt.Printf("Unknown opcode: 0x%X\n", c.opcode)
333 }
334
335 // Update timers
336 if c.delayTimer > 0 {
337 c.delayTimer--
338 }
339 if c.soundTimer > 0 {
340 if c.soundTimer == 1 {
341 fmt.Printf("Beep!\n")
342 }
343 c.soundTimer--
344 }
345}
346
347// LoadGame loads the rom file of the given file path into the Chip8 memory
348func (c *Chip8) LoadGame(filepath string) error {
349 buffer, err := ioutil.ReadFile(filepath)
350 if err != nil {
351 return err
352 }
353
354 for i := 0; i < len(buffer); i++ {
355 // 0x200 == 512
356 c.memory[512+i] = buffer[i]
357 }
358 return nil
359}
360
361var fontSet = [80]byte{
362 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
363 0x20, 0x60, 0x20, 0x20, 0x70, // 1
364 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
365 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
366 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
367 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
368 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
369 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
370 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
371 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
372 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
373 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
374 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
375 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
376 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
377 0xF0, 0x80, 0xF0, 0x80, 0x80, // F
378}