arnaucube
arnaucube/go-chip8Public Self-hosted repository
go-chip8/main.go
main.goRaw
  1package main
  2
  3import (
  4	"flag"
  5	"fmt"
  6	"io/ioutil"
  7)
  8
  9type chip8 struct {
 10	opcode uint16
 11	memory [4096]byte
 12
 13	// register
 14	v [16]byte
 15
 16	index uint16
 17	pc    uint16
 18
 19	gfx [64 * 32]byte
 20
 21	delayTimer byte
 22	soundTimer byte
 23
 24	stack [16]uint16
 25	sp    int
 26
 27	key [16]byte
 28
 29	drawFlag bool
 30}
 31
 32// Initialize registers and memory once
 33func (c *chip8) initialize() {
 34	c.pc = 0x200
 35	c.opcode = 0
 36	c.index = 0
 37	c.sp = 0
 38}
 39
 40func (c *chip8) emulateCycle() {
 41	// Fetch Opcode
 42	c.opcode = uint16(c.memory[c.pc])<<8 | uint16(c.memory[c.pc+1])
 43
 44	// Decode Opcode
 45	// https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
 46	// http://www.multigesture.net/wp-content/uploads/mirror/goldroad/chip8_instruction_set.shtml
 47	switch c.opcode & 0xF000 {
 48	case 0x0000:
 49		switch c.opcode & 0x000F {
 50		case 0x0000: // 0x00E0
 51			// clear screen
 52			// TODO
 53			c.pc += 2
 54			break
 55		case 0x000E: // 0x00EE
 56			// TODO
 57			break
 58		default:
 59			fmt.Printf("Unknown opcode [0x0000]: 0x%X\n", c.opcode)
 60		}
 61	case 0x2000:
 62		c.stack[c.sp] = c.pc
 63		c.sp++
 64		c.pc = c.opcode & 0x0FFF
 65		break
 66	case 0x6000:
 67		pos := (c.opcode & 0x0F00) >> 8
 68		c.v[pos] = byte(c.opcode)
 69		c.pc += 2
 70		break
 71	case 0x7000:
 72		c.v[c.opcode&0x0F00>>8] += byte(c.opcode)
 73		c.pc += 2
 74		break
 75	case 0x8000:
 76		switch c.opcode & 0x000F {
 77		case 0x0004: // 0x8XY4
 78			if c.v[(c.opcode&0x00F0)>>4] > (0xFF - c.v[c.opcode&0x0F00]) {
 79				c.v[0xF] = 1
 80			} else {
 81				c.v[0xF] = 0
 82			}
 83			c.v[(c.opcode&0x0F00)>>8] += c.v[(c.opcode&0x00F0)>>4]
 84			c.pc += 2
 85			break
 86		default:
 87			fmt.Printf("Unknown opcode [0x8000]: 0x%X\n", c.opcode)
 88		}
 89	case 0xA000:
 90		// set index to NNN position
 91		c.index = c.opcode & 0x0FFF
 92		c.pc += 2
 93		break
 94	case 0xD000:
 95		x := uint16(c.v[(c.opcode&0x0F00)>>8])
 96		y := uint16(c.v[(c.opcode&0x00F0)>>4])
 97		height := c.opcode & 0x000F
 98
 99		var pixel byte
100		c.v[0xF] = 0
101		for yline := uint16(0); yline < height; yline++ {
102			pixel = c.memory[c.index+yline]
103			for xline := uint16(0); xline < 8; xline++ {
104				if (pixel & (0x80 >> xline)) != 0 {
105					if c.gfx[(x+xline+((y+yline)*64))] == 1 {
106						c.v[0xF] = 1
107					}
108					c.gfx[x+xline+((y+yline)*64)] ^= 1
109				}
110			}
111
112		}
113		c.drawFlag = true
114		c.pc += 2
115		break
116	case 0xE000:
117		switch c.opcode & 0x00FF {
118		case 0x009E:
119			if c.key[c.v[(c.opcode&0x0F00)>>8]] != 0 {
120				c.pc += 4
121			} else {
122				c.pc += 2
123			}
124			break
125		}
126		break
127	case 0xF000:
128		x := (c.opcode & 0x0F00) >> 8
129		fmt.Printf("F: 0x%X\n", c.opcode)
130		switch c.opcode & 0x00FF {
131		case 0x0029:
132			// TODO
133			// c.index = uint16(c.v[x]) *
134			c.pc += 2
135			break
136		case 0x0033:
137			c.memory[c.index] = c.v[x] / 100
138			c.memory[c.index+1] = (c.v[x] / 10) % 10
139			c.memory[c.index+2] = (c.v[x] / 100) % 10
140			c.pc += 2
141			break
142		case 0x0065:
143			for i := uint16(0); i < x+1; i++ {
144				c.v[i] = c.memory[c.index+i]
145			}
146			c.pc += 2
147			break
148		}
149		break
150	default:
151		fmt.Printf("Unknown opcode: 0x%X\n", c.opcode)
152	}
153
154	// Update timers
155	if c.delayTimer > 0 {
156		c.delayTimer--
157	}
158	if c.soundTimer > 0 {
159		if c.soundTimer == 1 {
160			fmt.Printf("Beep!\n")
161		}
162		c.soundTimer--
163	}
164}
165
166func (c *chip8) loadGame(filepath string) error {
167	buffer, err := ioutil.ReadFile(filepath)
168	if err != nil {
169		return err
170	}
171
172	for i := 0; i < len(buffer); i++ {
173		// 0x200 == 512
174		c.memory[512+i] = buffer[i]
175	}
176	return nil
177}
178
179var fontSet = [80]byte{
180	0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
181	0x20, 0x60, 0x20, 0x20, 0x70, // 1
182	0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
183	0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
184	0x90, 0x90, 0xF0, 0x10, 0x10, // 4
185	0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
186	0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
187	0xF0, 0x10, 0x20, 0x40, 0x40, // 7
188	0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
189	0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
190	0xF0, 0x90, 0xF0, 0x90, 0x90, // A
191	0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
192	0xF0, 0x80, 0x80, 0x80, 0xF0, // C
193	0xE0, 0x90, 0x90, 0x90, 0xE0, // D
194	0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
195	0xF0, 0x80, 0xF0, 0x80, 0x80, // F
196}
197
198func (c *chip8) drawGraphics() {
199
200}
201
202func (c *chip8) setKeys() {
203
204}
205
206func main() {
207	filepath := flag.String("file", "file-path", "file path of the input file")
208
209	flag.Parse()
210
211	// setupGraphics()
212	// setupInput()
213
214	var c chip8
215	c.initialize()
216	err := c.loadGame(*filepath)
217	if err != nil {
218		panic(err)
219	}
220
221	for {
222		c.emulateCycle()
223		if c.drawFlag {
224			c.drawGraphics()
225			c.setKeys()
226		}
227	}
228}