emulator finally working 🥳
This commit is contained in:
parent
6f96020c61
commit
c3bc952330
|
@ -3,12 +3,11 @@ package chip8
|
|||
import (
|
||||
"encoding/binary"
|
||||
"image"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
MemorySize = 4096 // 4KB of memory
|
||||
InstructionSize = 2
|
||||
InstructionSize = 2 // 2 bytes long instructions
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -21,22 +20,31 @@ const (
|
|||
FPS = 60
|
||||
)
|
||||
|
||||
type Emulator struct {
|
||||
V [16]uint8 // general registers
|
||||
I uint16 // address register
|
||||
PC uint16 // program counter
|
||||
DT uint8 // delay timer
|
||||
ST uint8 // sound timer
|
||||
Stack *Stack // very simple stack
|
||||
Memory [MemorySize]uint8 // 4KB of system RAM
|
||||
ROM []uint8 // game rom
|
||||
Display *image.Gray // display buffer
|
||||
type KeyPressed func(key uint8) bool
|
||||
|
||||
type ROM struct {
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func NewEmulator() *Emulator {
|
||||
type Emulator struct {
|
||||
V [16]uint8 // general registers
|
||||
I uint16 // address register
|
||||
PC uint16 // program counter
|
||||
DT uint8 // delay timer
|
||||
ST uint8 // sound timer
|
||||
Stack *Stack // very simple stack
|
||||
Memory [MemorySize]uint8 // 4KB of system RAM
|
||||
ROM ROM // game rom
|
||||
Display *image.RGBA // display buffer
|
||||
KeyPressed KeyPressed // input function
|
||||
}
|
||||
|
||||
func NewEmulator(keyPressed KeyPressed) *Emulator {
|
||||
emulator := new(Emulator)
|
||||
emulator.KeyPressed = keyPressed
|
||||
emulator.Stack = NewStack()
|
||||
emulator.Display = image.NewGray(image.Rect(0, 0, Width, Height))
|
||||
emulator.Display = image.NewRGBA(image.Rect(0, 0, Width, Height))
|
||||
emulator.Reset()
|
||||
return emulator
|
||||
}
|
||||
|
@ -54,10 +62,9 @@ func (emulator *Emulator) Reset() {
|
|||
emulator.LoadFont()
|
||||
}
|
||||
|
||||
func (emulator *Emulator) LoadROM(rom []uint8) {
|
||||
func (emulator *Emulator) LoadROM(rom ROM) {
|
||||
emulator.ROM = rom
|
||||
// load rom on memory
|
||||
copy(emulator.Memory[ProgramAddress:], emulator.ROM)
|
||||
copy(emulator.Memory[ProgramAddress:], emulator.ROM.Data)
|
||||
}
|
||||
|
||||
func (emulator *Emulator) LoadFont() {
|
||||
|
@ -81,11 +88,18 @@ func (emulator *Emulator) LoadFont() {
|
|||
})
|
||||
}
|
||||
|
||||
func (emulator *Emulator) Cycle() {
|
||||
emulator.DT = uint8(math.Max(0, float64(emulator.DT)-1))
|
||||
emulator.ST = uint8(math.Max(0, float64(emulator.ST)-1))
|
||||
func (emulator *Emulator) UpdateTimers() {
|
||||
if emulator.DT > 0 {
|
||||
emulator.DT -= 1
|
||||
}
|
||||
if emulator.ST > 0 {
|
||||
emulator.ST -= 1
|
||||
}
|
||||
}
|
||||
|
||||
func (emulator *Emulator) Cycle() {
|
||||
instruction := binary.BigEndian.Uint16(emulator.Memory[emulator.PC:])
|
||||
emulator.PC += InstructionSize
|
||||
|
||||
nnn := instruction & 0x0FFF
|
||||
n := uint8(instruction & 0x000F)
|
||||
|
@ -93,20 +107,18 @@ func (emulator *Emulator) Cycle() {
|
|||
x := uint8(instruction & 0x0F00 >> 8)
|
||||
y := uint8(instruction & 0x00F0 >> 4)
|
||||
|
||||
switch instruction & 0xF000 >> 12 {
|
||||
switch instruction >> 12 & 0xF {
|
||||
case 0x0:
|
||||
switch instruction & 0x00FF {
|
||||
case 0xE0: // 00E0 - CLS
|
||||
switch instruction {
|
||||
case 0x00E0: // 00E0 - CLS
|
||||
emulator.ClearScreen()
|
||||
case 0xEE: // 00EE - RET
|
||||
case 0x00EE: // 00EE - RET
|
||||
emulator.Return()
|
||||
}
|
||||
case 0x1: // 1nnn - JP addr
|
||||
emulator.Jump(nnn)
|
||||
return // skip PC increment
|
||||
case 0x2: // 2nnn - CALL addr
|
||||
emulator.Call(nnn)
|
||||
return // skip PC increment
|
||||
case 0x3: // 3xkk - SE Vx, byte
|
||||
emulator.SkipEqualByte(x, kk)
|
||||
case 0x4: // 4xkk - SNE Vx, byte
|
||||
|
@ -177,6 +189,4 @@ func (emulator *Emulator) Cycle() {
|
|||
emulator.ReadRegisters(x)
|
||||
}
|
||||
}
|
||||
|
||||
emulator.PC += InstructionSize
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func TestEmulator_Reset(t *testing.T) {
|
||||
emulator := chip8.NewEmulator()
|
||||
emulator := chip8.NewEmulator(nil)
|
||||
emulator.V[0x03] = 0xFF
|
||||
emulator.V[0x0F] = 0xBB
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
_ "image/png"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
|
@ -125,7 +124,6 @@ func (emulator *Emulator) Add(x uint8, y uint8) {
|
|||
emulator.V[0xF] = uint8(sum >> 8)
|
||||
}
|
||||
|
||||
// SUB Vx, Vy
|
||||
// Set Vx = Vx - Vy, set VF = NOT borrow.
|
||||
//
|
||||
// If Vx > Vy, then VF is set to 1, otherwise 0.
|
||||
|
@ -140,7 +138,7 @@ func (emulator *Emulator) Sub(x uint8, y uint8) {
|
|||
// If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0.
|
||||
// Then Vx is divided by 2.
|
||||
func (emulator *Emulator) ShiftRight(x uint8) {
|
||||
emulator.V[0xF] = emulator.V[x] & 0b0000_0001
|
||||
emulator.V[0xF] = emulator.V[x] & 0b00000001
|
||||
emulator.V[x] >>= 1
|
||||
}
|
||||
|
||||
|
@ -158,7 +156,7 @@ func (emulator *Emulator) SubN(x uint8, y uint8) {
|
|||
// If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0.
|
||||
// Then Vx is multiplied by 2.
|
||||
func (emulator *Emulator) ShiftLeft(x uint8) {
|
||||
emulator.V[0xF] = emulator.V[x] & 0b1000_0000
|
||||
emulator.V[0xF] = (emulator.V[x] & 0b10000000) >> 7
|
||||
emulator.V[x] <<= 1
|
||||
}
|
||||
|
||||
|
@ -202,29 +200,27 @@ func (emulator *Emulator) Random(x uint8, kk uint8) {
|
|||
// VF is set to 1, otherwise it is set to 0. If the sprite is positioned so part of it
|
||||
// is outside the coordinates of the display, it wraps around to the opposite side of the screen.
|
||||
func (emulator *Emulator) Draw(x uint8, y uint8, n uint8) {
|
||||
x = emulator.V[x]
|
||||
y = emulator.V[y]
|
||||
width := uint8(8)
|
||||
height := n
|
||||
|
||||
emulator.V[0xF] = 0x00 // clean collision flag
|
||||
|
||||
for row := uint8(0); row < height; row += 1 {
|
||||
sprite := emulator.Memory[emulator.I+uint16(row)]
|
||||
for yline := uint8(0); yline < height; yline++ {
|
||||
sprite := emulator.Memory[emulator.I+uint16(yline)]
|
||||
|
||||
for col := uint8(0); col < width; col += 1 {
|
||||
if (sprite & 0b10000000) == 0b10000000 {
|
||||
px, py := int(emulator.V[x]+col), int(emulator.V[y]+row)
|
||||
for xline := uint8(0); xline < width; xline++ {
|
||||
if (sprite & 0b10000000) != 0x00 {
|
||||
px, py := int(x+xline)%Width, int(y+yline)%Height
|
||||
color := emulator.Display.PixOffset(px, py) + 1 // color offset: 0:red, 1:green, 2:blue
|
||||
|
||||
// check for pixel collision
|
||||
if emulator.Display.At(px, py) != color.Black {
|
||||
emulator.V[0xF] = 0x01 // set collision flag
|
||||
if emulator.Display.Pix[color] != 0x00 {
|
||||
emulator.V[0xF] = 0x01 // collision
|
||||
}
|
||||
|
||||
// draw pixel
|
||||
emulator.Display.Set(px, py, color.White)
|
||||
emulator.Display.Pix[color] ^= 0xFF
|
||||
}
|
||||
|
||||
// shift the sprite left 1
|
||||
// this will move the next next col/bit of the sprite into the first position
|
||||
sprite <<= 1
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +231,9 @@ func (emulator *Emulator) Draw(x uint8, y uint8, n uint8) {
|
|||
// Checks the keyboard, and if the key corresponding to the value of Vx
|
||||
// is currently in the down position, PC is increased by 2.
|
||||
func (emulator *Emulator) SkipKeyPressed(x uint8) {
|
||||
// TODO: implement input
|
||||
if emulator.KeyPressed(emulator.V[x]) {
|
||||
emulator.PC += InstructionSize
|
||||
}
|
||||
}
|
||||
|
||||
// Skip next instruction if key with the value of Vx is not pressed.
|
||||
|
@ -243,7 +241,9 @@ func (emulator *Emulator) SkipKeyPressed(x uint8) {
|
|||
// Checks the keyboard, and if the key corresponding to the value of Vx
|
||||
// is currently in the up position, PC is increased by 2.
|
||||
func (emulator *Emulator) SkipKeyNotPressed(x uint8) {
|
||||
// TODO: implement input
|
||||
if !emulator.KeyPressed(emulator.V[x]) {
|
||||
emulator.PC += InstructionSize
|
||||
}
|
||||
}
|
||||
|
||||
// Set Vx = delay timer value.
|
||||
|
@ -257,7 +257,14 @@ func (emulator *Emulator) ReadDT(x uint8) {
|
|||
//
|
||||
// All execution stops until a key is pressed, then the value of that key is stored in Vx.
|
||||
func (emulator *Emulator) ReadKey(x uint8) {
|
||||
// TODO: implement input
|
||||
for {
|
||||
for key := uint8(0); key < 16; key++ {
|
||||
if emulator.KeyPressed(key) {
|
||||
emulator.V[x] = key
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set delay timer = Vx.
|
||||
|
@ -286,7 +293,7 @@ func (emulator *Emulator) AddI(x uint8) {
|
|||
// The value of I is set to the location for the hexadecimal sprite
|
||||
// corresponding to the value of Vx.
|
||||
func (emulator *Emulator) SetI(x uint8) {
|
||||
emulator.I += uint16(emulator.V[x]) * 5
|
||||
emulator.I = uint16(emulator.V[x]) * 5
|
||||
}
|
||||
|
||||
// Store BCD representation of Vx in memory locations I, I+1, and I+2.
|
||||
|
@ -303,16 +310,12 @@ func (emulator *Emulator) LoadBCD(x uint8) {
|
|||
//
|
||||
// The interpreter copies the values of registers V0 through Vx into memory, starting at the address in I.
|
||||
func (emulator *Emulator) StoreRegisters(x uint8) {
|
||||
for i := uint8(0); i <= x; i++ {
|
||||
emulator.Memory[emulator.I+uint16(i)] = emulator.V[i]
|
||||
}
|
||||
copy(emulator.Memory[emulator.I:], emulator.V[:x+1])
|
||||
}
|
||||
|
||||
// Read registers V0 through Vx from memory starting at location I.
|
||||
//
|
||||
// The interpreter reads values from memory starting at location I into registers V0 through Vx.
|
||||
func (emulator *Emulator) ReadRegisters(x uint8) {
|
||||
for i := uint8(0); i <= x; i++ {
|
||||
emulator.V[i] = emulator.Memory[emulator.I+uint16(i)]
|
||||
}
|
||||
copy(emulator.V[0:], emulator.Memory[emulator.I:emulator.I+uint16(x)+1])
|
||||
}
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/tangzero/chip8-emulator/chip8"
|
||||
)
|
||||
|
||||
const (
|
||||
ScreenScale = 20
|
||||
Width = chip8.Width * ScreenScale
|
||||
Height = chip8.Height * ScreenScale
|
||||
)
|
||||
|
||||
type UI struct {
|
||||
Emulator *chip8.Emulator
|
||||
}
|
||||
|
||||
func (ui *UI) Update(screen *ebiten.Image) error {
|
||||
ui.Emulator.Cycle()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ui *UI) Draw(screen *ebiten.Image) {
|
||||
frame, err := ebiten.NewImageFromImage(ui.Emulator.Display, ebiten.FilterDefault)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
op := new(ebiten.DrawImageOptions)
|
||||
op.GeoM.Scale(ScreenScale, ScreenScale)
|
||||
|
||||
screen.DrawImage(frame, op)
|
||||
}
|
||||
|
||||
func (ui *UI) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return Width, Height
|
||||
}
|
||||
|
||||
func main() {
|
||||
rom, err := ioutil.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ui := UI{Emulator: chip8.NewEmulator()}
|
||||
ui.Emulator.LoadROM(rom)
|
||||
|
||||
ebiten.SetWindowSize(Width, Height)
|
||||
ebiten.SetWindowTitle("CHIP 8")
|
||||
if err := ebiten.RunGame(&ui); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
10
go.mod
10
go.mod
|
@ -2,18 +2,22 @@ module github.com/tangzero/chip8-emulator
|
|||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
|
||||
github.com/hajimehoshi/ebiten v1.12.12 // indirect
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.5 // indirect
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
|
28
go.sum
28
go.sum
|
@ -1,34 +1,24 @@
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/hajimehoshi/bitmapfont v1.3.0/go.mod h1:/Qb7yVjHYNUV4JdqNkPs6BSZwLjKqkZOMIp6jZD0KgE=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
|
||||
github.com/hajimehoshi/ebiten v1.12.12 h1:JvmF1bXRa+t+/CcLWxrJCRsdjs2GyBYBSiFAfIqDFlI=
|
||||
github.com/hajimehoshi/ebiten v1.12.12/go.mod h1:1XI25ImVCDPJiXox4h9yK/CvN5sjDYnbF4oZcFzPXHw=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.5 h1:i6NdS6pEi5kgfTh+4XAVCVtCXxjTyxzU1cj1oqHWkZQ=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.5/go.mod h1:olKl/qqhMBBAm2oI7Zy292nCtE+nitlmYKNF3UpbFn0=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.1/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto v0.6.8/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
|
||||
github.com/jakecoffman/cp v1.0.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
|
||||
github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4=
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4=
|
||||
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
|
||||
github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
|
||||
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
||||
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -36,48 +26,37 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 h1:peBP2oZO/xVnGMaWMCyFEI0WENsGj71wx5K12mRELHQ=
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -92,14 +71,13 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -16,7 +16,7 @@ var emulator *chip8.Emulator
|
|||
|
||||
//export Initialize
|
||||
func Initialize() {
|
||||
emulator = chip8.NewEmulator()
|
||||
emulator = chip8.NewEmulator(nil)
|
||||
}
|
||||
|
||||
//export Deinitialize
|
||||
|
|
107
main.go
Normal file
107
main.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/tangzero/chip8-emulator/chip8"
|
||||
)
|
||||
|
||||
//go:embed test_opcode.ch8
|
||||
var DefaultROM []byte
|
||||
|
||||
const (
|
||||
ScreenScale = 20
|
||||
Width = chip8.Width * ScreenScale
|
||||
Height = chip8.Height * ScreenScale
|
||||
)
|
||||
|
||||
type State = int
|
||||
|
||||
const (
|
||||
LoadingState State = iota
|
||||
RunningState
|
||||
)
|
||||
|
||||
// Keyboard Layout
|
||||
// 1 2 3 C
|
||||
// 4 5 6 D
|
||||
// 7 8 9 E
|
||||
// A 0 B F
|
||||
var KeyMapping = []ebiten.Key{
|
||||
ebiten.KeyX,
|
||||
ebiten.Key1,
|
||||
ebiten.Key2,
|
||||
ebiten.Key3,
|
||||
ebiten.KeyQ,
|
||||
ebiten.KeyW,
|
||||
ebiten.KeyE,
|
||||
ebiten.KeyA,
|
||||
ebiten.KeyS,
|
||||
ebiten.KeyD,
|
||||
ebiten.KeyZ,
|
||||
ebiten.KeyC,
|
||||
ebiten.Key4,
|
||||
ebiten.KeyR,
|
||||
ebiten.KeyF,
|
||||
ebiten.KeyV,
|
||||
}
|
||||
|
||||
type UI struct {
|
||||
Emulator *chip8.Emulator
|
||||
State State
|
||||
}
|
||||
|
||||
func (ui *UI) Run() {
|
||||
for {
|
||||
ui.Emulator.Cycle()
|
||||
time.Sleep(time.Millisecond * 2)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UI) Update() error {
|
||||
switch ui.State {
|
||||
case LoadingState:
|
||||
go ui.Run()
|
||||
ui.State = RunningState
|
||||
case RunningState:
|
||||
ui.Emulator.UpdateTimers()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ui *UI) Draw(screen *ebiten.Image) {
|
||||
frame := ebiten.NewImageFromImage(ui.Emulator.Display)
|
||||
|
||||
operation := new(ebiten.DrawImageOptions)
|
||||
operation.GeoM.Scale(ScreenScale, ScreenScale)
|
||||
|
||||
screen.DrawImage(frame, operation)
|
||||
}
|
||||
|
||||
func (ui *UI) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return Width, Height
|
||||
}
|
||||
|
||||
func KeyPressed(key uint8) bool {
|
||||
return ebiten.IsKeyPressed(KeyMapping[key])
|
||||
}
|
||||
|
||||
func main() {
|
||||
rom := LoadROM()
|
||||
|
||||
ui := UI{
|
||||
Emulator: chip8.NewEmulator(KeyPressed),
|
||||
State: LoadingState,
|
||||
}
|
||||
ui.Emulator.LoadROM(rom)
|
||||
|
||||
ebiten.SetWindowSize(Width, Height)
|
||||
ebiten.SetWindowTitle("CHIP-8 : " + rom.Name)
|
||||
|
||||
if err := ebiten.RunGame(&ui); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
29
main_desktop.go
Normal file
29
main_desktop.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
//go:build !js
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/tangzero/chip8-emulator/chip8"
|
||||
)
|
||||
|
||||
func LoadROM() chip8.ROM {
|
||||
data := DefaultROM
|
||||
name := "test_opcode"
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
bytes, err := ioutil.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
data = bytes
|
||||
name = strings.Split(path.Base(os.Args[1]), ".")[0]
|
||||
}
|
||||
|
||||
return chip8.ROM{Data: data, Name: name}
|
||||
}
|
11
main_web.go
Normal file
11
main_web.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
//go:build js
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/tangzero/chip8-emulator/chip8"
|
||||
|
||||
func LoadROM() chip8.ROM {
|
||||
data := DefaultROM
|
||||
name := "test_opcode"
|
||||
return chip8.ROM{Data: data, Name: name}
|
||||
}
|
38
roms/README.md
Normal file
38
roms/README.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# CHIP-8 Public Domain ROMs
|
||||
|
||||
* Chip-8 Games Pack:
|
||||
* Puzzle
|
||||
* Blinky
|
||||
* Blitz
|
||||
* Brix
|
||||
* Connect 4
|
||||
* Guess
|
||||
* Hidden
|
||||
* Invaders
|
||||
* Kaleid
|
||||
* Maze
|
||||
* Merun
|
||||
* Missle
|
||||
* Pong
|
||||
* Pong 2
|
||||
* Puzzle
|
||||
* Syzgy
|
||||
* Tank
|
||||
* Tetris
|
||||
* TicTac
|
||||
* UFO
|
||||
* Vbrix
|
||||
* Wipeoff
|
||||
|
||||
* Super Chip Games Pack:
|
||||
* Alien
|
||||
* Ant
|
||||
* Blinky
|
||||
* Car
|
||||
* Field
|
||||
* Joust
|
||||
* Piper
|
||||
* Race
|
||||
* Spacefig
|
||||
* Uboat
|
||||
* Worm3
|
Binary file not shown.
BIN
roms/c8-games/15puzzle.ch8
Normal file
BIN
roms/c8-games/15puzzle.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/blinky.ch8
Normal file
BIN
roms/c8-games/blinky.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/blitz.ch8
Normal file
BIN
roms/c8-games/blitz.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/brix.ch8
Normal file
BIN
roms/c8-games/brix.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/connect4.ch8
Normal file
BIN
roms/c8-games/connect4.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/guess.ch8
Normal file
BIN
roms/c8-games/guess.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/hidden.ch8
Normal file
BIN
roms/c8-games/hidden.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/invaders.ch8
Normal file
BIN
roms/c8-games/invaders.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/kaleid.ch8
Normal file
BIN
roms/c8-games/kaleid.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/maze.ch8
Normal file
BIN
roms/c8-games/maze.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/merlin.ch8
Normal file
BIN
roms/c8-games/merlin.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/missile.ch8
Normal file
BIN
roms/c8-games/missile.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/pong.ch8
Normal file
BIN
roms/c8-games/pong.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/pong2.ch8
Normal file
BIN
roms/c8-games/pong2.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/puzzle.ch8
Normal file
BIN
roms/c8-games/puzzle.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/syzygy.ch8
Normal file
BIN
roms/c8-games/syzygy.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/tank.ch8
Normal file
BIN
roms/c8-games/tank.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/tetris.ch8
Normal file
BIN
roms/c8-games/tetris.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/tictac.ch8
Normal file
BIN
roms/c8-games/tictac.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/ufo.ch8
Normal file
BIN
roms/c8-games/ufo.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/vbrix.ch8
Normal file
BIN
roms/c8-games/vbrix.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/vers.ch8
Normal file
BIN
roms/c8-games/vers.ch8
Normal file
Binary file not shown.
BIN
roms/c8-games/wipeoff.ch8
Normal file
BIN
roms/c8-games/wipeoff.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/alien.ch8
Normal file
BIN
roms/sc-games/alien.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/ant.ch8
Normal file
BIN
roms/sc-games/ant.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/blinky.ch8
Normal file
BIN
roms/sc-games/blinky.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/car.ch8
Normal file
BIN
roms/sc-games/car.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/field.ch8
Normal file
BIN
roms/sc-games/field.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/joust.ch8
Normal file
BIN
roms/sc-games/joust.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/piper.ch8
Normal file
BIN
roms/sc-games/piper.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/race.ch8
Normal file
BIN
roms/sc-games/race.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/spacefig.ch8
Normal file
BIN
roms/sc-games/spacefig.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/uboat.ch8
Normal file
BIN
roms/sc-games/uboat.ch8
Normal file
Binary file not shown.
BIN
roms/sc-games/worm3.ch8
Normal file
BIN
roms/sc-games/worm3.ch8
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user