emulator finally working 🥳

This commit is contained in:
Jairinho 2022-02-28 16:55:48 -03:00
parent 6f96020c61
commit c3bc952330
No known key found for this signature in database
GPG Key ID: 954589B18A21D5B6
47 changed files with 265 additions and 142 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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])
}

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View 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
View 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
View 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
View 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

BIN
roms/c8-games/15puzzle.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/blinky.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/blitz.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/brix.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/connect4.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/guess.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/hidden.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/invaders.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/kaleid.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/maze.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/merlin.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/missile.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/pong.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/pong2.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/puzzle.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/syzygy.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/tank.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/tetris.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/tictac.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/ufo.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/vbrix.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/vers.ch8 Normal file

Binary file not shown.

BIN
roms/c8-games/wipeoff.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/alien.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/ant.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/blinky.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/car.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/field.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/joust.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/piper.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/race.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/spacefig.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/uboat.ch8 Normal file

Binary file not shown.

BIN
roms/sc-games/worm3.ch8 Normal file

Binary file not shown.