From c3bc952330609ac10da35d6b893276ea082268d8 Mon Sep 17 00:00:00 2001 From: Jairinho Date: Mon, 28 Feb 2022 16:55:48 -0300 Subject: [PATCH] =?UTF-8?q?emulator=20finally=20working=20=F0=9F=A5=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chip8/chip8.go | 66 ++++++++------- chip8/chip8_test.go | 2 +- chip8/instructions.go | 57 +++++++------ cmd/chip8/main.go | 57 ------------- go.mod | 10 ++- go.sum | 28 +------ libretro/libretro.go | 2 +- main.go | 107 ++++++++++++++++++++++++ main_desktop.go | 29 +++++++ main_web.go | 11 +++ roms/README.md | 38 +++++++++ roms/Space Invaders [David Winter].ch8 | Bin 1301 -> 0 bytes roms/c8-games/15puzzle.ch8 | Bin 0 -> 384 bytes roms/c8-games/blinky.ch8 | Bin 0 -> 2356 bytes roms/c8-games/blitz.ch8 | Bin 0 -> 391 bytes roms/c8-games/brix.ch8 | Bin 0 -> 280 bytes roms/c8-games/connect4.ch8 | Bin 0 -> 194 bytes roms/c8-games/guess.ch8 | Bin 0 -> 148 bytes roms/c8-games/hidden.ch8 | Bin 0 -> 850 bytes roms/c8-games/invaders.ch8 | Bin 0 -> 1283 bytes roms/c8-games/kaleid.ch8 | Bin 0 -> 120 bytes roms/c8-games/maze.ch8 | Bin 0 -> 34 bytes roms/c8-games/merlin.ch8 | Bin 0 -> 345 bytes roms/c8-games/missile.ch8 | Bin 0 -> 180 bytes roms/c8-games/pong.ch8 | Bin 0 -> 246 bytes roms/c8-games/pong2.ch8 | Bin 0 -> 264 bytes roms/c8-games/puzzle.ch8 | Bin 0 -> 184 bytes roms/c8-games/syzygy.ch8 | Bin 0 -> 946 bytes roms/c8-games/tank.ch8 | Bin 0 -> 560 bytes roms/c8-games/tetris.ch8 | Bin 0 -> 494 bytes roms/c8-games/tictac.ch8 | Bin 0 -> 486 bytes roms/c8-games/ufo.ch8 | Bin 0 -> 224 bytes roms/c8-games/vbrix.ch8 | Bin 0 -> 507 bytes roms/c8-games/vers.ch8 | Bin 0 -> 230 bytes roms/c8-games/wipeoff.ch8 | Bin 0 -> 206 bytes roms/sc-games/alien.ch8 | Bin 0 -> 806 bytes roms/sc-games/ant.ch8 | Bin 0 -> 3196 bytes roms/sc-games/blinky.ch8 | Bin 0 -> 2528 bytes roms/sc-games/car.ch8 | Bin 0 -> 320 bytes roms/sc-games/field.ch8 | Bin 0 -> 860 bytes roms/sc-games/joust.ch8 | Bin 0 -> 2374 bytes roms/sc-games/piper.ch8 | Bin 0 -> 1828 bytes roms/sc-games/race.ch8 | Bin 0 -> 148 bytes roms/sc-games/spacefig.ch8 | Bin 0 -> 2839 bytes roms/sc-games/uboat.ch8 | Bin 0 -> 2016 bytes roms/sc-games/worm3.ch8 | Bin 0 -> 360 bytes roms/test_opcode.ch8 => test_opcode.ch8 | Bin 47 files changed, 265 insertions(+), 142 deletions(-) delete mode 100644 cmd/chip8/main.go create mode 100644 main.go create mode 100644 main_desktop.go create mode 100644 main_web.go create mode 100644 roms/README.md delete mode 100644 roms/Space Invaders [David Winter].ch8 create mode 100644 roms/c8-games/15puzzle.ch8 create mode 100644 roms/c8-games/blinky.ch8 create mode 100644 roms/c8-games/blitz.ch8 create mode 100644 roms/c8-games/brix.ch8 create mode 100644 roms/c8-games/connect4.ch8 create mode 100644 roms/c8-games/guess.ch8 create mode 100644 roms/c8-games/hidden.ch8 create mode 100644 roms/c8-games/invaders.ch8 create mode 100644 roms/c8-games/kaleid.ch8 create mode 100644 roms/c8-games/maze.ch8 create mode 100644 roms/c8-games/merlin.ch8 create mode 100644 roms/c8-games/missile.ch8 create mode 100644 roms/c8-games/pong.ch8 create mode 100644 roms/c8-games/pong2.ch8 create mode 100644 roms/c8-games/puzzle.ch8 create mode 100644 roms/c8-games/syzygy.ch8 create mode 100644 roms/c8-games/tank.ch8 create mode 100644 roms/c8-games/tetris.ch8 create mode 100644 roms/c8-games/tictac.ch8 create mode 100644 roms/c8-games/ufo.ch8 create mode 100644 roms/c8-games/vbrix.ch8 create mode 100644 roms/c8-games/vers.ch8 create mode 100644 roms/c8-games/wipeoff.ch8 create mode 100644 roms/sc-games/alien.ch8 create mode 100644 roms/sc-games/ant.ch8 create mode 100644 roms/sc-games/blinky.ch8 create mode 100644 roms/sc-games/car.ch8 create mode 100644 roms/sc-games/field.ch8 create mode 100644 roms/sc-games/joust.ch8 create mode 100644 roms/sc-games/piper.ch8 create mode 100644 roms/sc-games/race.ch8 create mode 100644 roms/sc-games/spacefig.ch8 create mode 100644 roms/sc-games/uboat.ch8 create mode 100644 roms/sc-games/worm3.ch8 rename roms/test_opcode.ch8 => test_opcode.ch8 (100%) diff --git a/chip8/chip8.go b/chip8/chip8.go index 78e8b26..37c21ba 100644 --- a/chip8/chip8.go +++ b/chip8/chip8.go @@ -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 } diff --git a/chip8/chip8_test.go b/chip8/chip8_test.go index f490828..9aa8ac9 100644 --- a/chip8/chip8_test.go +++ b/chip8/chip8_test.go @@ -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 diff --git a/chip8/instructions.go b/chip8/instructions.go index 64b0876..0cd3aa6 100644 --- a/chip8/instructions.go +++ b/chip8/instructions.go @@ -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]) } diff --git a/cmd/chip8/main.go b/cmd/chip8/main.go deleted file mode 100644 index 3b47293..0000000 --- a/cmd/chip8/main.go +++ /dev/null @@ -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) - } -} diff --git a/go.mod b/go.mod index 1768482..8c4f655 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index 113a7c6..8d674c1 100644 --- a/go.sum +++ b/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= diff --git a/libretro/libretro.go b/libretro/libretro.go index 23092b2..eff8665 100644 --- a/libretro/libretro.go +++ b/libretro/libretro.go @@ -16,7 +16,7 @@ var emulator *chip8.Emulator //export Initialize func Initialize() { - emulator = chip8.NewEmulator() + emulator = chip8.NewEmulator(nil) } //export Deinitialize diff --git a/main.go b/main.go new file mode 100644 index 0000000..96d6b70 --- /dev/null +++ b/main.go @@ -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) + } +} diff --git a/main_desktop.go b/main_desktop.go new file mode 100644 index 0000000..57cc361 --- /dev/null +++ b/main_desktop.go @@ -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} +} diff --git a/main_web.go b/main_web.go new file mode 100644 index 0000000..b26d3fd --- /dev/null +++ b/main_web.go @@ -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} +} diff --git a/roms/README.md b/roms/README.md new file mode 100644 index 0000000..8194191 --- /dev/null +++ b/roms/README.md @@ -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 diff --git a/roms/Space Invaders [David Winter].ch8 b/roms/Space Invaders [David Winter].ch8 deleted file mode 100644 index 3ada8dfe93a7d95925a9f52a1bf2cf385101bacd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1301 zcmZWnQAk@?82)d1y_piamh`f#L!@1A(Y5IsX0;I_gJ9ar3bKc*%*)hU?J_db zHpya)flW574=cK`vDvNnWm{kNuoto79NUwy4is_bVa)mx!w{k+p1c1hX*=A&s%{wQRRi1nI9zSDv1 zJPLJiw$)+c()-6IejM1JczHl<1M;gJPYIVr@_U<${9*fTlx&tciEOvEkR3Vx2Doc) z3O)2`Rq?%YRUtWY*g(dLwdaA19X80Jse2cRyD{b%E?soEWt@3AL7PtV1uN2{^5bAUzCT&h3Dh;D&&*7BvD{v<+c;@^XuA) zH6HSE=u=q^X*C5})%ghW!|+LNgx~p!T>I8}=qU^;mivcc7~!vTqn!KhH+2OMYKU>} z7WaCs`!3{t3Co(DMbp%@ov?hq#Kw3RavLshi~)QI9XJ3ujyr|`=#Z-D5Jd}sK?w&y zqN{5F0f1Zu=vWYk&Uj5c4zRSe;x}5nN8?3TeX+>%05LyXLL80nN-d8cpE(0?F!?j$ z;AngSBt=?S04Va?lauMaqw#0;@3!nhP8<7Q{AvA*uxJCI#8**rxvYcnpQh&Kp1_lj z=jJ}2-T%9}d9S^_9bT+&KY#9(U$%OC-(FkWf~^-$lE#%uGJz_j>(EFpM&Xr;xN?2m}F*reTjZFfRof zTXCYrXT%-lj7$_oj4%ggWO!B(SbBMx@jOd1^z9a~JBLFMgmS+Om+1SMA3w7KqYs?a zRDua0R5#2%U2W1K5TL{qrcD9~g_vn27^Zq<`?MN015qPv5^98Xb=nNK{^xX9MFEQ0(9n^cvUT~Vjc{JvVlv!A|lx9~5`cIUbP diff --git a/roms/c8-games/15puzzle.ch8 b/roms/c8-games/15puzzle.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..3ef0bc8e6937b45b8ce559f520fe59ff20ab494b GIT binary patch literal 384 zcmZR0ki+1^kjKA>IYHq=D8mD#eM)6YeM)gkZbAZy3`ye2ERF&U?-srIDEA@NfkDXR zgXSgELdGK26y_Eo69yrrB=KT4AtfO1Q7Uumr6~an?_8J}-lZ_MJW*OE702KcZ z7LQBe0jlSLst1Yx0gIQRi1Pr&S-|3bi(Y&I+P~<t9D$Z+`mMNpl5)=_xgS39m(q>)#b7WgmM%In@ zl5SCvP-OkE;)hLCBK=ZI>xUsyVU;oz%eW3H`H&^;5OE>doK6^5pZC2aZ6>?qeeZdG z&;MWEyYM}M1ET1Q9Pqs{o`@$-kNf%#y?W>s;k7>8CEJT@jgc5z{JB1oKXYkRIE~7} zWhs=eNJ9S3$({-G`-eV2A2J{H2+`ub&83pEe|-?~Q{bPth;HmmzuWsa*+y3(kf2kiir3C>D&J)DxY{}oGacG?!w(xTodk&p@iuK8n@2xQzqu0 z+n9)T`HO?d{!>d&F=-|!+TUvFPueG$0hj&c)2t~qtp<_(($nmd>HxBT1KFYS{L32? zd`~VZb>%A4_~MLYs9kdeq%PRsmQ1k6B_r&aNLQMPptKrcQcOgbQgKL*3fHqpGLcCg zpG!vU%T~gyV=ioPEQXDaKZU6DIA6*4eH{8E>W&isQ_OCJ{r)IqbXX{^cHmG&!;_u< zP`QbB`a?GkixBQqOsdxH1 zmC=wKI10Hom(DTTDUO@-&!XC>t&Kxna=PP);=UH1Y5AKdj@x+4Ur+EK>zEgjbBnN% ztvW56OV|S4-H*dOE(ev>ILxWZE-ntUHt|v%X4h(yOq3@Pc%q!Pcyhf{Qo`bS;OV>? z_DwNi+mu>TT0FV+LFCpGo`#U?0?fVv_wNoO*B6whRnYzbZ9QLg>w%{|dZ({jukqy8 zmz48NNy%5qlNuN4eW6E67#{fJaR!dh}|h*eShwtWW_#N6n-GNivQNVw*se`K*&9p-G7FO_)yI~ zmt;;c@P;(V7VR@1Y;S%+_Z-}>>ROsBvQc>(<}|B z13j)a67tp&;D`Ln5dA@-fC|qKAygYgo-YfwLO+pi0_^YQ+Wk;4B`s>ldK?&%%+a3 z`$?TkuyAeDFUziS|I=BBu(IZE7V>nn77+L99LI7A13Sxc{UjQ;S(1V3hE*_ftd&HX zOlHVJ1#+WZSi!_`X?B@(unJ+zS|7_)0c@GwfOzWL4Zbu3iPjWI*3EK|1nNQMZBlrF z1ho`)6^zCdy8)G)wo9S&c)5>(B#Fx%;LGF`W0gr)$km3g}h6hSF zL1bUSV!3SjGx9x0?rgGW6!N^YNyzdPdlh5#zdM_%{#!8!`PTinV-T{;X6Px%WyoV# zEPeN}J%i9eYX+gl1l9-hgxa$iJ1$f(bv~$MUMzQGV&jJd)(@f|*bNwjx~&<7Rxg$_ zWV|u4lF?E@XmvJ&Aw$i79|ob_AX93YY#oGl<}%bV3Z*hUC}$GV1&JnzBr+v&FIKo9 z4RkAqfsW8wq3a1;iM&ayi`g%Tg2l9i9ySyd{A*xa-2TD%Q)+_lN6kdu3!(+ApENIs zGQ4YUYHs@df}?}=(Y^&c6zm#+;77xcKL-x{Y`lH?H^Z;4Su=mU*LtC;$&jC+=hF{V vv|%5^J|#PoO*d{Z96rndBM%&4fTP<$z#t(3MW{Rp37{M^Ge{1o{`PGER~?*% literal 0 HcmV?d00001 diff --git a/roms/c8-games/brix.ch8 b/roms/c8-games/brix.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..ad639d95754002ee1d541444a6658dbab3f6dcaf GIT binary patch literal 280 zcmc~}O=ZYt%VJo}b8BN2iUhRkPusrLazK`fjbwKz9lduGAtIlAXvb$nD2sM zg2M;V59|gELMF%f)8#T4GygBk;HXzBAVBf=Z=ORd^t)+cRf_)oP zqI|oMyS&gEH--#GH~WnL?hKiX*9z?!g;oJYS0%7El-e@@X~qu{4GD>V8iW?}TqsQV z*U7B(t&~yen`?s5Jt4Qu{|yNZM}c}yB{DUL0_g`}@*S9bk)ZHFVliLc|Aq$*EQt&k z1nn4v4214g{P*~m@n7g&1)~R324e!t2Z_x8LIsOOz8Zf@{ivC2p2Be1wV3sj=4Drg PcMljE82&J8;^b2tfTD1FH>Y^>*LNggM7_%92`Sau#EluD} zG)iK)pnp-nh~G#~$mD|JMa3e<1g=H%L99jd?>_v)b@!nUt5BT{i%?ee|JvWSTtZdZ zY`MBZRW@uwGpZSDS#6DgB0%%*J`@UAwB@(lhg63@LNmG;3VweG?QC8&&yr!$!n;dA UW(fr(eE5(sVM0O!Dv&|}01EU=>i_@% literal 0 HcmV?d00001 diff --git a/roms/c8-games/guess.ch8 b/roms/c8-games/guess.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..36f783da104d05d22093fe2a349736e0da38589d GIT binary patch literal 148 zcmc~}WO$Ivn8ldQ*mI%hkq?8ALdOLqJ1Ykvg)GKuHXHj|Mq7I!t`A%etesE#p4d4E zF=feQ^L9*Nc%Wn_WV2|>593d%N+GwlR+Q|8_?&avoSL1?CAP~aW*OOkugBj#`qJK;`XM7ajWG;$X?0eF)eC>2k~=px=KeV#RhwBh>GtsGQM zSrs=-z`z-6&47NwW37>N#!Ji=%j3CQCr%NC*+=b?`THaq#ZToc{&M*O8qF85+i<>c zq{|na1U1|RRB-GdsyM5uv95FYOh#2sHI?*Ze*7SM>?Enje$0=PQNpRAg2;Z1_wnFv za`o9F6^tn5nW&xFm(2MBh-ibghIHYY+=a?tcTsB~vTY#)D&|b+SI&eLN7PPQhgEf9 zP0nP_#Ho^L&?0H(pzbZQX#%jm_HUig&xn0Y&txMlkSL@hzo*BS}Z+_$i>(04t(v}oB; ztLm@4doB# CKv!7+ literal 0 HcmV?d00001 diff --git a/roms/c8-games/invaders.ch8 b/roms/c8-games/invaders.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..f7db5f53b5006da92a38a2c0091e6da986ef6138 GIT binary patch literal 1283 zcmZWnVMtq76h1F~J|7Zjc4OEbmh`cwLu6gw(6#9rW=$iB41#SRE68BaFdx&rY8P2a z+a!%KrZ91cKL)z6vDudQXQO|{*dGybj_pqcJ8;NmevDcF#4vP`)>?>C|&Idsoj?~=!dX4zqU$0 z7XNX-DUnmx#ePsOkW+(ohHDt)yIMohRjgNlyer>La->^_k4_B25FcqU_-o$bwg~sk~Jo-|9fN zA7AR^EUUx#h0$Z<--q|cUk!`xKz@-&)51lOthGDHZs>E)!s(>6haFLNx{aGL*2QE1yp)VU{@$9_*e`~U zub#Y)x1hZ4Y%exI{=qIAAT9Gh?7RC#`GYax#kjo+g;YL86xdj~ZHL1ABkjaG4+S~! znJfpix+1OJnez@5hF~fm;kUmgS0C68+{FQ<;=Dfu5&jxC%sHp-L(z>n!~}PXd%NB_ z1qE-iQkk7a)7-q3ta!c2jWHi`8xBvL0o;L38~_~09YX+gN{8qa#R>p}G7f;G&lg4j zAXfuA6-Yo=qArmDSXx@~8?BzB@sgvlRN{GnxSuT{j>dPS)+bL+p9a{U_zAIpG`;|m zA}uTcl=!WQiOlZN`1AUAT6Z9?jr=eEto}tsDe@&490TP4;LTiQq@z1nHh|-G@5MOvT>GXcs8D1`gQph%d+WQ zhAlFijWLIUNLwl>nl5-8+3e>tGljI@V5 zh&!r^OcX_oFdHf|JSzw+v%JiBo~0Q2Rtwmj%_ay!wO@uy^!?0_pIL*^2X<;Ip(GHh z8)l!bHtP@!Qeq0zCV`|v%#0F>QoXi)T8){(m=QGzHKMvYZARPvb2_SG0-B_usKqOK zHLszlq1>TbP)$-3bLghgru1RvXq&liLWr1ueN&x`HdT$*_&;;iJlg>|XP`L+$53d+ko)Us1r}6951J literal 0 HcmV?d00001 diff --git a/roms/c8-games/kaleid.ch8 b/roms/c8-games/kaleid.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..a1bc7ccaa64910d8bee353938be35062b77a1f20 GIT binary patch literal 120 zcmYdbNNz}!PvTcHTEy^K?gQ6{PzMGfnPNs`1|g2*2C!HvM8p{=u4E)6=fG6--+`s@ zzXMw#qXS0~XFPK6EK%^EUz6g^VDvi;7(W+49}0w>D>rNi54uQ3&_+3vmrn`ngy(K_HPq>GWd51nxv>rPC=aO7~M4 zk~EYK9%61#SRDCP?n7vTcp^tKW3!SmLjhMZ6NoP4S}cA+EJ;M^AdqBLI#|jW5+G!n z$^cTiSo|}WF^f=TGK0``V^*PNpdLmbi(RO<5U3l-;tL8SejY9*YX&YuB0(%FeM~>5dFYzz#t@bT)dnk^Z%j`*9svj5*QyW ztp1n40wkFd;4%tW-tkfRsQ$t`$O5BrrZ$ zSp6@71xPX_$agc{+LplcU}5dQ1YRHs6zOHUa}A`rt)+cRf_)oPqI|oM8-q}Vo4rtz zyL{$v79}z@hyv+dVDbc*Jd~jDK|<+D zp9#=qcE*fA*ZnhQ%wQ4{tNiczFY~|9iAqLKrcA~JmJbqTjBXBP|AcZDefnqoDfOdf qibyKMmCzE=PnuUk8QwKO00RhQE95XNdUg9?4Wo^M(4Q;@hIarEDr-&v literal 0 HcmV?d00001 diff --git a/roms/c8-games/puzzle.ch8 b/roms/c8-games/puzzle.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..bec7af08ae2b0635f8dad89d5782cc12383d3eff GIT binary patch literal 184 zcmc~2%4SRyNMcA}ShV4y`Ugz|hFe<;IjT4e4TMw@1&TPbgsM3fGklQy5L&=!ARxq* z1yrBIko5lmTLF_&P|<%Q1|f?)hCGH3TuMQ8jCG7c9xV%6H?$ovU=m7kWfV#&{ZaPE zfJ3OjRZ^&+l%tHrfK8~zMM$Wo6 c*C6mA^rPmL&|6!(9J&K~5*XgSi~9!!0BPt&IsgCw literal 0 HcmV?d00001 diff --git a/roms/c8-games/syzygy.ch8 b/roms/c8-games/syzygy.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..b0653ef09ef49586ca50abac49e7eb27fa39b131 GIT binary patch literal 946 zcmaJLV+7O(2v$7G^2MIA<-0JbVrDnOYv*<4VBv-?d z%B4TTL>>@s={gXfV6ej*%Q_P8 zZ!CwZY`-|Y<4>R)EJ|`b5~{+k-0Wq*g$xlcVklq_LstY+P6VY;L@=C)%Ru~99Z6~( zlE$$K1>twnM)uaj;P;udz>ft8|R;qDinA^)HJ+gP*x{HM}E z9(mj%oz2&=ry{UpV;Zl`?ntug&Vh6ZmnI!{r+L5;leEb)7&{JQ2QhXW#tvg*o1b>< z7~>$>|HmJPek3MwnH|9Uax+r)I_iN045HH|(%f|zLT8xnft#$#|7VmYpVdpB_hywQ zA=g>nReZIkyMjio;ZMUEhc;V=^)T((i?oG%bf#14Dp#lI{8q=~Raqq(S<{hxN^W?x zD4!%j!g|F$;v=tgkQ?ayt<}-Rk%g@oztJgWwkNdv!FxXXT%B6d-zvC_@ecA3)>tRF zb}`nu4zNBjEswtQZiUSdpEdabpEdcZd;xOm>-@A?eRM7@SiQf(=@P5&XEFD@%elTVxp8YQQTYEX0gibRqo%1wj#)-AsQ#c79NXX)q#M9N$D9_uTv5x#x1)Z18zD;fd zLE&IG&wA`#gV<<+SF65_3}V5MENaNsc_U%TVa~sez?vGqMo>`^IBF+~GuxsoaxFg| z8&?Mz-$1}4r^4&TH`Z$i?ET$Cn)Ho0gDYXdyA)QbOWlnRU zvx|@F7Gb@?Y(w0NTJS48lPf<>59WSw{y7F$V!wx2Y30MTqtayuba{nn!iqqj{qH~% z;tsgp(E0n*B%fOvL+uWRe1=w-rmvcO7Y1oZhU7+u>=Glxx(Wdwt}vliIvj{250h1Yyyyxbo=)M;sB5)LOUE?V4SV kYbl&-8jRsaA1 literal 0 HcmV?d00001 diff --git a/roms/c8-games/tetris.ch8 b/roms/c8-games/tetris.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..9f5e0874dbd03b5e99d32a1d81fe3cb343904cb4 GIT binary patch literal 494 zcmYk2yGsK>5XNWCX+%VlyR+~?lv`40X#skLaSFO}dqV`E0DV;L4@g-?2y(W#Fx1@3Mc&ZNDkgLrFV^Cm-+M2rg zjM!iiTd~UQN-JPj=OW-q-SKIcNeUgIT_rCjxXUNY#8M`~7%h>@zU5Ei75S|TQ{~eY zGs<{VJTwsmk7WRuU;r5gjH4KbX3PZ3XD-eHy%{*pF6KkV))=4 z`JzkR?q2g_p!I^6O7DgGA{(S6IyO{AmyQH@i$ojd4RavFCM58G9nu!nCcxtj#eU@= zw4d4X3(SKF+M#^Vj^%BF6v*8n(9SpgEx(^=+fulYLOp3!@W9J`pXVGd9WpWt#{5ETeJ_^E?Uo9$mCTZn zUfzIBumBuUqqHa+j6CC`$e>6@tYYR zzin#z<229+Z{%Pd@E9enOS8OTvpsC${H)y z2`iR~2!flJ3PFDRl+O3mdim>TlAYuy(cGXJ_sm!F%@T2T*}}ZVpVB)LxCi$CiRii> f(ba&e6Zjp{onM45DpFWtk`fAsSV+;r8q>Z3BPN<( literal 0 HcmV?d00001 diff --git a/roms/c8-games/ufo.ch8 b/roms/c8-games/ufo.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..7fa5a15788e14f66874a80a8aa5d3c86a02ede38 GIT binary patch literal 224 zcmZ2ri-2avTZY~Cj z^!PCd>48jeWe{pB{oj&uCD@)($Ss$nH^`Sms7Gj$(wd6@LUKxr%2|b`0ZE|Exy*kh zKstol7XA2coXnuKjp5y*A3uzfjXljA6n+=Sim(_On@E+YLh36+w?HEyF(gr?c1Q;pm^z&ux3rauoMdRFB6MCV z2vqWtwF~*(-)POM|O(ofA)oA76h>ga7-TW~e$45>dW7*U5bB zE%M4g6;GpYJ>{#e3XDGCYb|iB0f}|Dc`dH8?mM%PR~i^`8`WX=*V cAWsl1ORzL0H4Su<1b_j^L~<^@|3J)Gz+vDZ zq*lndNc4hG!G9oTEMzj27qUp;NEAq7Nik35Ph+|ucqK4{@x{Ud)Yz{*n%e#d+|MS>@B;y6ah9egQ{TYR3I5DI!I@zcE zcV{V(K{#r#VG vq`vsSkQs;;(pYr%g5aVL-;6({CQGD9f6%<_TFm+pOnw5ALMIp-T>b+9XCG1m literal 0 HcmV?d00001 diff --git a/roms/sc-games/alien.ch8 b/roms/sc-games/alien.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..e21c7c4250ec0013c0dbe4285f654533ec4bdda7 GIT binary patch literal 806 zcmZWnUr19?82?Uk+ZbYT?%kLq%eFnNAXrW<^oq+&#D?}V37_0w)1A%LR!F_!*iKzxG_>onC;!e6&HRMJ{z(9%P-?)Nl(?x zvV(wRE>mUsG(F1*h_Ax$iEe)Zf0DRC@W6qaG{b8_;Q{Ysg9F$z>&JLtZR;8VrTe8i z^{$iVli8_!jBx>5f0Gjw)}Z#<4(tUesw{>aEo{9Hph%ih_SCy}@cW=BI-^MJrw-IA zZmP|&F3J4EVIgF!tS@V*j@LqH1q=0OC+9;awpJwTA-}k_0>v)iK&r`kZ!0RxdHZNg z=LIGmpzKJ_d#eiOHFTp8>K+Rz zmG@7++L%|T%5^pa*LhzIHmSa$C~NG%`3H5fehAu5aRVa?H`u1(yd!7`S%XkaWeLo@ zn8^Q~GdC$2HuW=^iTI{oKnad#xRFPh*?6yAohM&yGC3UW9?$oa)rUx@KWk70oj z4+^DP gd1a!5=lKqPU|@V4z{*?chLImvj7G)mPQriq4f8B49RL6T literal 0 HcmV?d00001 diff --git a/roms/sc-games/ant.ch8 b/roms/sc-games/ant.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..b140fabfd4420cc057b7e966c37a663adde2f621 GIT binary patch literal 3196 zcmb7FZ){W76+iF##(6|2xpC|l*P<_vhvT4#zL_v66Np_WCJ7Llh?0qES@j(3#y}Uj zUVQwm?oCJ&)$A13RO{!u8l-_CVN zN!nH0e0A@;@0{QH_q+Ezd#Zn+MFvl3r#2g5?T6r)r5H@HN4!FWsu(o`aj-sraX@l!piPFEA==0Qje58S0j$+kEu2N z=%J{7NFkr9jGdX@ej9f&*hgaWJ}JibLCinxf279Fq@>$&N)}jw=$JoVxZZb%Z-L{5 zEpUgoK>|EstSFHpytE-;_fC2u(jSck_&2TM;ByIi1oqIQ=-K)nj)$=Z%k$2H zt=fqpl$p>4rZy_DEO@{g=|(48zZH4& zosQGiLL5&uUTCFp5+$GdA>U{>7M7xJOZgu7b{$codp{`I{Y3eE zKUF68yA^A{TyNC#!^wD40;cbaSjVsrrw(GDCoTXo;~fOVtixQJhI>wGyLkdI1N!d$ z1(kP-?t1tBXKGtg*ltNeUO`VQaE0sZ5Qo|c=~c`pe(cb)Cj?pDp*) z0DhTP_Rj$Jh$-18MQ;b^#8(Pe8NX2Ci;#mN{Fh%d4N;w{G(Z*EC@FFP_d3d&S#wc$ z>5eXkm{G!a+UzvD#SB%2+(Lp@F10A^0!~W_b?ZVjR8Xl=3REb#hUAbFa)e$f8r5a* zC{@e+sKzIM4Ba|0^?Jx&Cw}(LR(}2WlM5o(`6J5JQ1{^hHg9t zC_TL$$tErSJU4x2UyXIBcH&^oY>zM3^*rxv(mI;}SKOln<)}prA`y>P$tHj**-d3q zC4*b80wPnft%a&Ls{i`&UmPcYI(Mz-TF+Om;R^kC>;sbrCRgh-&;u#;<3BsapE#AT zR9&mO_Rad5-}?6Yu)$XTY_NNBGr1XCvut~8%mxc)u^Vh>(J443$H7@}7Qsf@Eb73v zW+rAPEXxKq5uU(z&?Y9tlVxXSvW#irl>Mdu23QZ9;XT(2XD$p&j`K5TN%P!nNaM_s zhh(17qFO6&wT3Oi*J>$d)s!Jpj<9u1lev5@la6Kx$6*nRw#mUUzljiAlB;Q$a490S zl2+`nTEd2yBoi8UU2^-^1ldhZ;*zlAwfg$n_ZVSR-Y)Y>3qZ1oOfskB1^N5-LH_b= z%0v=7;i{xnAqW`y2Um`=3bvL}wj=k3^{v^B6C1~NjA0Tvx3LHXiSbnR) z5(#J;>yfPrAYxQEe4saXUuNBGu$D7kV*!&ECBL+&X}nV#XIJEFm4BD7uqzYeoP!47 zg=)>)^FpRK7#t>1X;K;^JiUDp68f4uymq~Nw2UXP`@IVE~7VE*tU(>s* z-QX(c;>OA!tJj&BrW~IKJcGC-< z^SY&<*MH^NPuuA#`wUNaMK9~)fl)Fve6F?aRwTVjFGr(oX%Ts}|MG|93te%(%3haS zX*8a>F|%OxpCco~9qG1Jdd&Y;B!2Nh=4OUuZeHw5<`c#Oj|}$lQp= J@gA4!`!@~Z>SX`` literal 0 HcmV?d00001 diff --git a/roms/sc-games/blinky.ch8 b/roms/sc-games/blinky.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..4d0d4bbc85ad80e2f05bcecfefbd7410a0bc70ad GIT binary patch literal 2528 zcmZ`)UuaWT7(dtEo5X9{g#4eB>`rHshw0Lq1_#o{WMkWlkwHado$;oz?%y?PTcxed zxk)t+M(INfK8*+tS~|psb@ZVRomg;tm_^zVN*FO|JD`U|&`?c34&brqZvYGw)E@T>m_~>=?pfdi+0UcS}JYgnx z+okyaHl3IVyQic+_moR9y`yOT$h;3@tic*NEo)>Aw)&AN?PuVrn9`=lc#`_=)qRWL zTLj;t=`pED>aQKR4IC_+Zjbh3)JQuCR4KLbz2%$rWmy0aNF zNj4)r0yF{8-f&NuPKLYP_iNL^_z5Vuk#O;eT;bl z(4q3&(puQGHyM+flKF9beNxgCZ(H7%nqd7@(!n|)X<_{~*fhun(O@~aYk&Eq5d>783aYXZazZu{;Q9EiYZ7R8Hkn?Kl13oUn@|{vx|0gN*>9MWFU_ zgB_}<_E=*)RIb_^U=RQ5=fZ;tMi;1HoN=$l^=HfG2$-uV&A>)qJaOeIcM9 zVFPHS9MBE30d}~&z5t!zlC~XLx4iJkDB04Dtml)H$@vhnes>sIZ=?!IoctcN-$45* zRj_U~>W#71t%zUlI0jhj3nQ$0nniu;EmVvc>vpJ1PPvGvU8vcIYwRb#>kGn0qF@!)7jR1m*;oOsYWMRMA5xr%X`~RIYks4D@oqm9%X-*E8HJ--hR>8o*1>v6}UO*HO!HLjx}uCE>^RsE+M{5l61M(p*#4(E?SMFbaz(7s6IO1!HTwK{uyFTj3u%%C-l9Yw8e@%V_Q6*;@F z#;$fdQENv$e!&BAUGXcC$UfLF^!7T5{_&M~Bu>zDWPwXp6I8XnVvsOd;8N5Lbk(8| z0WM<4@aT07SM#enALZcMq(6>Z=KrU2kYV8T%^ct~b1fk5)OnucqZ)3S=Q~L@EYk#m z>Y8P6@|=NW>U4TQp#s@x7Y11eowbepQk1Li>jJDun3&uyBl~do#-8VUe%!su{<6!={E4(ovPoeosDio;$cWqg uA>mBAHysk%{FC?*T0%+mCa$2D=pSSNse37&i|5YdKFkSA)Q^)(HUEE#l22Ly literal 0 HcmV?d00001 diff --git a/roms/sc-games/car.ch8 b/roms/sc-games/car.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..88b00f59ac089412795148f41f4cb62bfa4fb9a9 GIT binary patch literal 320 zcmWfVGt@IMQ1I3((+f_`OHV2-PR>v;GSN5DRkt)@VECVElxCE!(Hzi{kd%_ikj0qI zz;IA$Ljq$HYl21)gOJf;i_0D$%y95(hCPE&1xU__p@~)L4M;RnO*|FUS99rMtu+O^L_98yw7{z-GSzFN+h|I z6?`qp4dkw*itLeG$x+;0$tzhw68x?~AxLhSEmi*aOs#q(*qY<+fZ``UBGK1{VKzW+gz*M2 z1oG^CaxrZaB(n9&W|b&pyJSYLk}Sx)L?e$#4Dy&%fm|mYM{e*E+lZIKT4=1v4ct$8 zdbuU&nL3f%Uf)K|joLYG(NDO%pTaO>FkJp283OKYkWB1w`5+0SD$lIR+@d_IbAWl; zH_^AiugwScgG7ICn|GlVGp&?e2F2GPS(EtPd~T`sPkpwQqd?VX^blLyO~M_(p+3vV zDDEJrMb0Ww1?VICIe-vv9FKnJy6_;=9_#PWwm1EnvObWKhtqj27X`?`HTa4#lmkC=e z(GShBXpBwMA@qNu3#L88--Evad#un}dG(#9wQKAz`jGx@Di9Su!yNPjivWj9JR`bB z>P4qm4{!;@*lk6OiE**RcnS!Vj=D@LRreYoG$71763&z}(3iUBpGmQG4YcNO&lbHwkqmB8=n zV0Ph%FYty=St}TOW!f@d##cMS)63teDhpF#wHD3ro@$&d7b{opzNz7>+rrPjd_R5C P!g%ejW2fA9w?O^^Sk6R3 literal 0 HcmV?d00001 diff --git a/roms/sc-games/joust.ch8 b/roms/sc-games/joust.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..29626c01603e2e6a954bacce8cf6d83c96ac1601 GIT binary patch literal 2374 zcmZ`5eM}Q~^xd_cCuy`R*AI|2U3<`CHp7$%Ly&e5hE)b(^Pl};pxBO8e`klv{9-cV z$Fija*~W|^4NLrkT+Ct?vp?8Eh;H*A!A3)}7%w3c6JzvJjXJUb-Fw&avBllx-uLg_ z`@P?L@6Ni;9XfFM;30SS=1p4wGX8WMC?BSGcf!Yp{lM0Hk}wnFPjGBa;CnT~rZojE z{$0L!@!U2jPLp@fXoG6M90IG$^LHgkzc5oHc#_P}$a#|1-UZvjni{Z8sH)tU)-yY)g*M$uEKZK0=S3;)zF)vEIo%psdo<}APO(0$LZuB0j1@QrB ziME8P84`cVmj3BxKt7(Z>AX3|>w0OR7NjB4As%T4Y4}`R8b*)(=#kSs_MyKtXx=aJ zEf@#le!>)cuv{8mj=yUuevG|~VnQ5S~W}VIycAi};{y61`#O2AVi&NYCL2Qkc_8Lk;a~;z@*UQ^r!$y<$L+?grx0 zpD4p<+brz@!p`JgOicb-=Rr%aL-p7+X|4xp^3}dFSc67@s-LlFyxGb5{B~!M+u~vE1nsCl(%!m|vMoK_RBnoF?B+jL&_K`~CT*JBd zRvKqoPae&;o;;dwSf{WTA}6BTl|8rXZg))AO?MnL9^wzrcP}!dCs~L2tm_5M(TY~k z7H=1oaLSpTLqTw)g5{1s82^6CkqnBCOF_Xg9&|g#vAf*JiO9V{9J3_WG=t*->!eBK zz?sBKNrJc~t3hh<>y$Kt7`=hkh*p*0xJ2=R_6}JZTXB#CI-Pj|zq1ImfCZ@*qS&hm zhwkfj!cMF$RzxT1+{#Qjjcr-;T2Aujk|d12VTOTRtz1%PnV`-pqs-C>{?SVC|EP@0 zMeGR50iR0Qf-s=Y%6{U|a)dP^M^Zs9LjEF)la`fbrT$x>sk1#oO|Nqlqe7kT%c5M7 zx2gA09(PVqmuI_v2Tmcz$u-Iz^{>~)Xsm`>y)2<$iIzJ!UdZCPnL?v1AT-Nvf|SF7RH2&*1d&_9WyJTGS0HFT{AWf5=QGW^Zf>O; zmO3Qcg;tlY5z@Pla@bkeWmXs96_^P?^uVsn{=1KkL|<&qY~5Djl8GNrp6DjN=je8_ z`~NF5_MkVzHt-qk=btiok>x8z{#}a@m8xap+AiZS%fzbt8eE4PaAP8OpFCS@AiJe2 wJux?7UUko%OydV&<+?V1eIA#?_4GB}@aEjjG-=Cn_dLum(_3H*xWEkm0BY!n(f|Me literal 0 HcmV?d00001 diff --git a/roms/sc-games/piper.ch8 b/roms/sc-games/piper.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..b3087435e9fd1de38b981f5c2c0a51ecbc5e9305 GIT binary patch literal 1828 zcmc&!-Af!-6u&dGUs35!$C=GOB+H`fuGxmNnyqnF7ERhjRBVvA51}k~oLNjHHM?qB zDMjvRY)tb(YM>8!keFI_kWd1V5{QINEq#(O#HTe+)x5Q#C$l9L2=0C~^!SO+iWTcN5htFEn z8bPJ=D35EYrQa#}io+9Z>$K2?DOO)$hcKW|;Gi5+(6-Nz6nokgIVNEJGkjWR4Jqv! zv6sNFUk4oC#adoVV3kO^&pcF0DW}v}@xa!5llBs5lFzQ2!`a8Q2Sb2*SXfip>Acq-XH$q3`@Z9~YgE$xE&VKg8Fe1U^9 zt9vkScxg_(Bjajmt+x$W_2GV`IQluo*6&c*e!GI|+c%t(%x2_M{z`O%n@j2+Z2O9- z%V?Ir{Gm;rqS+dzMb-TCEl z)^v8Gz+n9;><;;f5u8AU(7x)M7_yrPnxWZI0s1}IxRkcK;U2E?S8WvI>w&hN2-97I z_xvXUCv05Gm_4RkT?6>lnreja!Sad9nw<#jfrDZFm?5G<;}YT1HGtxV7ZsqVu---em^kNN}gPZ)AtHi^)UM77T*F80}_hS^)4NE{z=&3;Ypk_1V> z+FU1^u;9CewSeAdy!k$oY#oebJA<@`lQ99mh?9Hk#*Yrg@&` zt>aVdyX_vw)^SfADro4Osg<WahUl3^@Wlb zPad{B_j{4&+i7c5@!TJUDMJN18`Vy-4{2q%WvJX;E5$D%Y6BCQ;xH;G2&R~UY35<7 zO_*j&%V|-1njTY%_h2br!O(M$8*q&jzY+~@!ijI~4L@?ylXb%h^pi#4`aDuO_^RqQ z*s}x+A}7q@Dw?XB_fIm1?0aE8lr!oceXI=kN&jJ!4t?x*uq%Hx2lYVD`E=#$ z$)aYj@}U?}POGhy6O;|8P%h_o#o~6wLOZhYO00^{vNkqoXk)|L5x0}$<^Y@dT<#IP zX-OKSU>Kj&nHeF0_ZM*(p;ozPkPM?bQd1H-wG{x9>G{EG#T^_dZ>GGW4`__1@}RHy3Yy h@pNrvEztI2^_!LFh0h;f`ql4Gm&@fes{Nmye*o+ti~9fo literal 0 HcmV?d00001 diff --git a/roms/sc-games/race.ch8 b/roms/sc-games/race.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..aedd30497f0b2d2f17ef8565294b4edf8ba3452f GIT binary patch literal 148 zcmZShpK6n)n{Lw_(2}sI@+oi@fNt!(Vh#Bf)lTgY~OHjj0Q8Te;g9*uin3ScY zA?B4tRFk59m?|wvR-+al7e!x2?7MaX$J6)^?uoohpZ zeVF=r=jyz7?>Xn5bMEh)d)4McK6jh9?%2`JzEZlf#N;_`I@IOv?)IMCSJUG@+17db z8Aw6II$XF{ZQ)%k2uvr8oJbg+c=^}x%k<0b0d)n@xWyibGlf@pr{Hygn)Rgw##U`H z6m^*W&o=S(-+F0KzgL1>id=?Vj$HVNcL`pfNnl(7h!$Q^H}f?Dv22E@~?wY!1% zA>ffNsq`Ce! z$n}$~xWi-!jJ1E=jy1XSum0X6Z-Z&QPaZd*y*yW8X}49_Dl+H5EZQwSWKPb*@5etS zFhs^ug5Fk5G&JOz$qtC}O>wgN_+%>1lmf2tY@Wc7bm;K<;7t6h0y81LO`WUd>lX}Z ztRf@F$?P2Pbulkj?#t@UM^6kZ67-rrb6>B7Z;CPIz8aUNvBrs9jr;=gZcTR0F63fl z8?sfCeGsD#!8sLQ8^bE(CYwyT$@Q#B#zhbKT7qPZ(KH?hbF2CFx2P(q24|W^+y|LF zN#6Wxf+3#H52EDsLqMv(A2hWbR?DM`8^~JI$R@Z__a44#T4V0xn=-y3dW_O|j-@mn z%LNjZ)RcPCG3tlvd9-lI*n-7o`cgL>hrr zppTajX(d6wm*Nj{DuF4HppVA&i2u_@L{5y>br^9)U`RyVDaM}3$RsGUrDf=!$YS1i zRKxJ*woV{e{1W*31Y$9ovt*#*j?W22b*I$*lwGRb1<@DL+Z`XVAc$bMkS-CC89`g8 z%hUp*HG8!Fn&*)BYW*eq(slX)cx(T#8E@^44ZJhnSw-XuW?W195dQ}-b{1dj-Wy|z z9uVw_jv!70!JO=gd3%#Ja-FVbn@&fpXdqof(DzbBXupnV(e0u*D~^1mt7Vn=r8C}L zKD-kxJ_;i-mXyGIyJ3AJ>HPE%JxFtDS}H|VY`?=B$UOyLa}8yh8!>c z+ly~V(GGjWg?`+xc&O3GUs!wPz7!3*-(WsoH(bl#a!Vz;u}q$$OFACkg*|QQI-Ljp z=(h%Pm-s3gzA*R7@kXn3UqaOSQ>(_h$FgWWj`M6=1#;CIlR2`!0{elS-sDzZTiOy$ zo@YQg?KWL~yhSd_!!|N?G2YA$oNm zX1`S~$@>aD944JP1npq6ouoKZA62f(ixauv9fI>H9cdV9jT-`kx12GCubke|meWU_ zFXdclf%jpgfol~EF)z^|5=y;`l_67tR>-13zaOHC9Y+d}6#v5ByhFLIJh#8xB9T3% zEHo)VCzCp}zyNA&BBJ}=WNotclS)yCoc`e_?FgL_e>hhtAkCSt zLP6A-SecpTzlcQMOb!@;2m~P18(I$E3ttXjUR@qoCiwuQkTNp>A+jW#z1W@T=DHJI ziHwx!?YMs)LJ+=0_L8x((C^;RLlzDh$Zmz^LO+%-%4M*qU(|2tS66qG5c+8R=FM@W z0*8<7X9pbF#+~`be4{bj_}O^b_)SB{DSuA~QYaJ-hvv_|;9EEs5y}=Kfm;(36LV)O z=jSgdLpcxUCa3;6^RJl?a#%t>M*+aEzZ7#6W z!Qj~eXJT+=VwkjmN~N+5+(~jAXFol$aAxOnZ0_N^N~QAg!*^%z-JPAicPZQO$FV=r zH?AN~1gYl7{LxSR8Lkl1kBRW;pt9^nc2Z zv*W&T$9Mq;2qFw(CpjE`|APm?V0N$|Xb<{=kzg{J9N6&hnDkGgIv5NkL;lZYpUVQt znU$GM`hokm!r|3{H=hLFzW;VC7KtDzNJ8T4@aodnq27hb;fKSEi<6JLyXL!2oXEd) acXlcm9KML82fdz4jWXZ2d&cem(f2>LB+}9V literal 0 HcmV?d00001 diff --git a/roms/sc-games/uboat.ch8 b/roms/sc-games/uboat.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..58c2d84a14ad77dbf4a2f218d256c8958505162b GIT binary patch literal 2016 zcmb7EO>7fa5T4!mZym?B{1;Rz6D8hkIg~CWw6sEG^W&1FP+N(Ope}URu>}Mg1?sfO zLh05{462-}iZ~!tMSvpY<-h?|D5`pJrK;+wN~*#EAr$q15Qo)L5RFiYJF|8Khh92s zy>DjT%)EIs^X!}0oqV$|)|+TMuzj1j2S)p2BT*O}+B*n)d-q3Eu>;W&h>t`^M^i^q z@qN9AK2Cj-iXI$FK&n3$KQw}diPVw9hh<3ZAL<|MjqXc*5Zxa~r-6}Jd^D9B97^Dd z6z@*})Y>mb9=1PMn$z3IAPZOdNM96u)3RgritmDK$2I3G!8*KvRRy#kABT->5i(2` zR8j2{q5Z*(dy3iV@KsJR+dF*UDc*FO8RtDS&Mzo$HRWMbz9T%N5A=$wt3l z!>l^#{@Cr!!Xxv9lc3LfXS+*nr2*}F**n)3xu@MI$4$J6vW)U|lo5|IGgBJrq`64L z%H(FIx*@PwoI*db9B6nFhd#xrE(laXFT2a`IsESZK|RB4eiDA~k^+wu=6rA$*6)}{ zyG8Xhk`f%RNiN=tUGy58eXxV&Mu+DKE4CF2W@4+b zcP(FkKRi7S!(WcWUDPRnQ(&;bfnueu@yn`wLhplKs0xxcLt3&I_k?noIa%S9(q0VfFGw4@ z&TnRyg#_qFp)jc}{B6eKOJ6YW*h1eP#U9i(owrO4PJCgKFHw{p2>nWk6~rl}leBrV zox=_hN{p~WVuf8B_MOiumJF1IRwj#CkS(*;x>pHgYso6J#->W(wk+Jo{%oprn;hv9 z#DT6Z&tWHMCDhbL=Vr{#xmK{A^^VZ1{FD)Lv$uCbtBg{zt_MBGbUQ)M;6!#Ijsd=7 z#57<}5fzwZL_`@Yg#E~G{L{!5BW(WLSX(cfJGuYZ`2qb{BWTz~XuX5HrNRG+yraSE z$V(0WIp~(xYwIAsSg%2~+T(MDd*CH8Dx-P~k3slSyTP$iQ+3N4?QdCYsR0TIjr2$;MhJzMbN+wtAj6jto7(G+8+h!@O$#KX_-0+vBu4tsbPh+q8-_ d4PGQzpP!#U314R~yp3;*=Oka(a9w{3wj=P>% z_RmaEC@z`pv*_ZCA95d3TNoM^d2l7l+>3)Wy;* z0A}MIUdeSV|X}E=ztl6(1A*(G`7y9M9zov(m`TZ%o&8P zRQ^k6>n!@rZpm_DU`(7a+=%=QsXegcvV@06WDwt(yr7D{3I zB2mhybVq1e3fC8jTbsLByG2260r{#Sv?0Ks;X|mfP8}n|yT##WYHFCy+SD@Lx$e&Z l;@`P0Y{J68@aGQ$1IvR44;DONaImPk_wQeg#fJtM006S=lZ^lX literal 0 HcmV?d00001 diff --git a/roms/test_opcode.ch8 b/test_opcode.ch8 similarity index 100% rename from roms/test_opcode.ch8 rename to test_opcode.ch8