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}