You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
4.1 KiB

package main
import (
"flag"
"fmt"
"io/ioutil"
)
type chip8 struct {
opcode uint16
memory [4096]byte
// register
v [16]byte
index uint16
pc uint16
gfx [64 * 32]byte
delayTimer byte
soundTimer byte
stack [16]uint16
sp int
key [16]byte
drawFlag bool
}
// Initialize registers and memory once
func (c *chip8) initialize() {
c.pc = 0x200
c.opcode = 0
c.index = 0
c.sp = 0
}
func (c *chip8) emulateCycle() {
// Fetch Opcode
c.opcode = uint16(c.memory[c.pc])<<8 | uint16(c.memory[c.pc+1])
// Decode Opcode
// https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
// http://www.multigesture.net/wp-content/uploads/mirror/goldroad/chip8_instruction_set.shtml
switch c.opcode & 0xF000 {
case 0x0000:
switch c.opcode & 0x000F {
case 0x0000: // 0x00E0
// clear screen
// TODO
c.pc += 2
break
case 0x000E: // 0x00EE
// TODO
break
default:
fmt.Printf("Unknown opcode [0x0000]: 0x%X\n", c.opcode)
}
case 0x2000:
c.stack[c.sp] = c.pc
c.sp++
c.pc = c.opcode & 0x0FFF
break
case 0x6000:
pos := (c.opcode & 0x0F00) >> 8
c.v[pos] = byte(c.opcode)
c.pc += 2
break
case 0x7000:
c.v[c.opcode&0x0F00>>8] += byte(c.opcode)
c.pc += 2
break
case 0x8000:
switch c.opcode & 0x000F {
case 0x0004: // 0x8XY4
if c.v[(c.opcode&0x00F0)>>4] > (0xFF - c.v[c.opcode&0x0F00]) {
c.v[0xF] = 1
} else {
c.v[0xF] = 0
}
c.v[(c.opcode&0x0F00)>>8] += c.v[(c.opcode&0x00F0)>>4]
c.pc += 2
break
default:
fmt.Printf("Unknown opcode [0x8000]: 0x%X\n", c.opcode)
}
case 0xA000:
// set index to NNN position
c.index = c.opcode & 0x0FFF
c.pc += 2
break
case 0xD000:
x := uint16(c.v[(c.opcode&0x0F00)>>8])
y := uint16(c.v[(c.opcode&0x00F0)>>4])
height := c.opcode & 0x000F
var pixel byte
c.v[0xF] = 0
for yline := uint16(0); yline < height; yline++ {
pixel = c.memory[c.index+yline]
for xline := uint16(0); xline < 8; xline++ {
if (pixel & (0x80 >> xline)) != 0 {
if c.gfx[(x+xline+((y+yline)*64))] == 1 {
c.v[0xF] = 1
}
c.gfx[x+xline+((y+yline)*64)] ^= 1
}
}
}
c.drawFlag = true
c.pc += 2
break
case 0xE000:
switch c.opcode & 0x00FF {
case 0x009E:
if c.key[c.v[(c.opcode&0x0F00)>>8]] != 0 {
c.pc += 4
} else {
c.pc += 2
}
break
}
break
case 0xF000:
x := (c.opcode & 0x0F00) >> 8
fmt.Printf("F: 0x%X\n", c.opcode)
switch c.opcode & 0x00FF {
case 0x0029:
// TODO
// c.index = uint16(c.v[x]) *
c.pc += 2
break
case 0x0033:
c.memory[c.index] = c.v[x] / 100
c.memory[c.index+1] = (c.v[x] / 10) % 10
c.memory[c.index+2] = (c.v[x] / 100) % 10
c.pc += 2
break
case 0x0065:
for i := uint16(0); i < x+1; i++ {
c.v[i] = c.memory[c.index+i]
}
c.pc += 2
break
}
break
default:
fmt.Printf("Unknown opcode: 0x%X\n", c.opcode)
}
// Update timers
if c.delayTimer > 0 {
c.delayTimer--
}
if c.soundTimer > 0 {
if c.soundTimer == 1 {
fmt.Printf("Beep!\n")
}
c.soundTimer--
}
}
func (c *chip8) loadGame(filepath string) error {
buffer, err := ioutil.ReadFile(filepath)
if err != nil {
return err
}
for i := 0; i < len(buffer); i++ {
// 0x200 == 512
c.memory[512+i] = buffer[i]
}
return nil
}
var fontSet = [80]byte{
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80, // F
}
func (c *chip8) drawGraphics() {
}
func (c *chip8) setKeys() {
}
func main() {
filepath := flag.String("file", "file-path", "file path of the input file")
flag.Parse()
// setupGraphics()
// setupInput()
var c chip8
c.initialize()
err := c.loadGame(*filepath)
if err != nil {
panic(err)
}
for {
c.emulateCycle()
if c.drawFlag {
c.drawGraphics()
c.setKeys()
}
}
}