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