├── .circleci
└── config.yml
├── .gitignore
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── cmd
└── gopher-boy
│ ├── main.go
│ └── wasm_main.go
├── docs
├── debug.html
├── debugger
│ └── index.js
├── dino.gb
├── gopher-boy.png
├── gopher.png
├── hello.gb
├── index.html
├── index.js
├── main.wasm
├── opus5.gb
├── style.css
├── tobu.gb
└── wasm_exec.js
├── go.mod
├── go.sum
├── makefile
├── pkg
├── bus
│ ├── bus.go
│ └── bus_test.go
├── cartridge
│ ├── cartridge.go
│ ├── mbc.go
│ ├── mbc0.go
│ └── mbc1.go
├── constants
│ └── window.go
├── cpu
│ ├── cpu.go
│ ├── cpu_test.go
│ └── helpers.go
├── gb
│ ├── gb.go
│ └── gb_test.go
├── gpu
│ ├── gpu.go
│ └── gpu_test.go
├── interfaces
│ ├── bus
│ │ └── bus.go
│ ├── interrupt
│ │ └── interrupt.go
│ ├── logger
│ │ └── logger.go
│ ├── pad
│ │ └── pad.go
│ └── window
│ │ └── window.go
├── interrupt
│ └── interrupt.go
├── logger
│ └── logger.go
├── mocks
│ └── bus.go
├── pad
│ └── pad.go
├── ram
│ └── ram.go
├── rom
│ └── rom.go
├── serial
│ └── serial.go
├── timer
│ └── timer.go
├── types
│ └── types.go
├── utils
│ ├── byte.go
│ └── loader.go
└── window
│ ├── native.go
│ ├── wasm.go
│ └── window.go
├── roms
├── acceptance
│ ├── add_sp_e_timing.gb
│ ├── bits
│ │ ├── mem_oam.gb
│ │ ├── reg_f.gb
│ │ └── unused_hwio-GS.gb
│ ├── boot_div-S.gb
│ ├── boot_div-dmg0.gb
│ ├── boot_div-dmgABCmgb.gb
│ ├── boot_div2-S.gb
│ ├── boot_hwio-S.gb
│ ├── boot_hwio-dmg0.gb
│ ├── boot_hwio-dmgABCmgb.gb
│ ├── boot_regs-dmg0.gb
│ ├── boot_regs-dmgABC.gb
│ ├── boot_regs-mgb.gb
│ ├── boot_regs-sgb.gb
│ ├── boot_regs-sgb2.gb
│ ├── call_cc_timing.gb
│ ├── call_cc_timing2.gb
│ ├── call_timing.gb
│ ├── call_timing2.gb
│ ├── di_timing-GS.gb
│ ├── div_timing.gb
│ ├── ei_sequence.gb
│ ├── ei_timing.gb
│ ├── halt_ime0_ei.gb
│ ├── halt_ime0_nointr_timing.gb
│ ├── halt_ime1_timing.gb
│ ├── halt_ime1_timing2-GS.gb
│ ├── if_ie_registers.gb
│ ├── instr
│ │ └── daa.gb
│ ├── interrupts
│ │ └── ie_push.gb
│ ├── intr_timing.gb
│ ├── jp_cc_timing.gb
│ ├── jp_timing.gb
│ ├── ld_hl_sp_e_timing.gb
│ ├── oam_dma
│ │ ├── basic.gb
│ │ ├── reg_read.gb
│ │ └── sources-dmgABCmgbS.gb
│ ├── oam_dma_restart.gb
│ ├── oam_dma_start.gb
│ ├── oam_dma_timing.gb
│ ├── pop_timing.gb
│ ├── ppu
│ │ ├── hblank_ly_scx_timing-GS.gb
│ │ ├── intr_1_2_timing-GS.gb
│ │ ├── intr_2_0_timing.gb
│ │ ├── intr_2_mode0_timing.gb
│ │ ├── intr_2_mode0_timing_sprites.gb
│ │ ├── intr_2_mode3_timing.gb
│ │ ├── intr_2_oam_ok_timing.gb
│ │ ├── lcdon_timing-dmgABCmgbS.gb
│ │ ├── lcdon_write_timing-GS.gb
│ │ ├── stat_irq_blocking.gb
│ │ ├── stat_lyc_onoff.gb
│ │ └── vblank_stat_intr-GS.gb
│ ├── push_timing.gb
│ ├── rapid_di_ei.gb
│ ├── ret_cc_timing.gb
│ ├── ret_timing.gb
│ ├── reti_intr_timing.gb
│ ├── reti_timing.gb
│ ├── rst_timing.gb
│ ├── serial
│ │ └── boot_sclk_align-dmgABCmgb.gb
│ └── timer
│ │ ├── div_write.gb
│ │ ├── rapid_toggle.gb
│ │ ├── tim00.gb
│ │ ├── tim00_div_trigger.gb
│ │ ├── tim01.gb
│ │ ├── tim01_div_trigger.gb
│ │ ├── tim10.gb
│ │ ├── tim10_div_trigger.gb
│ │ ├── tim11.gb
│ │ ├── tim11_div_trigger.gb
│ │ ├── tima_reload.gb
│ │ ├── tima_write_reloading.gb
│ │ └── tma_write_reloading.gb
├── cpu_instrs
│ ├── 01-special.gb
│ ├── 02-interrupts.gb
│ ├── 03-op sp,hl.gb
│ ├── 04-op r,imm.gb
│ ├── 05-op rp.gb
│ ├── 06-ld r,r.gb
│ ├── 07-jr,jp,call,ret,rst.gb
│ ├── 08-misc instrs.gb
│ ├── 09-op r,r.gb
│ ├── 10-bit ops.gb
│ ├── 11-op a,(hl).gb
│ └── cpu_instrs.gb
├── dino
│ └── dino.gb
├── emulator-only
│ ├── mbc1
│ │ ├── bits_bank1.gb
│ │ ├── bits_bank2.gb
│ │ ├── bits_mode.gb
│ │ ├── bits_ramg.gb
│ │ ├── multicart_rom_8Mb.gb
│ │ ├── ram_256kb.gb
│ │ ├── ram_64kb.gb
│ │ ├── rom_16Mb.gb
│ │ ├── rom_1Mb.gb
│ │ ├── rom_2Mb.gb
│ │ ├── rom_4Mb.gb
│ │ ├── rom_512kb.gb
│ │ └── rom_8Mb.gb
│ ├── mbc2
│ │ ├── bits_ramg.gb
│ │ ├── bits_romb.gb
│ │ ├── bits_unused.gb
│ │ ├── ram.gb
│ │ ├── rom_1Mb.gb
│ │ ├── rom_2Mb.gb
│ │ └── rom_512kb.gb
│ └── mbc5
│ │ ├── rom_16Mb.gb
│ │ ├── rom_1Mb.gb
│ │ ├── rom_2Mb.gb
│ │ ├── rom_32Mb.gb
│ │ ├── rom_4Mb.gb
│ │ ├── rom_512kb.gb
│ │ ├── rom_64Mb.gb
│ │ └── rom_8Mb.gb
├── hangman
│ └── Hangman (PD).gb
├── helloworld
│ ├── LICENSE
│ ├── README.md
│ ├── hardware.inc
│ ├── hello.gb
│ ├── hello.o
│ ├── hello.s
│ ├── make.cmd
│ └── speccy.chr
├── instr_timing
│ ├── instr_timing.gb
│ ├── readme.txt
│ └── source
│ │ ├── common
│ │ ├── build_gbs.s
│ │ ├── build_rom.s
│ │ ├── console.bin
│ │ ├── console.s
│ │ ├── crc.s
│ │ ├── delay.s
│ │ ├── gb.inc
│ │ ├── macros.inc
│ │ ├── numbers.s
│ │ ├── printing.s
│ │ ├── runtime.s
│ │ ├── testing.s
│ │ └── timer.s
│ │ ├── instr_timing.s
│ │ ├── linkfile
│ │ └── shell.inc
├── interrupt_time
│ ├── interrupt_time.gb
│ └── interrupt_time.s
├── mbc1
│ ├── bits_bank1.gb
│ ├── bits_bank2.gb
│ ├── bits_mode.gb
│ ├── bits_ramg.gb
│ ├── multicart_rom_8Mb.gb
│ ├── ram_256kb.gb
│ ├── ram_64kb.gb
│ ├── rom_16Mb.gb
│ ├── rom_1Mb.gb
│ ├── rom_2Mb.gb
│ ├── rom_4Mb.gb
│ ├── rom_512kb.gb
│ └── rom_8Mb.gb
├── mem_timing
│ ├── individual
│ │ ├── 01-read_timing.gb
│ │ ├── 02-write_timing.gb
│ │ └── 03-modify_timing.gb
│ ├── mem_timing.gb
│ ├── readme.txt
│ └── source
│ │ ├── 01-read_timing.s
│ │ ├── 02-write_timing.s
│ │ ├── 03-modify_timing.s
│ │ ├── common
│ │ ├── build_gbs.s
│ │ ├── build_rom.s
│ │ ├── console.bin
│ │ ├── console.s
│ │ ├── crc.s
│ │ ├── delay.s
│ │ ├── gb.inc
│ │ ├── macros.inc
│ │ ├── numbers.s
│ │ ├── printing.s
│ │ ├── runtime.s
│ │ ├── testing.s
│ │ └── tima_64.s
│ │ ├── linkfile
│ │ └── shell.inc
├── opus5
│ └── opus5.gb
├── tictactoe
│ └── GB-TicTacToe.gb
└── tobu
│ └── tobu.gb
├── screenshot
├── dino.png
├── drmario.png
├── mario.png
├── tetris.png
└── tobu.png
└── test
├── actual
├── bits_bank1.png
├── cpu_instr.png
├── daa.png
├── div_write.png
├── hello_world.png
├── if_ie_registers.png
├── mem_oam.png
├── opus5.png
├── reg_f.png
├── tictactoe.png
├── tim00.png
├── tim01.png
├── tim10.png
├── tim11.png
└── tobu.png
├── diff
├── bits_bank1.png
├── cpu_instr.png
├── hello_world.png
├── opus5.png
└── tictactoe.png
└── expect
├── bits_bank1.png
├── cpu_instr.png
├── daa.png
├── div_write.png
├── hello_world.png
├── if_ie_registers.png
├── mem_oam.png
├── opus5.png
├── reg_f.png
├── tictactoe.png
├── tim00.png
├── tim01.png
├── tim10.png
├── tim11.png
└── tobu.png
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | defaults: &defaults
2 | working_directory: ~/gopher-boy
3 | docker:
4 | - image: bokuweb/node-go-with-browser:latest
5 | environment:
6 | TZ: Asia/Tokyo
7 | GOPATH: /home/circleci/go
8 | version: 2
9 | jobs:
10 | build:
11 | <<: *defaults
12 | steps:
13 | - checkout
14 | - run:
15 | name: Install dependencies
16 | command: apt-get install -yq xorg-dev libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libopenal-dev libasound2-dev libgl1-mesa-dev
17 | - run:
18 | name: Install reg
19 | command: npm i -g reg-cli
20 | - run:
21 | name: Test
22 | command: |
23 | make -B test
24 | - run:
25 | name: Reg
26 | command: reg-cli ./test/actual ./test/expect ./test/diff
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | reg.json
2 | logs/*.log
3 | *.sym
4 | *.wat
5 | logs
6 | gopher-boy
7 | docs/*.gb
8 | main.wat
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${workspaceRoot}/cmd/gopher-boy/main.go",
13 | "env": {
14 | "GO111MODULE": "on",
15 | "LEVEL": "info"
16 | },
17 | "args": ["${workspaceRoot}/roms/cpu_instrs/02-interrupts.gb"]
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 bokuweb
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gopher-boy
2 |
3 | [](https://circleci.com/gh/bokuweb/gopher-boy/tree/master) [](https://goreportcard.com/report/github.com/bokuweb/gopher-boy)
4 |
5 |
6 |
7 | ## Installation
8 |
9 | you can install `gopher-boy` with following comand.
10 |
11 | ```sh
12 | go get github.com/bokuweb/gopher-boy/cmd/gopher-boy
13 | ```
14 |
15 | This emulator uses the go library [pixel](https://github.com/faiface/pixel), which requires OpenGL. You may need to install some requirements which can be found on the [pixels readme](https://github.com/faiface/pixel#requirements).
16 |
17 | ## Requirements
18 |
19 | - go1.12.7
20 |
21 | ## Development
22 |
23 | #### Native
24 |
25 | ```
26 | GO111MODULE=on go run -tags="native" cmd/gopher-boy/main.go hello.gb
27 | ```
28 |
29 | #### WebAssembly
30 |
31 | ```
32 | make build-wasm
33 | make serve
34 | ```
35 |
36 | ## Usage
37 |
38 | ```sh
39 | gopher-boy YOUR_GAMEBOY_ROM.gb
40 | ```
41 |
42 | ### Keymap
43 |
44 | | keyboard | game pad |
45 | | -------------------- | ------------- |
46 | | ← | ← button |
47 | | ↑ | ↑ button |
48 | | ↓ | ↓ button |
49 | | → | → button |
50 | | Z | A button |
51 | | X | B button |
52 | | Enter | Start button |
53 | | Backspace | Select button |
54 |
55 | ## Testing
56 |
57 | ```
58 | make test
59 | ```
60 |
61 | ### Current status
62 |
63 | #### Blargg's test ROM
64 |
65 | | ROM | Result |
66 | | ------------ | --------- |
67 | | cpu_instrs | ✅ |
68 | | instr_timing | ❌ (#255) |
69 |
70 | #### mooneye-gb's test ROM
71 |
72 | | ROM | Result |
73 | | ----------------------------- | ------ |
74 | | emulator-only/mbc1/bits_bank1 | ✅ |
75 | | acceptance/instr/daa | ✅ |
76 | | acceptance/timer/div_write | ✅ |
77 | | acceptance/timer/tim00 | ✅ |
78 | | acceptance/timer/tim01 | ✅ |
79 | | acceptance/timer/tim10 | ✅ |
80 | | acceptance/timer/tim11 | ✅ |
81 | | acceptance/if_ie_registers | ✅ |
82 |
83 | ### Visual regression test
84 |
85 | Please install `reg-cli`.
86 |
87 | ```sh
88 | npm i -g reg-cli
89 | ```
90 |
91 | Execute regression test
92 |
93 | ```sh
94 | make reg
95 | ```
96 |
97 | Update expected images
98 |
99 | ```sh
100 | make reg-update
101 | ```
102 |
103 | ## Credit
104 |
105 | "gopher" by Renée French CC-BY-3.0
106 |
107 | ## Known Bugs and TODO list
108 |
109 | PR welcome :)
110 |
111 | - [ ] Implement APU
112 | - [ ] Support scale option
113 | - [x] Support WebAssembly
114 | - [x] 8\*16 sprite
115 | - [ ] LCD interrupt
116 | - [ ] Keypad interrupt
117 | - [ ] cartridges
118 | - [x] Support ROM+MBC1+RAM+BATT catridge
119 | - [ ] Support ROM+MBC2 catridge
120 | - [ ] Support ROM+MBC2+BATTERY catridge
121 | - [ ] Support ROM+RAM catridge
122 | - [ ] Support ROM+RAM+BATTERY catridge
123 | - [ ] Support ROM+MMM01 catridge
124 | - [ ] Support ROM+MMM01+SRAM catridge
125 | - [ ] Support ROM+MMM01+SRAM+BATT catridge
126 | - [ ] Support ROM+MBC3+RAM catridge
127 | - [ ] Support ROM+MBC3+RAM+BATT catridge
128 | - [ ] Support ROM+MBC5 catridge
129 | - [ ] Support ROM+MBC5+RAM catridge
130 | - [ ] Support ROM+MBC5+RAM+BATT catridge
131 | - [ ] Support ROM+MBC5+RUMBLE catridge
132 | - [ ] Support ROM+MBC5+RUMBLE+SRAM catridge
133 | - [ ] Support ROM+MBC5+RUMBLE+SRAM+BATT catridge
134 | - [ ] Support Pocket Camera catridge
135 | - [ ] Support Bandai TAMA5 catridge
136 | - [ ] Support Hudson HuC-3 catridge
137 |
--------------------------------------------------------------------------------
/cmd/gopher-boy/main.go:
--------------------------------------------------------------------------------
1 | // +build native
2 |
3 | package main
4 |
5 | import (
6 | "errors"
7 | "log"
8 | "os"
9 |
10 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
11 | "github.com/bokuweb/gopher-boy/pkg/pad"
12 |
13 | "github.com/bokuweb/gopher-boy/pkg/gpu"
14 | "github.com/bokuweb/gopher-boy/pkg/timer"
15 | "github.com/bokuweb/gopher-boy/pkg/utils"
16 |
17 | "github.com/bokuweb/gopher-boy/pkg/cpu"
18 | "github.com/bokuweb/gopher-boy/pkg/gb"
19 | "github.com/bokuweb/gopher-boy/pkg/logger"
20 | "github.com/bokuweb/gopher-boy/pkg/ram"
21 |
22 | "github.com/bokuweb/gopher-boy/pkg/bus"
23 | "github.com/bokuweb/gopher-boy/pkg/cartridge"
24 | "github.com/bokuweb/gopher-boy/pkg/window"
25 | )
26 |
27 | func main() {
28 | level := "Debug"
29 | if os.Getenv("LEVEL") != "" {
30 | level = os.Getenv("LEVEL")
31 | }
32 | l := logger.NewLogger(logger.LogLevel(level))
33 | if len(os.Args) != 2 {
34 | log.Fatalf("ERROR: %v", errors.New("Please specify the ROM"))
35 | }
36 | file := os.Args[1]
37 | log.Println(file)
38 | buf, err := utils.LoadROM(file)
39 | if err != nil {
40 | log.Fatalf("ERROR: %v", errors.New("Failed to load ROM"))
41 | }
42 | cart, err := cartridge.NewCartridge(buf)
43 | if err != nil {
44 | log.Fatalf("ERROR: %v", errors.New("Failed to create cartridge"))
45 | }
46 | vRAM := ram.NewRAM(0x2000)
47 | wRAM := ram.NewRAM(0x2000)
48 | hRAM := ram.NewRAM(0x80)
49 | oamRAM := ram.NewRAM(0xA0)
50 | gpu := gpu.NewGPU()
51 | t := timer.NewTimer()
52 | pad := pad.NewPad()
53 | irq := interrupt.NewInterrupt()
54 | b := bus.NewBus(l, cart, gpu, vRAM, wRAM, hRAM, oamRAM, t, irq, pad)
55 | gpu.Init(b, irq)
56 | win := window.NewWindow(pad)
57 | emu := gb.NewGB(cpu.NewCPU(l, b, irq), gpu, t, irq, win)
58 | win.Run(func() {
59 | win.Init()
60 | emu.Start()
61 | })
62 | }
63 |
--------------------------------------------------------------------------------
/cmd/gopher-boy/wasm_main.go:
--------------------------------------------------------------------------------
1 | // +build wasm
2 |
3 | package main
4 |
5 | import (
6 | "errors"
7 |
8 | // "image/color"
9 | "log"
10 | "syscall/js"
11 |
12 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
13 | "github.com/bokuweb/gopher-boy/pkg/logger"
14 | "github.com/bokuweb/gopher-boy/pkg/pad"
15 | "github.com/bokuweb/gopher-boy/pkg/types"
16 | "github.com/bokuweb/gopher-boy/pkg/window"
17 |
18 | "github.com/bokuweb/gopher-boy/pkg/gpu"
19 | "github.com/bokuweb/gopher-boy/pkg/timer"
20 |
21 | "github.com/bokuweb/gopher-boy/pkg/cpu"
22 | "github.com/bokuweb/gopher-boy/pkg/gb"
23 | "github.com/bokuweb/gopher-boy/pkg/ram"
24 |
25 | "github.com/bokuweb/gopher-boy/pkg/bus"
26 | "github.com/bokuweb/gopher-boy/pkg/cartridge"
27 | )
28 |
29 | func newGB(this js.Value, args []js.Value) interface{} {
30 | buf := []byte{}
31 | for i := 0; i < args[0].Get("length").Int(); i++ {
32 | buf = append(buf, byte(args[0].Index(i).Int()))
33 | }
34 | l := logger.NewLogger(logger.LogLevel("INFO"))
35 | cart, err := cartridge.NewCartridge(buf)
36 | if err != nil {
37 | log.Fatalf("ERROR: %v", errors.New("Failed to create cartridge"))
38 | }
39 | vRAM := ram.NewRAM(0x2000)
40 | wRAM := ram.NewRAM(0x2000)
41 | hRAM := ram.NewRAM(0x80)
42 | oamRAM := ram.NewRAM(0xA0)
43 | gpu := gpu.NewGPU()
44 | t := timer.NewTimer()
45 | pad := pad.NewPad()
46 | irq := interrupt.NewInterrupt()
47 | b := bus.NewBus(l, cart, gpu, vRAM, wRAM, hRAM, oamRAM, t, irq, pad)
48 | gpu.Init(b, irq)
49 |
50 | win := window.NewWindow(pad)
51 | emu := gb.NewGB(cpu.NewCPU(l, b, irq), gpu, t, irq, win)
52 |
53 | this.Set("next", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
54 | img := emu.Next()
55 | return js.CopyBytesToJS(args[0], img)
56 | }))
57 | this.Set("keyDown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
58 | win.KeyDown(byte(args[0].Int()))
59 | return nil
60 | }))
61 | this.Set("keyUp", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
62 | win.KeyUp(byte(args[0].Int()))
63 | return nil
64 | }))
65 | // for Debuging
66 | this.Set("getVRAM", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
67 | d := vRAM.GetBuf()
68 | return js.CopyBytesToJS(args[0], d)
69 | }))
70 | this.Set("getOAMRAM", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
71 | d := oamRAM.GetBuf()
72 | return js.CopyBytesToJS(args[0], d)
73 | }))
74 | this.Set("readGPU", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
75 | return gpu.Read(types.Word(args[0].Int()))
76 | }))
77 | return this
78 | }
79 |
80 | func main() {
81 | w := js.Global()
82 | w.Set("GB", js.FuncOf(newGB))
83 | select {}
84 | }
85 |
--------------------------------------------------------------------------------
/docs/dino.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/dino.gb
--------------------------------------------------------------------------------
/docs/gopher-boy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/gopher-boy.png
--------------------------------------------------------------------------------
/docs/gopher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/gopher.png
--------------------------------------------------------------------------------
/docs/hello.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/hello.gb
--------------------------------------------------------------------------------
/docs/index.js:
--------------------------------------------------------------------------------
1 | import { renderDebugInfo } from "./debugger/index.js";
2 |
3 | window.onload = async () => {
4 | document.addEventListener(
5 | "touchmove",
6 | function(event) {
7 | if (event.scale !== 1) {
8 | event.preventDefault();
9 | }
10 | },
11 | false
12 | );
13 |
14 | const go = new Go();
15 | const res = await fetch("./main.wasm");
16 | const bytes = await res.arrayBuffer();
17 | const { instance } = await WebAssembly.instantiate(bytes, go.importObject);
18 | go.run(instance);
19 |
20 | const canvas = document.querySelector(".game");
21 | const input = document.querySelector("#file_upload");
22 |
23 | // GPU
24 |
25 | const init = async buf => {
26 | const ctx = canvas.getContext("2d");
27 | const image = ctx.createImageData(160, 144);
28 |
29 | if (!buf) {
30 | const rom = await fetch("./tobu.gb");
31 | buf = await rom.arrayBuffer();
32 | }
33 | let gb = new GB(new Uint8Array(buf));
34 |
35 | document.querySelector(".led").style.background = "red";
36 |
37 | const frame = () => {
38 | if (!gb) return;
39 | gb.next(image.data);
40 | ctx.putImageData(image, 0, 0);
41 | renderDebugInfo(gb);
42 | window.requestAnimationFrame(frame);
43 | };
44 | frame();
45 |
46 | const onKeydown = e => {
47 | switch (e.key) {
48 | case "z":
49 | return gb.keyDown(0x01);
50 | case "x":
51 | return gb.keyDown(0x02);
52 | case "Backspace":
53 | return gb.keyDown(0x04);
54 | case "Enter":
55 | return gb.keyDown(0x08);
56 | case "ArrowLeft":
57 | return gb.keyDown(0x20);
58 | case "ArrowUp":
59 | return gb.keyDown(0x40);
60 | case "ArrowRight":
61 | return gb.keyDown(0x10);
62 | case "ArrowDown":
63 | return gb.keyDown(0x80);
64 | }
65 | };
66 |
67 | const onKeyup = e => {
68 | switch (e.key) {
69 | case "z":
70 | return gb.keyUp(0x01);
71 | case "x":
72 | return gb.keyUp(0x02);
73 | case "Backspace":
74 | return gb.keyUp(0x04);
75 | case "Enter":
76 | return gb.keyUp(0x08);
77 | case "ArrowLeft":
78 | return gb.keyUp(0x20);
79 | case "ArrowUp":
80 | return gb.keyUp(0x40);
81 | case "ArrowRight":
82 | return gb.keyUp(0x10);
83 | case "ArrowDown":
84 | return gb.keyUp(0x80);
85 | }
86 | };
87 |
88 | const removeHandler = classname => {
89 | const el = document.querySelector(`.${classname}`);
90 | const elClone = el.cloneNode(true);
91 | el.parentNode.replaceChild(elClone, el);
92 | };
93 |
94 | const cleanup = () => {
95 | input.removeEventListener("change", onFileChange);
96 | window.removeEventListener("keydown", onKeydown);
97 | window.removeEventListener("keyup", onKeyup);
98 | removeHandler("buttonA");
99 | removeHandler("buttonB");
100 | removeHandler("buttonSelect");
101 | removeHandler("buttonStart");
102 | removeHandler("right");
103 | removeHandler("left");
104 | removeHandler("up");
105 | removeHandler("down");
106 | gb = null;
107 | };
108 |
109 | window.addEventListener("keydown", onKeydown);
110 |
111 | window.addEventListener("keyup", onKeyup);
112 |
113 | const addHandler = (classname, bit) => {
114 | const el = document.querySelector(`.${classname}`);
115 | el.addEventListener("mousedown", e => {
116 | return gb.keyDown(bit);
117 | });
118 | el.addEventListener(
119 | "touchstart",
120 | e => {
121 | e.preventDefault();
122 | navigator.vibrate && navigator.vibrate(30);
123 | return gb.keyDown(bit);
124 | },
125 | true
126 | );
127 |
128 | el.addEventListener("mouseup", e => {
129 | return gb.keyUp(bit);
130 | });
131 | el.addEventListener(
132 | "touchend",
133 | e => {
134 | e.preventDefault();
135 | return gb.keyUp(bit);
136 | },
137 | true
138 | );
139 | };
140 |
141 | addHandler("buttonA", 0x01);
142 | addHandler("buttonB", 0x02);
143 | addHandler("buttonSelect", 0x04);
144 | addHandler("buttonStart", 0x08);
145 |
146 | addHandler("right", 0x10);
147 | addHandler("left", 0x20);
148 | addHandler("up", 0x40);
149 | addHandler("down", 0x80);
150 | };
151 |
152 | init();
153 | };
154 |
--------------------------------------------------------------------------------
/docs/main.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/main.wasm
--------------------------------------------------------------------------------
/docs/opus5.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/opus5.gb
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | padding: 0;
3 | margin: 0;
4 |
5 | -webkit-text-size-adjust: none;
6 | touch-action: manipulation;
7 | }
8 |
9 | .title {
10 | position: fixed;
11 | top: 48px;
12 | left: 48px;
13 | display: flex;
14 | align-items: center;
15 | }
16 |
17 | .title h1 {
18 | font-size: 32px;
19 | color: white;
20 | padding-top: 12px;
21 | padding-left: 12px;
22 | }
23 |
24 | .title img {
25 | height: 64px;
26 | width: auto;
27 | }
28 |
29 | .content {
30 | width: 400px;
31 | height: 680px;
32 | display: flex;
33 | flex-direction: column;
34 | align-items: center;
35 | }
36 |
37 | .content div.control {
38 | display: flex;
39 | width: 400px;
40 | height: 160px;
41 | align-items: center;
42 | justify-content: center;
43 | }
44 |
45 | .content input {
46 | display: none;
47 | }
48 |
49 | .gameboy {
50 | height: 480px;
51 | width: 300px;
52 | position: relative;
53 | background: #eee;
54 | border-radius: 10px 10px 50px 10px;
55 | overflow: hidden;
56 | box-shadow: 5px 5px rgba(0, 0, 0, 0.1);
57 | /*cursor: pointer*/
58 | }
59 | .gameboy:before {
60 | position: absolute;
61 | width: 500px;
62 | height: 20px;
63 | border-bottom: 3px solid #e0e0e0;
64 | content: "";
65 | }
66 | .gameboy:after {
67 | position: absolute;
68 | width: 240px;
69 | height: 20px;
70 | left: 24px;
71 | border-left: 3px solid #e0e0e0;
72 | border-right: 3px solid #e0e0e0;
73 | content: "";
74 | }
75 | .screen {
76 | position: absolute;
77 | width: 252px;
78 | height: 188px;
79 | left: 24px;
80 | top: 40px;
81 | background: #777;
82 | border-radius: 5px 5px 40px 5px;
83 | }
84 | canvas.game {
85 | background: #aec5a0;
86 | position: absolute;
87 | top: 22px;
88 | left: 48px;
89 | }
90 |
91 | .led {
92 | position: absolute;
93 | height: 8px;
94 | width: 8px;
95 | top: 107px;
96 | left: 42px;
97 | border-radius: 100%;
98 | box-shadow: 1px 1px 0 #400 inset;
99 | background: #600;
100 | content: "";
101 | }
102 |
103 | .pad {
104 | position: absolute;
105 | width: 26px;
106 | height: 80px;
107 | top: 296px;
108 | left: 50px;
109 | background: #444;
110 | border-radius: 3px;
111 | z-index: 998;
112 | }
113 | .pad:after {
114 | position: absolute;
115 | width: 80px;
116 | height: 26px;
117 | top: 26px;
118 | left: -26px;
119 | background: #444;
120 | border-radius: 3px;
121 | content: "";
122 | z-index: 999;
123 | }
124 | .pad:before {
125 | position: absolute;
126 | height: 26px;
127 | width: 26px;
128 | top: 26px;
129 | border-radius: 100%;
130 | background: #333;
131 | content: "";
132 | z-index: 1000;
133 | }
134 |
135 | .up {
136 | position: absolute;
137 | width: 46px;
138 | height: 50px;
139 | top: 276px;
140 | left: 40px;
141 | border-radius: 3px;
142 | z-index: 998;
143 | }
144 |
145 | .down {
146 | position: absolute;
147 | width: 46px;
148 | height: 50px;
149 | top: 350px;
150 | left: 40px;
151 | border-radius: 3px;
152 | z-index: 998;
153 | }
154 |
155 | .right {
156 | position: absolute;
157 | width: 46px;
158 | height: 50px;
159 | top: 310px;
160 | left: 78px;
161 | border-radius: 3px;
162 | z-index: 998;
163 | }
164 |
165 | .left {
166 | position: absolute;
167 | width: 46px;
168 | height: 50px;
169 | top: 310px;
170 | left: 8px;
171 | border-radius: 3px;
172 | z-index: 998;
173 | }
174 |
175 | .hole {
176 | position: absolute;
177 | height: 46px;
178 | width: 6px;
179 | top: 436px;
180 | left: 190px;
181 | border-radius: 2px;
182 | background: #ddd;
183 | -webkit-transform: rotate(-30deg);
184 | box-shadow: 0px 0px #ddd, 17px 0px #ddd, 34px 0px #ddd, 51px 0px #ddd,
185 | 68px 0px #ddd, 85px 0px #ddd;
186 | content: "";
187 | }
188 | .buttonB {
189 | position: absolute;
190 | width: 36px;
191 | height: 36px;
192 | top: 322px;
193 | right: 69px;
194 | border-radius: 100%;
195 | background: #a93671;
196 | }
197 |
198 | .buttonA {
199 | position: absolute;
200 | width: 36px;
201 | height: 36px;
202 | top: 300px;
203 | right: 24px;
204 | border-radius: 100%;
205 | background: #a93671;
206 | }
207 |
208 | .buttonSelect {
209 | position: absolute;
210 | height: 40px;
211 | width: 12px;
212 | bottom: 52px;
213 | left: 102px;
214 | background: #999;
215 | border-radius: 10px;
216 | -webkit-transform: rotate(63deg);
217 | cursor: pointer;
218 | }
219 |
220 | .buttonStart {
221 | position: absolute;
222 | height: 40px;
223 | width: 12px;
224 | bottom: 58px;
225 | left: 156px;
226 | background: #999;
227 | border-radius: 10px;
228 | -webkit-transform: rotate(63deg);
229 | cursor: pointer;
230 | }
231 |
232 | .github-link {
233 | position: fixed;
234 | bottom: 68px;
235 | right: 68px;
236 | color: black;
237 | text-decoration: none;
238 | }
239 |
240 | .nes-octocat {
241 | position: absolute;
242 | top: 88px;
243 | left: 170px;
244 | }
245 |
246 | .nes-select {
247 | width: 160px;
248 | margin-left: 16px;
249 | }
250 |
251 | @media screen and (max-width: 780px) {
252 | .title {
253 | display: none;
254 | }
255 |
256 | .github-link {
257 | display: none;
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/docs/tobu.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/docs/tobu.gb
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/bokuweb/gopher-boy
2 |
3 | require (
4 | github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 // indirect
5 | github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // indirect
6 | github.com/faiface/pixel v0.8.0
7 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 // indirect
8 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 // indirect
9 | github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 // indirect
10 | github.com/pkg/errors v0.8.1 // indirect
11 | github.com/stretchr/testify v1.3.0
12 | golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
13 | )
14 |
15 | go 1.13
16 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 h1:FvZ0mIGh6b3kOITxUnxS3tLZMh7yEoHo75v3/AgUqg0=
4 | github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380/go.mod h1:zqnPFFIuYFFxl7uH2gYByJwIVKG7fRqlqQCbzAnHs9g=
5 | github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q=
6 | github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
7 | github.com/faiface/pixel v0.8.0 h1:phOHW6ixfMAKRamjnvhI6FFI2VRyPEq7+LmmkDGXB/4=
8 | github.com/faiface/pixel v0.8.0/go.mod h1:CEUU/s9E82Kqp01Boj1O67KnBskqiLghANqvUJGgDAM=
9 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
10 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
11 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
12 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
13 | github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 h1:THttjeRn1iiz69E875U6gAik8KTWk/JYAHoSVpUxBBI=
14 | github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
15 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
16 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
19 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
21 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
22 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
23 | golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f h1:FO4MZ3N56GnxbqxGKqh+YTzUWQ2sDwtFQEZgLOxh9Jc=
24 | golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
25 | golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff h1:+2zgJKVDVAz/BWSsuniCmU1kLCjL88Z8/kv39xCI9NQ=
26 | golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
27 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
28 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all clean
2 |
3 | test:
4 | GO111MODULE=on go test -tags=native github.com/bokuweb/gopher-boy/...
5 |
6 | reg:
7 | reg-cli ./test/actual ./test/expect ./test/diff
8 |
9 | reg-update:
10 | reg-cli ./test/actual ./test/expect ./test/diff -U
11 |
12 | build:
13 | GO111MODULE=on go build -tags="native" -o "gopher-boy" "cmd/gopher-boy/main.go"
14 |
15 | build-wasm:
16 | GOOS=js GOARCH=wasm go build -tags=wasm -o "docs/main.wasm" "cmd/gopher-boy/wasm_main.go"
17 |
18 | serve:
19 | xdg-open 'http://localhost:5000'
20 | serve -r docs -a :5000 || (go get -v github.com/mattn/serve && serve -r docs -a :5000)
21 |
22 | clean:
23 | rm -f *.wasm
--------------------------------------------------------------------------------
/pkg/bus/bus.go:
--------------------------------------------------------------------------------
1 | package bus
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/interfaces/pad"
5 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
6 |
7 | "github.com/bokuweb/gopher-boy/pkg/cartridge"
8 | "github.com/bokuweb/gopher-boy/pkg/gpu"
9 | "github.com/bokuweb/gopher-boy/pkg/interfaces/logger"
10 | "github.com/bokuweb/gopher-boy/pkg/ram"
11 | "github.com/bokuweb/gopher-boy/pkg/serial"
12 | "github.com/bokuweb/gopher-boy/pkg/timer"
13 | "github.com/bokuweb/gopher-boy/pkg/types"
14 | "github.com/bokuweb/gopher-boy/pkg/utils"
15 | )
16 |
17 | const (
18 | // DMGStatusReg is DMA status register
19 | DMGStatusReg types.Word = 0xFF50
20 | )
21 |
22 | // Bus is gb bus
23 | type Bus struct {
24 | logger logger.Logger
25 | bootmode bool
26 | cartridge *cartridge.Cartridge
27 | gpu *gpu.GPU
28 | vRAM *ram.RAM
29 | wRAM *ram.RAM
30 | hRAM *ram.RAM
31 | oamRAM *ram.RAM
32 | timer *timer.Timer
33 | irq *interrupt.Interrupt
34 | pad pad.Pad
35 | }
36 |
37 | /* --------------------------+
38 | | Interrupt Enable Register |
39 | ------------------------------ 0xFFFF
40 | | Internal RAM |
41 | ------------------------------ 0xFF80
42 | | Empty but unusable for I/O |
43 | ------------------------------ 0xFF4C
44 | | I/O ports |
45 | ------------------------------ 0xFF00
46 | | Empty but unusable for I/O |
47 | ------------------------------ 0xFEA0
48 | | Sprite Attrib Memory (OAM) |
49 | ------------------------------ 0xFE00
50 | | Echo of 8kB Internal RAM |
51 | ------------------------------ 0xE000
52 | | 8kB Internal RAM |
53 | ------------------------------ 0xC000
54 | | 8kB switchable RAM bank |
55 | ------------------------------ 0xA000
56 | | 8kB Video RAM |
57 | ------------------------------ 0x8000 --+
58 | | 16kB switchable ROM bank | |
59 | ------------------------------ 0x4000 | = 32kB Cartrigbe
60 | | 16kB ROM bank #0 | |
61 | ------------------------------ 0x0000 --+ */
62 |
63 | // NewBus is bus constructor
64 | func NewBus(
65 | logger logger.Logger,
66 | cartridge *cartridge.Cartridge,
67 | gpu *gpu.GPU,
68 | vram *ram.RAM,
69 | wram *ram.RAM,
70 | hRAM *ram.RAM,
71 | oamRAM *ram.RAM,
72 | timer *timer.Timer,
73 | irq *interrupt.Interrupt,
74 | pad pad.Pad) *Bus {
75 | return &Bus{
76 | logger: logger,
77 | bootmode: true,
78 | cartridge: cartridge,
79 | gpu: gpu,
80 | vRAM: vram,
81 | wRAM: wram,
82 | hRAM: hRAM,
83 | oamRAM: oamRAM,
84 | timer: timer,
85 | irq: irq,
86 | pad: pad,
87 | }
88 | }
89 |
90 | // ReadByte is byte data reader from bus
91 | func (b *Bus) ReadByte(addr types.Word) byte {
92 |
93 | switch {
94 | case addr >= 0x0000 && addr <= 0x7FFF:
95 | if b.bootmode && addr < 0x0100 {
96 | return BIOS[addr]
97 | }
98 | if addr == 0x0100 {
99 | b.bootmode = false
100 | }
101 | return b.cartridge.ReadByte(addr)
102 | // Video RAM
103 | case addr >= 0x8000 && addr <= 0x9FFF:
104 | return b.vRAM.Read(addr - 0x8000)
105 | case addr >= 0xA000 && addr <= 0xBFFF:
106 | return b.cartridge.ReadByte(addr)
107 | // Working RAM
108 | case addr >= 0xC000 && addr <= 0xDFFF:
109 | return b.wRAM.Read(addr - 0xC000)
110 | // Shadow
111 | case addr >= 0xE000 && addr <= 0xFDFF:
112 | return b.wRAM.Read(addr - 0xE000)
113 | // OAM
114 | case addr >= 0xFE00 && addr <= 0xFE9F:
115 | return b.oamRAM.Read(addr - 0xFE00)
116 | // Pad
117 | case addr == 0xFF00:
118 | return b.pad.Read()
119 | // Timer
120 | case addr >= 0xFF04 && addr <= 0xFF07:
121 | return b.timer.Read(addr - 0xFF00)
122 | // IF
123 | case addr == 0xFF0F:
124 | return b.irq.Read(addr - 0xFF00)
125 | // GPU
126 | case addr >= 0xFF40 && addr <= 0xFF7F:
127 | return b.gpu.Read(addr - 0xFF40)
128 | // Zero page RAM
129 | case addr >= 0xFF80 && addr <= 0xFFFE:
130 | return b.hRAM.Read(addr - 0xFF80)
131 | // IE
132 | case addr == 0xFFFF:
133 | return b.irq.Read(addr - 0xFF00)
134 | default:
135 | }
136 | return 0
137 | }
138 |
139 | // ReadWord is word data reader from bus
140 | func (b *Bus) ReadWord(addr types.Word) types.Word {
141 | l := b.ReadByte(addr)
142 | u := b.ReadByte(addr + 1)
143 | return utils.Bytes2Word(u, l)
144 | }
145 |
146 | // WriteByte is byte data writer to bus
147 | func (b *Bus) WriteByte(addr types.Word, data byte) {
148 | switch {
149 | case addr >= 0x0000 && addr <= 0x7FFF:
150 | b.cartridge.WriteByte(addr, data)
151 | // Video RAM
152 | case addr >= 0x8000 && addr <= 0x9FFF:
153 | b.vRAM.Write(addr-0x8000, data)
154 | case addr >= 0xA000 && addr <= 0xBFFF:
155 | b.cartridge.WriteByte(addr, data)
156 | // Working RAM
157 | case addr >= 0xC000 && addr <= 0xDFFF:
158 | b.wRAM.Write(addr-0xC000, data)
159 | // Shadow
160 | case addr >= 0xE000 && addr <= 0xFDFF:
161 | b.wRAM.Write(addr-0xE000, data)
162 | // OAM
163 | case addr >= 0xFE00 && addr <= 0xFE9F:
164 | b.oamRAM.Write(addr-0xFE00, data)
165 | // Pad
166 | case addr == 0xFF00:
167 | b.pad.Write(data)
168 | // Serial
169 | case addr == 0xFF01:
170 | serial.Send(data)
171 | // Timer
172 | case addr >= 0xFF04 && addr <= 0xFF07:
173 | b.timer.Write(addr-0xFF00, data)
174 | // IF
175 | case addr == 0xFF0F:
176 | b.irq.Write(addr-0xFF00, data)
177 | // GPU
178 | case addr >= 0xFF40 && addr <= 0xFF7F:
179 | b.gpu.Write(addr-0xFF40, data)
180 | //Zero page RAM
181 | case addr >= 0xFF80 && addr <= 0xFFFE:
182 | b.hRAM.Write(addr-0xFF80, data)
183 | // IE
184 | case addr == 0xFFFF:
185 | b.irq.Write(addr-0xFF00, data)
186 | default:
187 | // fmt.Printf("Error: You can not write 0x%X, this area is invalid or unimplemented area.\n", addr)
188 | }
189 | }
190 |
191 | // WriteWord is word data writer to bus
192 | func (b *Bus) WriteWord(addr types.Word, data types.Word) {
193 | upper, lower := utils.Word2Bytes(data)
194 | b.WriteByte(addr, lower)
195 | b.WriteByte(addr+1, upper)
196 | }
197 |
198 | // BIOS is
199 | var BIOS = []byte{ /* Removed*/ }
200 |
--------------------------------------------------------------------------------
/pkg/bus/bus_test.go:
--------------------------------------------------------------------------------
1 | package bus
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/bokuweb/gopher-boy/pkg/cartridge"
7 | "github.com/bokuweb/gopher-boy/pkg/gpu"
8 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
9 | "github.com/bokuweb/gopher-boy/pkg/logger"
10 | "github.com/bokuweb/gopher-boy/pkg/pad"
11 | "github.com/bokuweb/gopher-boy/pkg/ram"
12 | "github.com/bokuweb/gopher-boy/pkg/timer"
13 | "github.com/bokuweb/gopher-boy/pkg/types"
14 | "github.com/stretchr/testify/assert"
15 | )
16 |
17 | func setup() (*Bus, *ram.RAM, *ram.RAM) {
18 | buf := make([]byte, 0x8000)
19 | cart, _ := cartridge.NewCartridge(buf)
20 | vRAM := ram.NewRAM(0x2000)
21 | wRAM := ram.NewRAM(0x2000)
22 | hRAM := ram.NewRAM(0x80)
23 | oamRAM := ram.NewRAM(0xA0)
24 | gpu := gpu.NewGPU()
25 | pad := pad.NewPad()
26 | l := logger.NewLogger(logger.LogLevel("Debug"))
27 | t := timer.NewTimer()
28 | irq := interrupt.NewInterrupt()
29 | return NewBus(l, cart, gpu, vRAM, wRAM, hRAM, oamRAM, t, irq, pad), wRAM, hRAM
30 | }
31 |
32 | func TestWRAMReadWrite(t *testing.T) {
33 | assert := assert.New(t)
34 | b, wRAM, _ := setup()
35 | b.WriteByte(0xC000, 0xA5)
36 | b.WriteWord(0xC100, 0xDEAD)
37 | assert.Equal(byte(0xA5), wRAM.Read(0x0000))
38 | assert.Equal(byte(0xA5), b.ReadByte(0xC000))
39 | assert.Equal(byte(0xAD), wRAM.Read(0x0100))
40 | assert.Equal(byte(0xDE), wRAM.Read(0x0101))
41 | assert.Equal(types.Word(0xDEAD), b.ReadWord(0xC100))
42 | }
43 |
44 | func TestShadowWRAMReadWrite(t *testing.T) {
45 | assert := assert.New(t)
46 | b, wRAM, _ := setup()
47 | b.WriteByte(0xE000, 0xA5)
48 | b.WriteWord(0xE100, 0xDEAD)
49 | assert.Equal(byte(0xA5), wRAM.Read(0x0000))
50 | assert.Equal(byte(0xA5), b.ReadByte(0xE000))
51 | assert.Equal(byte(0xAD), wRAM.Read(0x0100))
52 | assert.Equal(byte(0xDE), wRAM.Read(0x0101))
53 | assert.Equal(types.Word(0xDEAD), b.ReadWord(0xE100))
54 | }
55 |
56 | func TestZRAMReadWrite(t *testing.T) {
57 | assert := assert.New(t)
58 | b, _, hRAM := setup()
59 | b.WriteByte(0xFF80, 0xA5)
60 | b.WriteWord(0xFF90, 0xDEAD)
61 | assert.Equal(byte(0xA5), hRAM.Read(0x0000))
62 | assert.Equal(types.Word(0xDEAD), b.ReadWord(0xFF90))
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/cartridge/cartridge.go:
--------------------------------------------------------------------------------
1 | package cartridge
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/bokuweb/gopher-boy/pkg/types"
8 | )
9 |
10 | // Cartridge is GameBoy cartridge
11 | type Cartridge struct {
12 | mbc MBC
13 | Title string
14 | ROM []byte
15 | RAMSize int
16 | }
17 |
18 | /*
19 | CartridgeType is
20 | 0x00: ROM ONLY
21 | 0x01: ROM+MBC1
22 | 0x02: ROM+MBC1+RAM
23 | 0x03: ROM+MBC1+RAM+BATT
24 | 0x05: ROM+MBC2
25 | 0x06: ROM+MBC2+BATTERY
26 | 0x08: ROM+RAM
27 | 0x09: ROM+RAM+BATTERY
28 | 0x0B: ROM+MMM01
29 | 0x0C: ROM+MMM01+SRAM
30 | 0x0D: ROM+MMM01+SRAM+BATT
31 | 0x12: ROM+MBC3+RAM
32 | 0x13: ROM+MBC3+RAM+BATT
33 | 0x19: ROM+MBC5
34 | 0x1A: ROM+MBC5+RAM
35 | 0x1B: ROM+MBC5+RAM+BATT
36 | 0x1C: ROM+MBC5+RUMBLE
37 | 0x1D: ROM+MBC5+RUMBLE+SRAM
38 | 0x1E: ROM+MBC5+RUMBLE+SRAM+BATT
39 | 0x1F: Pocket Camera
40 | 0xFD: Bandai TAMA5
41 | 0xFE: Hudson HuC-3
42 | */
43 | type CartridgeType byte
44 |
45 | const (
46 | MBC_0 CartridgeType = 0x00
47 | MBC_1 = 0x01
48 | MBC_1_RAM = 0x02
49 | MBC_1_RAM_BATT = 0x03
50 | MBC_3_RAM_BATT = 0x13
51 | MBC_3_RAM_BATT_RTC = 0x10
52 | MBC_5 = 0x19
53 | MBC_5_RAM = 0x1A
54 | MBC_5_RAM_BATT = 0x1B
55 | MBC_5_RUMBLE = 0x1C
56 | MBC_5_RAM_RUMBLE = 0x1D
57 | MBC_5_RAM_BATT_RUMBLE = 0x1E
58 | )
59 |
60 | // NewCartridge is cartridge constructure
61 | func NewCartridge(buf []byte) (*Cartridge, error) {
62 | title := strings.TrimSpace(string(buf[0x0134:0x0142]))
63 | // romSize := 0x8000 << buf[0x0148]
64 | ramSize := getRAMSize(buf[0x0149])
65 | cartridgeType := CartridgeType(buf[0x0147])
66 | fmt.Println("cartridge type is ", cartridgeType)
67 | var mbc MBC
68 | switch cartridgeType {
69 | case MBC_0:
70 | mbc = NewMBC0(buf[0x0000:0x8000])
71 | case MBC_1:
72 | mbc = NewMBC1(buf, ramSize, false)
73 | case MBC_1_RAM:
74 | mbc = NewMBC1(buf, ramSize, false)
75 | case MBC_1_RAM_BATT:
76 | mbc = NewMBC1(buf, ramSize, true)
77 | }
78 |
79 | return &Cartridge{
80 | mbc: mbc,
81 | Title: title,
82 | ROM: buf,
83 | RAMSize: ramSize,
84 | }, nil
85 | }
86 |
87 | //RAM size:
88 | // 0 - None
89 | // 1 - 16kBit = 2kB = 1 bank
90 | // 2 - 64kBit = 8kB = 1 bank
91 | // 3 - 256kBit = 32kB = 4 banks
92 | // 4 - 1MBit =128kB =16 banks
93 | func getRAMSize(size byte) int {
94 | switch size {
95 | case 0x00:
96 | return 0
97 | case 0x01:
98 | return 2 * 1024
99 | case 0x02:
100 | return 8 * 1024
101 | case 0x03:
102 | return 32 * 1024
103 | case 0x04:
104 | return 128 * 1024
105 | }
106 | return 0
107 | }
108 |
109 | func (c *Cartridge) ReadByte(addr types.Word) byte {
110 | return c.mbc.Read(addr)
111 | }
112 |
113 | func (c *Cartridge) WriteByte(addr types.Word, data byte) {
114 | c.mbc.Write(addr, data)
115 | }
116 |
--------------------------------------------------------------------------------
/pkg/cartridge/mbc.go:
--------------------------------------------------------------------------------
1 | package cartridge
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/types"
5 | )
6 |
7 | type MBC interface {
8 | Write(addr types.Word, value byte)
9 | Read(addr types.Word) byte
10 | switchROMBank(bank int)
11 | switchRAMBank(bank int)
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/cartridge/mbc0.go:
--------------------------------------------------------------------------------
1 | package cartridge
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/rom"
5 | "github.com/bokuweb/gopher-boy/pkg/types"
6 | )
7 |
8 | // MBC0 ROM ONLY
9 | // This is a 32kB (256kb) ROM and occupies 0000-7FFF.
10 | type MBC0 struct {
11 | rom *rom.ROM
12 | }
13 |
14 | // NewMBC0 constracts MBC0
15 | func NewMBC0(data []byte) *MBC0 {
16 | m := new(MBC0)
17 | m.rom = rom.NewROM(data)
18 | return m
19 | }
20 |
21 | func (m *MBC0) Write(addr types.Word, value byte) {
22 | }
23 |
24 | func (m *MBC0) Read(addr types.Word) byte {
25 | return m.rom.Read(uint32(addr))
26 | }
27 |
28 | func (m *MBC0) switchROMBank(bank int) {
29 | // nop
30 | }
31 |
32 | func (m *MBC0) switchRAMBank(bank int) {
33 | // nop
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/cartridge/mbc1.go:
--------------------------------------------------------------------------------
1 | package cartridge
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/ram"
5 | "github.com/bokuweb/gopher-boy/pkg/rom"
6 | "github.com/bokuweb/gopher-boy/pkg/types"
7 | )
8 |
9 | // MBC1 is (Memory Bank Controller 1
10 | // MBC1 has two different maximum memory modes: 16Mbit ROM/8KByte RAM or 4Mbit ROM/32KByte RAM.
11 | type MBC1 struct {
12 | rom *rom.ROM
13 | ram *ram.RAM
14 | selectedROMBank int
15 | selectedRAMBank int
16 | romBanking bool
17 | ramEnabled bool
18 | hasBattery bool
19 | memoryMode MBC1MemoryMode
20 | RAMSize int
21 | }
22 |
23 | // MBC1MemoryMode is MBC1 max memory mode
24 | // The MBC1 defaults to 16Mbit ROM/8KByte RAM mode
25 | // on power up. Writing a value (XXXXXXXS - X = Don't care, S = Memory model select) into 6000-7FFF area
26 | // will select the memory model to use.
27 | // S = 0 selects 16/8 mode. S = 1 selects 4/32 mode.
28 | type MBC1MemoryMode = string
29 |
30 | const (
31 | // ROM16mRAM8kMode is 4/32 memory mode
32 | // Writing a value (XXXXXXBB - X = Don't care, B = bank select bits) into 4000-5FFF area
33 | // will set the two most significant ROM address lines.
34 | // * NOTE: The Super Smart Card doesn't require this operation because it's RAM bank is ALWAYS enabled.
35 | // Include this operation anyway to allow your code to work with both
36 | ROM16mRAM8kMode MBC1MemoryMode = "ROM16M/RAM8K"
37 | // ROM4mRAM32kMode is 4/32 memory mode
38 | // Writing a value (XXXXXXBB - X = Don't care, B = bank select bits) into 4000-5FFF area
39 | // will select an appropriate RAM bank at A000-C000.
40 | // Before you can read or write to a RAM bank you have to enable it by writing a XXXX1010 into 0000-1FFF area*.
41 | // To disable RAM bank operations write any value but XXXX1010 into 0000-1FFF area.
42 | // Disabling a RAM bank probably protects that bank from false writes during power down of the GameBoy.
43 | // (NOTE: Nintendo suggests values $0A to enable and $00 to disable RAM bank!!)
44 | ROM4mRAM32kMode = "ROM4M/RAM32K"
45 | )
46 |
47 | // NewMBC1 constracts MBC1
48 | func NewMBC1(buf []byte, ramSize int, hasBattery bool) *MBC1 {
49 | m := &MBC1{
50 | selectedROMBank: 1,
51 | }
52 | m.memoryMode = ROM16mRAM8kMode
53 | m.hasBattery = hasBattery
54 | m.RAMSize = ramSize
55 | if ramSize > 0 {
56 | m.ramEnabled = true
57 | m.selectedRAMBank = 0
58 | m.ram = ram.NewRAM(0x8000)
59 | }
60 | m.rom = rom.NewROM(buf)
61 |
62 | return m
63 | }
64 |
65 | func (m *MBC1) Write(addr types.Word, value byte) {
66 | switch {
67 | // 4 bits wide; value of 0x0A enables RAM, any other value disables
68 | case addr < 0x2000:
69 | if m.memoryMode == ROM4mRAM32kMode {
70 | m.ramEnabled = value&0x0F == 0x0A
71 | }
72 | // Writing a value (XXXBBBBB - X = Don't cares, B = bank select bits) into 2000-3FFF area
73 | // will select an appropriate ROM bank at 4000-7FFF
74 | // Values of 0 and 1 do the same thing and point to ROM bank 1.
75 | // Rom bank 0 is not accessible from 4000-7FFF and can only be read from 0000-3FFF.
76 | case addr < 0x4000:
77 | m.switchROMBank(int(value & 0x1F))
78 | case addr < 0x6000:
79 | if m.romBanking {
80 | m.switchROMBank((m.selectedROMBank & 0x1F) | int(value&0xE0))
81 | break
82 | }
83 | m.switchRAMBank(int(value & 0x03))
84 |
85 | case addr < 0x8000:
86 | m.romBanking = value&0x01 == 0x00
87 | if m.romBanking {
88 | m.switchRAMBank(0)
89 | break
90 | }
91 | case addr < 0xC000:
92 | if m.ramEnabled {
93 | switch m.memoryMode {
94 | case ROM4mRAM32kMode:
95 | m.ram.Write(types.Word((int(addr)+m.selectedRAMBank*0x2000)-0xA000), value)
96 | case ROM16mRAM8kMode:
97 | m.ram.Write(types.Word((int(addr))-0xA000), value)
98 | }
99 | }
100 | }
101 | }
102 |
103 | func (m *MBC1) Read(addr types.Word) byte {
104 | if addr < 0x4000 {
105 | return m.rom.Read(uint32(addr))
106 | } else if addr < 0x8000 {
107 | base := uint32(m.selectedROMBank * 0x4000)
108 | return m.rom.Read(base + uint32(addr) - 0x4000)
109 | } else if addr < 0xC000 {
110 | if m.ramEnabled {
111 | switch m.memoryMode {
112 | case ROM4mRAM32kMode:
113 | return m.ram.Read(types.Word((int(addr) + m.selectedRAMBank*0x2000) - 0xA000))
114 | case ROM16mRAM8kMode:
115 | return m.ram.Read(types.Word((int(addr)) - 0xA000))
116 | }
117 | }
118 | }
119 | return 0x00
120 | }
121 |
122 | func (m *MBC1) switchROMBank(bank int) {
123 | m.selectedROMBank = bank
124 | if m.selectedROMBank == 0x00 || m.selectedROMBank == 0x20 || m.selectedROMBank == 0x40 || m.selectedROMBank == 0x60 {
125 | m.selectedROMBank++
126 | }
127 | }
128 |
129 | func (m *MBC1) switchRAMBank(bank int) {
130 | m.selectedRAMBank = bank
131 | }
132 |
--------------------------------------------------------------------------------
/pkg/constants/window.go:
--------------------------------------------------------------------------------
1 | package constants
2 |
3 | const (
4 | // ScreenWidth is the number of pixels width on the GameBoy LCD panel.
5 | ScreenWidth = 160
6 |
7 | // ScreenHeight is the number of pixels height on the GameBoy LCD panel.
8 | ScreenHeight = 144
9 | )
10 |
--------------------------------------------------------------------------------
/pkg/cpu/cpu_test.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
7 | "github.com/bokuweb/gopher-boy/pkg/logger"
8 | "github.com/bokuweb/gopher-boy/pkg/mocks"
9 | "github.com/bokuweb/gopher-boy/pkg/types"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func setupCPU(offset types.Word, data []byte) (*CPU, *mocks.MockBus) {
14 | b := mocks.MockBus{}
15 | b.SetMemory(offset, data)
16 | irq := interrupt.NewInterrupt()
17 | l := logger.NewLogger(logger.LogLevel("Debug"))
18 | return NewCPU(l, &b, irq), &b
19 | }
20 |
21 | func TestNOP(t *testing.T) {
22 | cpu, _ := setupCPU(0, []byte{0})
23 | cpu.Step()
24 | }
25 |
26 | func TestLDn_nn(t *testing.T) {
27 | assert := assert.New(t)
28 | cpu, _ := setupCPU(0, []byte{0x01, 0xDE, 0xAD})
29 | cpu.PC = 0x00
30 | cpu.Step()
31 | assert.Equal(byte(0xAD), cpu.Regs.B, "should B equals 0xad")
32 | assert.Equal(byte(0xDE), cpu.Regs.C, "should C equals 0xde")
33 | }
34 |
35 | func TestLDrr_r(t *testing.T) {
36 | assert := assert.New(t)
37 | cpu, bus := setupCPU(0, []byte{0x02})
38 | cpu.PC = 0x00
39 | cpu.Regs.A = 0xA5
40 | cpu.Regs.B = 0x10
41 | cpu.Regs.C = 0x20
42 | cpu.Step()
43 | assert.Equal(byte(0xA5), bus.MockMemory[0x1020], "should memory equals 0xa5")
44 | }
45 |
46 | func TestIncrr(t *testing.T) {
47 | assert := assert.New(t)
48 | cpu, _ := setupCPU(0, []byte{0x03})
49 | cpu.PC = 0x00
50 | cpu.Regs.B = 0x10
51 | cpu.Regs.C = 0x20
52 | cpu.Step()
53 | assert.Equal(byte(0x10), cpu.Regs.B, "should not B incremented")
54 | assert.Equal(byte(0x21), cpu.Regs.C, "should C incremented")
55 | }
56 |
57 | func TestIncB(t *testing.T) {
58 | assert := assert.New(t)
59 | cpu, _ := setupCPU(0, []byte{0x04})
60 | cpu.PC = 0x00
61 | cpu.Regs.B = 0x10
62 | cpu.Step()
63 | assert.Equal(byte(0x11), cpu.Regs.B, "should B incremented")
64 | }
65 |
66 | func TestDecB(t *testing.T) {
67 | assert := assert.New(t)
68 | cpu, _ := setupCPU(0, []byte{0x05})
69 | cpu.PC = 0x00
70 | cpu.Regs.B = 0x10
71 | cpu.Step()
72 | assert.Equal(byte(0x0F), cpu.Regs.B, "should B decremented")
73 | }
74 |
75 | func TestLDnn_n(t *testing.T) {
76 | assert := assert.New(t)
77 | cpu, _ := setupCPU(0, []byte{0x06, 0xA5})
78 | cpu.PC = 0x00
79 | cpu.Step()
80 | assert.Equal(cpu.Regs.B, byte(0xA5), "should B equals 0xa5")
81 | }
82 |
--------------------------------------------------------------------------------
/pkg/cpu/helpers.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/types"
5 | "github.com/bokuweb/gopher-boy/pkg/utils"
6 | )
7 |
8 | func (cpu *CPU) inc(value byte) byte {
9 | incremented := value + 0x01
10 | cpu.clearFlag(N)
11 | if incremented == 0 {
12 | cpu.setFlag(Z)
13 | } else {
14 | cpu.clearFlag(Z)
15 | }
16 | if (incremented^0x01^value)&0x10 == 0x10 {
17 | cpu.setFlag(H)
18 | } else {
19 | cpu.clearFlag(H)
20 | }
21 | return incremented
22 | }
23 |
24 | func (cpu *CPU) dec(value byte) byte {
25 | decremented := value - 1
26 | cpu.setFlag(N)
27 | cpu.applyZeroBy(decremented)
28 | if (decremented^0x01^value)&0x10 == 0x10 {
29 | cpu.setFlag(H)
30 | } else {
31 | cpu.clearFlag(H)
32 | }
33 | return decremented
34 | }
35 |
36 | func (cpu *CPU) clearFlag(flag flags) {
37 | switch flag {
38 | case Z:
39 | cpu.Regs.F = cpu.clearBit(7, cpu.Regs.F)
40 | case N:
41 | cpu.Regs.F = cpu.clearBit(6, cpu.Regs.F)
42 | case H:
43 | cpu.Regs.F = cpu.clearBit(5, cpu.Regs.F)
44 | case C:
45 | cpu.Regs.F = cpu.clearBit(4, cpu.Regs.F)
46 | }
47 | cpu.Regs.F &= 0xF0
48 | }
49 |
50 | func (cpu *CPU) setFlag(flag flags) {
51 | switch flag {
52 | case Z:
53 | cpu.Regs.F = cpu.setBit(7, cpu.Regs.F)
54 | case N:
55 | cpu.Regs.F = cpu.setBit(6, cpu.Regs.F)
56 | case H:
57 | cpu.Regs.F = cpu.setBit(5, cpu.Regs.F)
58 | case C:
59 | cpu.Regs.F = cpu.setBit(4, cpu.Regs.F)
60 | }
61 | cpu.Regs.F &= 0xF0
62 | }
63 |
64 | func (cpu *CPU) isSet(flag flags) bool {
65 | switch flag {
66 | case Z:
67 | return cpu.Regs.F&0x80 != 0
68 | case N:
69 | return cpu.Regs.F&0x40 != 0
70 | case H:
71 | return cpu.Regs.F&0x20 != 0
72 | case C:
73 | return cpu.Regs.F&0x10 != 0
74 | }
75 | return false
76 | }
77 |
78 | func (cpu *CPU) setBit(bit byte, a byte) byte {
79 | return a | (1 << uint(bit))
80 | }
81 |
82 | func (cpu *CPU) clearBit(bit byte, a byte) byte {
83 | return a & ^(1 << uint(bit))
84 | }
85 |
86 | func (cpu *CPU) addBytes(a, b byte) byte {
87 | computed := a + b
88 | cpu.clearFlag(N)
89 | if computed == 0x00 {
90 | cpu.setFlag(Z)
91 | } else {
92 | cpu.clearFlag(Z)
93 | }
94 | if (computed^b^a)&0x10 == 0x10 {
95 | cpu.setFlag(H)
96 | } else {
97 | cpu.clearFlag(H)
98 | }
99 | if computed < a {
100 | cpu.setFlag(C)
101 | } else {
102 | cpu.clearFlag(C)
103 | }
104 | return computed
105 | }
106 |
107 | func (cpu *CPU) addWords(a, b types.Word) types.Word {
108 | computed := a + b
109 | if computed < a {
110 | cpu.setFlag(C)
111 | } else {
112 | cpu.clearFlag(C)
113 | }
114 | if (computed^b^a)&0x1000 == 0x1000 {
115 | cpu.setFlag(H)
116 | } else {
117 | cpu.clearFlag(H)
118 | }
119 |
120 | cpu.clearFlag(N)
121 |
122 | return computed
123 | }
124 |
125 | func (cpu *CPU) subBytes(a, b byte) byte {
126 | cpu.setFlag(N)
127 | if a&0xF < b&0xF {
128 | cpu.setFlag(H)
129 | } else {
130 | cpu.clearFlag(H)
131 | }
132 | if a&0xFF < b&0xFF {
133 | cpu.setFlag(C)
134 | } else {
135 | cpu.clearFlag(C)
136 | }
137 | computed := a - b
138 | if computed == 0x00 {
139 | cpu.setFlag(Z)
140 | } else {
141 | cpu.clearFlag(Z)
142 | }
143 | return computed
144 | }
145 |
146 | func (cpu *CPU) getHL() types.Word {
147 | return utils.Bytes2Word(cpu.Regs.H, cpu.Regs.L)
148 | }
149 |
150 | func (cpu *CPU) toHLRegs(v types.Word) {
151 | cpu.Regs.H, cpu.Regs.L = utils.Word2Bytes(v)
152 | }
153 |
154 | func (cpu *CPU) and(a, b byte) byte {
155 | cpu.setFlag(H)
156 | cpu.clearFlag(N)
157 | cpu.clearFlag(C)
158 | computed := a & b
159 | if computed == 0x00 {
160 | cpu.setFlag(Z)
161 | } else {
162 | cpu.clearFlag(Z)
163 | }
164 | return computed
165 | }
166 |
167 | func (cpu *CPU) xor(a, b byte) byte {
168 | cpu.clearFlag(H)
169 | cpu.clearFlag(N)
170 | cpu.clearFlag(C)
171 | computed := a ^ b
172 | if computed == 0x00 {
173 | cpu.setFlag(Z)
174 | } else {
175 | cpu.clearFlag(Z)
176 | }
177 | return computed
178 | }
179 |
180 | func (cpu *CPU) or(a, b byte) byte {
181 | cpu.clearFlag(H)
182 | cpu.clearFlag(N)
183 | cpu.clearFlag(C)
184 | computed := a | b
185 | if computed == 0x00 {
186 | cpu.setFlag(Z)
187 | } else {
188 | cpu.clearFlag(Z)
189 | }
190 | return computed
191 | }
192 |
193 | func (cpu *CPU) pop() byte {
194 | b := cpu.bus.ReadByte(cpu.SP)
195 | cpu.SP++
196 | return b
197 | }
198 |
199 | func (cpu *CPU) push(v byte) {
200 | cpu.SP--
201 | cpu.bus.WriteByte(cpu.SP, v)
202 | }
203 |
204 | func (cpu *CPU) pushPC() {
205 | upper, lower := utils.Word2Bytes(cpu.PC)
206 | cpu.push(upper)
207 | cpu.push(lower)
208 | }
209 |
210 | func (cpu *CPU) pop2PC() {
211 | lower := cpu.pop()
212 | upper := cpu.pop()
213 | cpu.PC = utils.Bytes2Word(upper, lower)
214 | }
215 |
216 | func (cpu *CPU) applyZeroBy(computed byte) {
217 | if computed == 0 {
218 | cpu.setFlag(Z)
219 | } else {
220 | cpu.clearFlag(Z)
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/pkg/gb/gb.go:
--------------------------------------------------------------------------------
1 | package gb
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/bokuweb/gopher-boy/pkg/cpu"
7 | "github.com/bokuweb/gopher-boy/pkg/gpu"
8 | "github.com/bokuweb/gopher-boy/pkg/interfaces/window"
9 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
10 | "github.com/bokuweb/gopher-boy/pkg/timer"
11 | )
12 |
13 | // CyclesPerFrame is cpu clock num for 1frame.
14 | const CyclesPerFrame = 70224
15 |
16 | // GB is gameboy emulator struct
17 | type GB struct {
18 | currentCycle uint
19 | cpu *cpu.CPU
20 | gpu *gpu.GPU
21 | timer *timer.Timer
22 | irq *interrupt.Interrupt
23 | win window.Window
24 | }
25 |
26 | // NewGB is gb initializer
27 | func NewGB(cpu *cpu.CPU, gpu *gpu.GPU, timer *timer.Timer, irq *interrupt.Interrupt, win window.Window) *GB {
28 | return &GB{
29 | currentCycle: 0,
30 | cpu: cpu,
31 | gpu: gpu,
32 | timer: timer,
33 | irq: irq,
34 | win: win,
35 | }
36 | }
37 |
38 | // Start is
39 | func (g *GB) Start() {
40 | t := time.NewTicker(16 * time.Millisecond)
41 | for {
42 | select {
43 | case <-t.C:
44 | buf := g.Next()
45 | g.win.Render(buf)
46 | }
47 | }
48 | t.Stop()
49 | }
50 | func (g *GB) Next() []byte {
51 | for {
52 | var cycles uint
53 | if g.gpu.DMAStarted() {
54 | g.gpu.Transfer()
55 | // https://github.com/Gekkio/mooneye-gb/blob/master/docs/accuracy.markdown#how-many-cycles-does-oam-dma-take
56 | cycles = 162
57 | } else {
58 | cycles = g.cpu.Step()
59 | }
60 | g.gpu.Step(cycles * 4)
61 | if overflowed := g.timer.Update(cycles); overflowed {
62 | g.irq.SetIRQ(interrupt.TimerOverflowFlag)
63 | }
64 | g.currentCycle += cycles * 4
65 | if g.currentCycle >= CyclesPerFrame {
66 | g.win.PollKey()
67 | g.currentCycle -= CyclesPerFrame
68 | return g.gpu.GetImageData()
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/pkg/gb/gb_test.go:
--------------------------------------------------------------------------------
1 | package gb
2 |
3 | import (
4 | "image"
5 | "image/color"
6 | "image/png"
7 | "os"
8 | "testing"
9 |
10 | "github.com/bokuweb/gopher-boy/pkg/constants"
11 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
12 | "github.com/bokuweb/gopher-boy/pkg/pad"
13 | "github.com/bokuweb/gopher-boy/pkg/timer"
14 |
15 | "github.com/bokuweb/gopher-boy/pkg/bus"
16 | "github.com/bokuweb/gopher-boy/pkg/cartridge"
17 | "github.com/bokuweb/gopher-boy/pkg/cpu"
18 | "github.com/bokuweb/gopher-boy/pkg/gpu"
19 | "github.com/bokuweb/gopher-boy/pkg/interfaces/window"
20 | "github.com/bokuweb/gopher-boy/pkg/logger"
21 | "github.com/bokuweb/gopher-boy/pkg/ram"
22 | "github.com/bokuweb/gopher-boy/pkg/utils"
23 | )
24 |
25 | const (
26 | RomPathPrefix = "../../roms/"
27 | ImagePathPrefix = "../../test/actual/"
28 | )
29 |
30 | // MockWindow is
31 | type mockWindow struct {
32 | window.Window
33 | }
34 |
35 | func (m mockWindow) PollKey() {
36 | }
37 |
38 | func setup(file string) *GB {
39 | l := logger.NewLogger(logger.LogLevel("DEBUG"))
40 | buf, err := utils.LoadROM(file)
41 | if err != nil {
42 | panic(err)
43 | }
44 | cart, err := cartridge.NewCartridge(buf)
45 | if err != nil {
46 | panic(err)
47 | }
48 |
49 | vRAM := ram.NewRAM(0x2000)
50 | wRAM := ram.NewRAM(0x2000)
51 | hRAM := ram.NewRAM(0x80)
52 | oamRAM := ram.NewRAM(0xA0)
53 | gpu := gpu.NewGPU()
54 | t := timer.NewTimer()
55 | pad := pad.NewPad()
56 | irq := interrupt.NewInterrupt()
57 | b := bus.NewBus(l, cart, gpu, vRAM, wRAM, hRAM, oamRAM, t, irq, pad)
58 | gpu.Init(b, irq)
59 | win := mockWindow{}
60 | emu := NewGB(cpu.NewCPU(l, b, irq), gpu, t, irq, win)
61 | return emu
62 | }
63 |
64 | func set(img *image.RGBA, buf []byte) {
65 | imgData := make([]color.RGBA, constants.ScreenWidth*constants.ScreenHeight)
66 | i := 0
67 | for i*4 < len(buf) {
68 | y := constants.ScreenHeight - (i / constants.ScreenWidth) - 1
69 | imgData[y*constants.ScreenWidth+i%constants.ScreenWidth] = color.RGBA{buf[i*4], buf[i*4+1], buf[i*4+2], buf[i*4+3]}
70 | i++
71 | }
72 |
73 | rect := img.Rect
74 | for y := rect.Min.Y; y < rect.Max.Y; y++ {
75 | for x := rect.Min.X; x < rect.Max.X; x++ {
76 | img.Set(x, rect.Max.Y-y, imgData[y*rect.Max.X+x])
77 | }
78 | }
79 | }
80 |
81 | func skipFrame(emu *GB, n int) []byte {
82 | var image []byte
83 | for i := 0; i < n; i++ {
84 | image = emu.Next()
85 | }
86 | return image
87 | }
88 |
89 | func TestROMs(t *testing.T) {
90 | tests := []struct {
91 | name string
92 | path string
93 | frame int
94 | }{
95 | {
96 | "hello_world",
97 | RomPathPrefix + "helloworld/hello.gb",
98 | 100,
99 | },
100 | {
101 | "tictactoe",
102 | RomPathPrefix + "tictactoe/GB-TicTacToe.gb",
103 | 100,
104 | },
105 | {
106 | "cpu_instr",
107 | RomPathPrefix + "cpu_instrs/cpu_instrs.gb",
108 | 4000,
109 | },
110 | {
111 | "opus5",
112 | RomPathPrefix + "opus5/opus5.gb",
113 | 100,
114 | },
115 | {
116 | "div_write",
117 | RomPathPrefix + "acceptance/timer/div_write.gb",
118 | 1000,
119 | },
120 | {
121 | "tim00",
122 | RomPathPrefix + "acceptance/timer/tim00.gb",
123 | 100,
124 | },
125 | {
126 | "tim01",
127 | RomPathPrefix + "acceptance/timer/tim01.gb",
128 | 100,
129 | },
130 | {
131 | "tim10",
132 | RomPathPrefix + "acceptance/timer/tim10.gb",
133 | 100,
134 | },
135 | {
136 | "tim11",
137 | RomPathPrefix + "acceptance/timer/tim10.gb",
138 | 100,
139 | },
140 | {
141 | "bits_bank1",
142 | RomPathPrefix + "mbc1/bits_bank1.gb",
143 | 100,
144 | },
145 | {
146 | "reg_f",
147 | RomPathPrefix + "acceptance/bits/reg_f.gb",
148 | 100,
149 | },
150 | {
151 | "mem_oam",
152 | RomPathPrefix + "acceptance/bits/mem_oam.gb",
153 | 100,
154 | },
155 | {
156 | "daa",
157 | RomPathPrefix + "acceptance/instr/daa.gb",
158 | 100,
159 | },
160 | {
161 | "if_ie_registers",
162 | RomPathPrefix + "acceptance/if_ie_registers.gb",
163 | 100,
164 | },
165 | {
166 | "tobu",
167 | RomPathPrefix + "tobu/tobu.gb",
168 | 100,
169 | },
170 | }
171 | for _, tt := range tests {
172 | t.Run(tt.name, func(t *testing.T) {
173 | emu := setup(tt.path)
174 | buf := skipFrame(emu, tt.frame)
175 | file, err := os.Create(ImagePathPrefix + tt.name + ".png")
176 | defer file.Close()
177 | if err != nil {
178 | panic(err)
179 | }
180 | img := image.NewRGBA(image.Rect(0, 0, constants.ScreenWidth, constants.ScreenHeight))
181 | set(img, buf)
182 | if err := png.Encode(file, img); err != nil {
183 | panic(err)
184 | }
185 | })
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/pkg/gpu/gpu_test.go:
--------------------------------------------------------------------------------
1 | package gpu
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/bokuweb/gopher-boy/pkg/constants"
7 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
8 | "github.com/bokuweb/gopher-boy/pkg/mocks"
9 |
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func setup() *GPU {
14 | g := NewGPU()
15 | irq := interrupt.NewInterrupt()
16 | g.Init(&mocks.MockBus{}, irq)
17 | return g
18 | }
19 |
20 | func TestLY(t *testing.T) {
21 | assert := assert.New(t)
22 | g := setup()
23 | for y := 0; y < int(constants.ScreenHeight+LCDVBlankHeight+10); y++ {
24 | if y == int(constants.ScreenHeight+LCDVBlankHeight) {
25 | assert.Equal(uint8(0x9a), g.Read(LY))
26 | } else {
27 | assert.Equal(byte(y%int(constants.ScreenHeight+LCDVBlankHeight)), g.Read(LY), y)
28 | }
29 |
30 | g.Step(CyclePerLine)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/interfaces/bus/bus.go:
--------------------------------------------------------------------------------
1 | package bus
2 |
3 | import "github.com/bokuweb/gopher-boy/pkg/types"
4 |
5 | // Accessor bus accessor interface
6 | type Accessor interface {
7 | WriteByte(addr types.Word, data byte)
8 | WriteWord(addr types.Word, data types.Word)
9 |
10 | ReadByte(addr types.Word) byte
11 | ReadWord(addr types.Word) types.Word
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/interfaces/interrupt/interrupt.go:
--------------------------------------------------------------------------------
1 | package interrupt
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/interrupt"
5 | "github.com/bokuweb/gopher-boy/pkg/types"
6 | )
7 |
8 | // IRQFlag is
9 | type IRQFlag = interrupt.IRQFlag
10 |
11 | // Interrupt defined irq interface
12 | type Interrupt interface {
13 | SetIRQ(f interrupt.IRQFlag)
14 | Enable()
15 | Disable()
16 | Enabled() bool
17 | Read(addr types.Word) byte
18 | Write(addr types.Word, data byte)
19 | HasIRQ() bool
20 | ResolveISRAddr() *types.Word
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/interfaces/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | // Logger is
4 | type Logger interface {
5 | Debug(args ...interface{})
6 | Info(args ...interface{})
7 | Error(args ...interface{})
8 | Warn(args ...interface{})
9 | }
10 |
--------------------------------------------------------------------------------
/pkg/interfaces/pad/pad.go:
--------------------------------------------------------------------------------
1 | package pad
2 |
3 | import "github.com/bokuweb/gopher-boy/pkg/pad"
4 |
5 | // Pad defined keypad interface
6 | type Pad interface {
7 | Press(b pad.Button)
8 | Release(b pad.Button)
9 | Read() byte
10 | Write(v byte)
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/interfaces/window/window.go:
--------------------------------------------------------------------------------
1 | package window
2 |
3 | // Window is
4 | type Window interface {
5 | Render(imageData []byte)
6 | Run(run func())
7 | PollKey()
8 | KeyDown(button byte)
9 | KeyUp(button byte)
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/interrupt/interrupt.go:
--------------------------------------------------------------------------------
1 | package interrupt
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/types"
5 | )
6 |
7 | var (
8 | // VerticalBlankISRAddr is Vertical Blank Interrupt Start Address
9 | VerticalBlankISRAddr types.Word = 0x0040
10 | // LCDCStatusTriggersISRAddr is LCDC Status Interrupt Start Address
11 | LCDCStatusTriggersISRAddr types.Word = 0x0048
12 | // TimeroverflowISRAddr is Timer Overflow Interrupt Start Address
13 | TimeroverflowISRAddr types.Word = 0x0050
14 | // SerialTransferISRAddr is Serial Transfer Completion Interrupt
15 | SerialTransferISRAddr types.Word = 0x0058
16 | // JoypadPressISRAddr is High-to-Low of P10-P13 Interrupt
17 | JoypadPressISRAddr types.Word = 0x0060
18 | )
19 |
20 | // IRQFlag is
21 | type IRQFlag = byte
22 |
23 | const (
24 | VerticalBlankFlag IRQFlag = 0x01
25 | LCDSFlag IRQFlag = 0x02
26 | TimerOverflowFlag IRQFlag = 0x04
27 | SerialTransferFlag IRQFlag = 0x08
28 | JoypadPressFlag IRQFlag = 0x10
29 | )
30 |
31 | const (
32 | RegisterOffset = 0xFF00
33 | // IF - Interrupt Flag (R/W)
34 | // Bit 4: Transition from High to Low of Pin
35 | // number P10-P13
36 | // Bit 3: Serial I/O transfer complete
37 | // Bit 2: Timer Overflow
38 | // Bit 1: LCDC (see STAT)
39 | // Bit 0: V-Blank
40 | // The priority and jump address for the above 5
41 | // interrupts are:
42 | // Interrupt Priority Start Address
43 | // V-Blank 1 $0040
44 | // LCDC Status 2 $0048 - Modes 0, 1, 2
45 | // LYC=LY coincide
46 | // (selectable)
47 | // Timer Overflow 3 $0050
48 | // Serial Transfer 4 $0058 - when transfer
49 | // is complete
50 | // Hi-Lo of P10-P13 5 $0060
51 | // * When more than 1 interrupts occur at the same
52 | // time only the interrupt with the highest priority
53 | // can be acknowledged. When an interrupt is used a
54 | // '0' should be stored in the IF register before the
55 | // IE register is set.
56 | IF = 0x0F
57 | // IE - Interrupt Enable (R/W)
58 | // Bit 4:
59 | // Transition from High to Low of Pin number P10-P13.
60 | // Serial I/O transfer complete
61 | // Timer Overflow
62 | // Bit 3:
63 | // Bit 2:
64 | // Bit 1:
65 | // Bit 0: V-Blank
66 | // 0: disable
67 | // 1: enable
68 | IE = 0xFF
69 | )
70 |
71 | const (
72 | InterruptFlagAddr = RegisterOffset + IF
73 | InterruptEnableFlagAddr = RegisterOffset + IE
74 | )
75 |
76 | // Interrupt has 2 registers to manage
77 | type Interrupt struct {
78 | IF byte
79 | IE byte
80 | enabled bool
81 | }
82 |
83 | // NewInterrupt constructs irq peripheral.
84 | func NewInterrupt() *Interrupt {
85 | return &Interrupt{
86 | IF: 0x00,
87 | IE: 0x00,
88 | enabled: false,
89 | }
90 | }
91 |
92 | // SetIRQ set flag
93 | func (irq *Interrupt) SetIRQ(f IRQFlag) {
94 | irq.IF |= f
95 | }
96 |
97 | func (irq *Interrupt) Read(addr types.Word) byte {
98 | switch addr {
99 | case IE:
100 | return irq.IE
101 | case IF:
102 | return irq.IF | 0xE0
103 | }
104 | panic("Illegal access detected.")
105 | }
106 |
107 | func (irq *Interrupt) Write(addr types.Word, data byte) {
108 | switch addr {
109 | case IE:
110 | irq.IE = data
111 | return
112 | case IF:
113 | irq.IF = data
114 | return
115 | }
116 | panic("Illegal access detected.")
117 | }
118 |
119 | func (irq *Interrupt) HasIRQ() bool {
120 | i := irq.IF & irq.IE
121 | return i != 0x00
122 | }
123 |
124 | func (irq *Interrupt) Enable() {
125 | irq.enabled = true
126 | }
127 |
128 | func (irq *Interrupt) Enabled() bool {
129 | return irq.enabled
130 | }
131 |
132 | func (irq *Interrupt) Disable() {
133 | irq.enabled = false
134 | }
135 |
136 | func (irq *Interrupt) ResolveISRAddr() *types.Word {
137 | i := irq.IF & irq.IE
138 | switch {
139 | case i&VerticalBlankFlag != 0:
140 | irq.IF &= ^VerticalBlankFlag
141 | return &VerticalBlankISRAddr
142 | case i&LCDSFlag != 0:
143 | irq.IF &= ^LCDSFlag
144 | return &LCDCStatusTriggersISRAddr
145 | case i&TimerOverflowFlag != 0:
146 | irq.IF &= ^TimerOverflowFlag
147 | return &TimeroverflowISRAddr
148 | case i&SerialTransferFlag != 0:
149 | irq.IF &= ^SerialTransferFlag
150 | return &SerialTransferISRAddr
151 | case i&JoypadPressFlag != 0:
152 | irq.IF &= ^JoypadPressFlag
153 | return &JoypadPressISRAddr
154 | }
155 | return nil
156 | }
157 |
--------------------------------------------------------------------------------
/pkg/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "log"
5 | )
6 |
7 | // LogLevel is kind of Loglavel
8 | type LogLevel string
9 |
10 | const (
11 | // LogDebug is
12 | LogDebug LogLevel = "Debug"
13 | // LogInfo is
14 | LogInfo LogLevel = "Info"
15 | // LogSilent is
16 | LogSilent LogLevel = "Silent"
17 | )
18 |
19 | // Log is
20 | type Log struct {
21 | Level LogLevel
22 | }
23 |
24 | // NewLogger is logger constructor
25 | func NewLogger(level LogLevel) *Log {
26 | // now := time.Now().Unix()
27 | // n := strconv.FormatInt(now, 10)
28 | // logfile, err := os.OpenFile("./logs/test-"+n+".log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
29 | // if err != nil {
30 | // panic("cannnot open test.log:" + err.Error())
31 | // }
32 | // log.SetOutput(io.MultiWriter(logfile))
33 | // log.SetFlags(log.Ldate | log.Ltime)
34 |
35 | return &Log{
36 | Level: level,
37 | }
38 | }
39 |
40 | // Debug is
41 | func (l *Log) Debug(args ...interface{}) {
42 | if l.Level != "Debug" {
43 | return
44 | }
45 | log.Println("[DEBUG] ", args)
46 | }
47 |
48 | // Info is
49 | func (l *Log) Info(args ...interface{}) {
50 | log.Println("[Info] ", args)
51 | }
52 |
53 | // Error is
54 | func (l *Log) Error(args ...interface{}) {
55 | log.Println("[ERROR] ", args)
56 | }
57 |
58 | // Warn is
59 | func (l *Log) Warn(args ...interface{}) {
60 | log.Println("[WARNING] ", args)
61 | }
62 |
--------------------------------------------------------------------------------
/pkg/mocks/bus.go:
--------------------------------------------------------------------------------
1 | package mocks
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/types"
5 | )
6 |
7 | type MockBus struct {
8 | MockMemory [0x10000]byte
9 | }
10 |
11 | func (b *MockBus) WriteByte(addr types.Word, data byte) {
12 | b.MockMemory[addr] = data
13 | }
14 |
15 | func (b *MockBus) WriteWord(addr types.Word, data types.Word) {
16 | b.MockMemory[addr] = byte(data & 0xFF)
17 | b.MockMemory[addr+1] = byte(data >> 8)
18 | }
19 |
20 | func (b *MockBus) ReadByte(addr types.Word) byte {
21 | return b.MockMemory[addr]
22 | }
23 |
24 | func (b *MockBus) ReadWord(addr types.Word) types.Word {
25 | upper := types.Word(b.MockMemory[addr]) << 8
26 | return upper + types.Word(b.MockMemory[addr+1])
27 | }
28 |
29 | func (b *MockBus) SetMemory(offset types.Word, data []byte) {
30 | for i, d := range data {
31 | b.MockMemory[offset+types.Word(i)] = d
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pkg/pad/pad.go:
--------------------------------------------------------------------------------
1 | package pad
2 |
3 | // This is the matrix layout for register $FF00:
4 | // P14 P15
5 | // | |
6 | // P10-------O-Right----O-A
7 | // | |
8 | // P11-------O-Left-----O-B
9 | // | |
10 | // P12-------O-Up-------O-Select
11 | // | |
12 | // P13-------O-Down-----O-Start
13 | //
14 |
15 | type Pad struct {
16 | // Bit 7 - Not used
17 | // Bit 6 - Not used
18 | // Bit 5 - P15 out port
19 | // Bit 4 - P14 out port
20 | // Bit 3 - P13 in port
21 | // Bit 2 - P12 in port
22 | // Bit 1 - P11 in port
23 | // Bit 0 - P10 in port
24 | reg byte
25 | state Button
26 | }
27 |
28 | type Button byte
29 |
30 | const (
31 | // A is the A button on the GameBoy.
32 | A Button = 0x01
33 | // B is the B button on the GameBoy.
34 | B Button = 0x02
35 | // Select is the select button on the GameBoy.
36 | Select Button = 0x04
37 | // Start is the start button on the GameBoy.
38 | Start Button = 0x08
39 | // Right is the right pad direction on the GameBoy.
40 | Right Button = 0x10
41 | // Left is the left pad direction on the GameBoy.
42 | Left Button = 0x20
43 | // Up is the up pad direction on the GameBoy.
44 | Up Button = 0x40
45 | // Down is the down pad direction on the GameBoy.
46 | Down Button = 0x80
47 | )
48 |
49 | // NewPad constructs pad peripheral.
50 | func NewPad() *Pad {
51 | return &Pad{
52 | reg: 0x3F,
53 | }
54 | }
55 |
56 | func (pad *Pad) Read() byte {
57 | if pad.isP14On() {
58 | return pad.reg & ^byte(pad.state>>4)
59 | }
60 | if pad.isP15On() {
61 | return pad.reg & ^byte(pad.state&0x0F)
62 | }
63 | return pad.reg | 0x0f
64 | }
65 |
66 | func (pad *Pad) Write(data byte) {
67 | pad.reg = (pad.reg & 0xCF) | (data & 0x30)
68 | }
69 |
70 | func (pad *Pad) isP14On() bool {
71 | return pad.reg&0x10 == 0
72 | }
73 |
74 | func (pad *Pad) isP15On() bool {
75 | return pad.reg&0x20 == 0
76 | }
77 |
78 | func (pad *Pad) Press(button Button) {
79 | pad.state |= button
80 | }
81 |
82 | func (pad *Pad) Release(button Button) {
83 | pad.state &= ^button
84 | }
85 |
--------------------------------------------------------------------------------
/pkg/ram/ram.go:
--------------------------------------------------------------------------------
1 | package ram
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/types"
5 | )
6 |
7 | // RAM is
8 | type RAM struct {
9 | data []byte
10 | }
11 |
12 | // NewRAM is RAM constructor
13 | func NewRAM(size int) *RAM {
14 | data := make([]byte, size)
15 | return &RAM{
16 | data: data,
17 | }
18 | }
19 |
20 | func (r *RAM) Read(addr types.Word) byte {
21 | return r.data[addr]
22 | }
23 |
24 | func (r *RAM) Write(addr types.Word, data byte) {
25 | r.data[addr] = data
26 | }
27 |
28 | // Debugging
29 | func (r *RAM) GetBuf() []byte {
30 | return r.data
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/rom/rom.go:
--------------------------------------------------------------------------------
1 | package rom
2 |
3 | // ROM
4 | type ROM struct {
5 | data []byte
6 | }
7 |
8 | // NewROM is ROM constructor
9 | func NewROM(v []byte) *ROM {
10 | return &ROM{
11 | data: v,
12 | }
13 | }
14 |
15 | func (r *ROM) Read(addr uint32) byte {
16 | return r.data[addr]
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/serial/serial.go:
--------------------------------------------------------------------------------
1 | package serial
2 |
3 | import "fmt"
4 |
5 | func Send(b byte) {
6 | fmt.Printf("%v", string(b))
7 | }
8 |
--------------------------------------------------------------------------------
/pkg/timer/timer.go:
--------------------------------------------------------------------------------
1 | package timer
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/types"
5 | )
6 |
7 | const (
8 | // TimerRegisterOffset is register offset address
9 | TimerRegisterOffset types.Word = 0xFF00
10 | // DIV - Divider Register (R/W)
11 | // This register is incremented 16384 (~16779 on SGB) times a second.
12 | // Writing any value sets it to $00.
13 | DIV = 0x04
14 | // TIMA - Timer counter (R/W)
15 | // This timer is incremented by a clock frequency specified by the TAC register ($FF07).
16 | // The timer generates an interrupt when it overflows.
17 | TIMA = 0x05
18 | // TMA - Timer Modulo (R/W)
19 | // When the TIMA overflows, this data will be loaded.
20 | TMA = 0x06
21 | // TAC - Timer Control (R/W)
22 | // Bit 2 - Timer Stop
23 | // 0: Stop Timer
24 | // 1: Start Timer
25 | // Bits 1+0 - Input Clock Select
26 | // 00: 4.096 KHz (~4.194 KHz SGB)
27 | // 01: 262.144 Khz (~268.4 KHz SGB)
28 | // 10: 65.536 KHz (~67.11 KHz SGB)
29 | // 11: 16.384 KHz (~16.78 KHz SGB)
30 | TAC = 0x07
31 | )
32 |
33 | // Timer has 4 registers.
34 | type Timer struct {
35 | internalCounter uint16
36 | TIMA byte
37 | TAC byte
38 | TMA byte
39 | }
40 |
41 | // NewTimer constructs timer peripheral.
42 | func NewTimer() *Timer {
43 | return &Timer{
44 | // 4.194304MHz / 256 = 16.384KHz
45 | internalCounter: 0,
46 | TIMA: 0x00,
47 | TAC: 0x00,
48 | TMA: 0x00,
49 | }
50 | }
51 |
52 | // Update timer counter registers
53 | // If timer is overflowed return true
54 | func (timer *Timer) Update(cycles uint) bool {
55 | r := false
56 | for cycles > 0 {
57 | cycles--
58 | old := timer.internalCounter
59 | timer.internalCounter += 4
60 |
61 | if !timer.isStarted() {
62 | continue
63 | }
64 | if !timer.hasFallingEdgeDetected(old, timer.internalCounter) {
65 | continue
66 | }
67 | timer.TIMA++
68 | if timer.TIMA == 0 {
69 | timer.TIMA = timer.TMA
70 | r = true
71 | }
72 | }
73 | return r
74 | }
75 |
76 | func (timer *Timer) Read(addr types.Word) byte {
77 | switch addr {
78 | case DIV:
79 | return byte(timer.internalCounter >> 8)
80 | case TIMA:
81 | return timer.TIMA
82 | case TMA:
83 | return timer.TMA
84 | case TAC:
85 | return timer.TAC
86 | }
87 | panic("Illegal access detected.")
88 | }
89 |
90 | func (timer *Timer) Write(addr types.Word, data byte) {
91 | switch addr {
92 | case DIV:
93 | // Writing any value sets it to $00.
94 | // When writing to DIV, the whole counter is reseted, so the timer is also affected.
95 | // When writing to DIV, if the current output is '1' and timer is enabled
96 | // as the new value after reseting DIV will be '0', the falling edge detector will detect a falling edge and TIMA will increase.
97 | if timer.hasFallingEdgeDetected(timer.internalCounter, 0) {
98 | timer.TIMA++
99 | }
100 | timer.internalCounter = 0
101 | case TIMA:
102 | timer.TIMA = data
103 | case TMA:
104 | timer.TMA = data
105 | case TAC:
106 | timer.TAC = data
107 | }
108 | }
109 |
110 | func (timer *Timer) isStarted() bool {
111 | return timer.TAC&0x04 == 0x04
112 | }
113 |
114 | func (timer *Timer) hasFallingEdgeDetected(old, new uint16) bool {
115 | mask := uint16(1 << timer.getMaskBit())
116 | return ((old & mask) != 0) && ((new & mask) == 0)
117 | }
118 |
119 | func (timer *Timer) getMaskBit() uint {
120 | switch timer.TAC & 0x03 {
121 | case 0x00:
122 | return 9
123 | case 0x01:
124 | return 3
125 | case 0x02:
126 | return 5
127 | case 0x03:
128 | return 7
129 | }
130 | return 0
131 | }
132 |
--------------------------------------------------------------------------------
/pkg/types/types.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import "image/color"
4 |
5 | // Register is generic register
6 | type Register = byte
7 |
8 | // RGB display rgb color
9 | type RGB struct {
10 | Red byte
11 | Green byte
12 | Blue byte
13 | }
14 |
15 | // Word for gb
16 | type Word uint16
17 |
18 | type ImageData = []color.RGBA
19 |
20 | type Bit int
21 |
22 | const (
23 | Bit0 Bit = 0x01
24 | Bit1 Bit = 0x02
25 | Bit2 Bit = 0x04
26 | Bit3 Bit = 0x08
27 | Bit4 Bit = 0x10
28 | Bit5 Bit = 0x20
29 | Bit6 Bit = 0x40
30 | Bit7 Bit = 0x80
31 | )
32 |
--------------------------------------------------------------------------------
/pkg/utils/byte.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "github.com/bokuweb/gopher-boy/pkg/types"
4 |
5 | func Bytes2Word(upper, lower byte) types.Word {
6 | return (types.Word(upper) << 8) ^ types.Word(lower)
7 | }
8 |
9 | func Word2Bytes(w types.Word) (byte, byte) {
10 | return byte(w >> 8), byte(w & 0x00FF)
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/utils/loader.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bufio"
5 | "os"
6 | )
7 |
8 | // LoadROM loads gameboy ROMfile to buf
9 | func LoadROM(filename string) ([]byte, error) {
10 | file, err := os.Open(filename)
11 | if err != nil {
12 | return nil, err
13 | }
14 | defer file.Close()
15 | stats, statsErr := file.Stat()
16 | if statsErr != nil {
17 | return nil, statsErr
18 | }
19 | size := stats.Size()
20 | bytes := make([]byte, size)
21 | b := bufio.NewReader(file)
22 | _, err = b.Read(bytes)
23 | if err != nil {
24 | return nil, err
25 | }
26 | return bytes, nil
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/window/native.go:
--------------------------------------------------------------------------------
1 | // +build native
2 |
3 | package window
4 |
5 | import (
6 | "image/color"
7 | "math"
8 |
9 | "github.com/bokuweb/gopher-boy/pkg/constants"
10 | "github.com/bokuweb/gopher-boy/pkg/pad"
11 | "github.com/faiface/pixel"
12 | "github.com/faiface/pixel/pixelgl"
13 | "golang.org/x/image/colornames"
14 | )
15 |
16 | // Window is
17 | type Window struct {
18 | win *pixelgl.Window
19 | image *pixel.PictureData
20 | pad *pad.Pad
21 | }
22 |
23 | // Render renders the pixels on the window.
24 | func (w *Window) Render(buf []byte) {
25 |
26 | imgData := make([]color.RGBA, constants.ScreenWidth*constants.ScreenHeight)
27 | i := 0
28 | for i*4 < len(buf) {
29 | y := constants.ScreenHeight - (i / constants.ScreenWidth) - 1
30 | imgData[y*constants.ScreenWidth+i%constants.ScreenWidth] = color.RGBA{buf[i*4], buf[i*4+1], buf[i*4+2], buf[i*4+3]}
31 | i++
32 | }
33 |
34 | w.image.Pix = imgData
35 |
36 | bg := color.RGBA{R: 0x0F, G: 0x38, B: 0x0F, A: 0xFF}
37 | w.win.Clear(bg)
38 |
39 | spr := pixel.NewSprite(pixel.Picture(w.image), pixel.R(0, 0, constants.ScreenWidth, constants.ScreenHeight))
40 | spr.Draw(w.win, pixel.IM)
41 | w.updateCamera()
42 | w.win.Update()
43 | }
44 |
45 | func (w *Window) Run(f func()) {
46 | pixelgl.Run(f)
47 | }
48 |
49 | func (w *Window) updateCamera() {
50 | xScale := w.win.Bounds().W() / constants.ScreenWidth
51 | yScale := w.win.Bounds().H() / constants.ScreenHeight
52 | scale := math.Min(yScale, xScale)
53 |
54 | shift := w.win.Bounds().Size().Scaled(0.5).Sub(pixel.ZV)
55 | cam := pixel.IM.Scaled(pixel.ZV, scale).Moved(shift)
56 | w.win.SetMatrix(cam)
57 | }
58 |
59 | func (w *Window) Init() {
60 | cfg := pixelgl.WindowConfig{
61 | Title: "gopher-boy",
62 | Bounds: pixel.R(0, 0, constants.ScreenWidth, constants.ScreenHeight),
63 | }
64 | win, err := pixelgl.NewWindow(cfg)
65 | if err != nil {
66 | panic(err)
67 | }
68 | win.Clear(colornames.Skyblue)
69 | w.win = win
70 | w.image = &pixel.PictureData{
71 | Pix: make([]color.RGBA, constants.ScreenWidth*constants.ScreenHeight),
72 | Stride: constants.ScreenWidth,
73 | Rect: pixel.R(0, 0, constants.ScreenWidth, constants.ScreenHeight),
74 | }
75 |
76 | // Hack: https://github.com/faiface/pixel/issues/140
77 | pos := win.GetPos()
78 | win.SetPos(pixel.ZV)
79 | win.SetPos(pos)
80 | w.updateCamera()
81 | win.Update()
82 | }
83 |
84 | func (w *Window) PollKey() {
85 | for key, button := range keyMap {
86 | if w.win.JustPressed(key) {
87 | w.pad.Press(button)
88 | }
89 | if w.win.JustReleased(key) {
90 | w.pad.Release(button)
91 | }
92 | }
93 | }
94 |
95 | func (w *Window) KeyDown(button byte) {
96 | /* NOP */
97 | }
98 |
99 | func (w *Window) KeyUp(button byte) {
100 | /* NOP */
101 | }
102 |
103 | var keyMap = map[pixelgl.Button]pad.Button{
104 | pixelgl.KeyZ: pad.A,
105 | pixelgl.KeyX: pad.B,
106 | pixelgl.KeyBackspace: pad.Select,
107 | pixelgl.KeyEnter: pad.Start,
108 | pixelgl.KeyRight: pad.Right,
109 | pixelgl.KeyLeft: pad.Left,
110 | pixelgl.KeyUp: pad.Up,
111 | pixelgl.KeyDown: pad.Down,
112 | }
113 |
--------------------------------------------------------------------------------
/pkg/window/wasm.go:
--------------------------------------------------------------------------------
1 | // +build wasm
2 |
3 | package window
4 |
5 | import (
6 | "github.com/bokuweb/gopher-boy/pkg/pad"
7 | )
8 |
9 | // Window is
10 | type Window struct {
11 | keyState byte
12 | pad *pad.Pad
13 | }
14 |
15 | // Render renders the pixels on the window.
16 | func (w *Window) Render(imageData []byte) {
17 | /* NOP */
18 | }
19 |
20 | func (w *Window) Run(f func()) {
21 | /* NOP */
22 | }
23 |
24 | func (w *Window) Init() {
25 | /* NOP */
26 | }
27 |
28 | func (w *Window) PollKey() {
29 | i := byte(0)
30 | for i < 8 {
31 | b := byte(0x01 << i)
32 | if w.keyState&b != 0 {
33 | w.pad.Press(pad.Button(b))
34 | } else {
35 | w.pad.Release(pad.Button(b))
36 | }
37 | i++
38 | }
39 | }
40 |
41 | func (w *Window) KeyDown(button byte) {
42 | w.keyState |= byte(button)
43 | }
44 |
45 | func (w *Window) KeyUp(button byte) {
46 | w.keyState &= ^byte(button)
47 | }
48 |
--------------------------------------------------------------------------------
/pkg/window/window.go:
--------------------------------------------------------------------------------
1 | package window
2 |
3 | import (
4 | "github.com/bokuweb/gopher-boy/pkg/pad"
5 | )
6 |
7 | func NewWindow(pad *pad.Pad) *Window {
8 | return &Window{pad: pad}
9 | }
10 |
--------------------------------------------------------------------------------
/roms/acceptance/add_sp_e_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/add_sp_e_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/bits/mem_oam.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/bits/mem_oam.gb
--------------------------------------------------------------------------------
/roms/acceptance/bits/reg_f.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/bits/reg_f.gb
--------------------------------------------------------------------------------
/roms/acceptance/bits/unused_hwio-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/bits/unused_hwio-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_div-S.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_div-S.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_div-dmg0.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_div-dmg0.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_div-dmgABCmgb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_div-dmgABCmgb.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_div2-S.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_div2-S.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_hwio-S.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_hwio-S.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_hwio-dmg0.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_hwio-dmg0.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_hwio-dmgABCmgb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_hwio-dmgABCmgb.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_regs-dmg0.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_regs-dmg0.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_regs-dmgABC.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_regs-dmgABC.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_regs-mgb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_regs-mgb.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_regs-sgb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_regs-sgb.gb
--------------------------------------------------------------------------------
/roms/acceptance/boot_regs-sgb2.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/boot_regs-sgb2.gb
--------------------------------------------------------------------------------
/roms/acceptance/call_cc_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/call_cc_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/call_cc_timing2.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/call_cc_timing2.gb
--------------------------------------------------------------------------------
/roms/acceptance/call_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/call_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/call_timing2.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/call_timing2.gb
--------------------------------------------------------------------------------
/roms/acceptance/di_timing-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/di_timing-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/div_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/div_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ei_sequence.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ei_sequence.gb
--------------------------------------------------------------------------------
/roms/acceptance/ei_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ei_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/halt_ime0_ei.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/halt_ime0_ei.gb
--------------------------------------------------------------------------------
/roms/acceptance/halt_ime0_nointr_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/halt_ime0_nointr_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/halt_ime1_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/halt_ime1_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/halt_ime1_timing2-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/halt_ime1_timing2-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/if_ie_registers.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/if_ie_registers.gb
--------------------------------------------------------------------------------
/roms/acceptance/instr/daa.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/instr/daa.gb
--------------------------------------------------------------------------------
/roms/acceptance/interrupts/ie_push.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/interrupts/ie_push.gb
--------------------------------------------------------------------------------
/roms/acceptance/intr_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/intr_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/jp_cc_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/jp_cc_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/jp_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/jp_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ld_hl_sp_e_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ld_hl_sp_e_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/oam_dma/basic.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/oam_dma/basic.gb
--------------------------------------------------------------------------------
/roms/acceptance/oam_dma/reg_read.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/oam_dma/reg_read.gb
--------------------------------------------------------------------------------
/roms/acceptance/oam_dma/sources-dmgABCmgbS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/oam_dma/sources-dmgABCmgbS.gb
--------------------------------------------------------------------------------
/roms/acceptance/oam_dma_restart.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/oam_dma_restart.gb
--------------------------------------------------------------------------------
/roms/acceptance/oam_dma_start.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/oam_dma_start.gb
--------------------------------------------------------------------------------
/roms/acceptance/oam_dma_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/oam_dma_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/pop_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/pop_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/hblank_ly_scx_timing-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/hblank_ly_scx_timing-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/intr_1_2_timing-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/intr_1_2_timing-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/intr_2_0_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/intr_2_0_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/intr_2_mode0_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/intr_2_mode0_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/intr_2_mode0_timing_sprites.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/intr_2_mode0_timing_sprites.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/intr_2_mode3_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/intr_2_mode3_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/intr_2_oam_ok_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/intr_2_oam_ok_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/lcdon_timing-dmgABCmgbS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/lcdon_timing-dmgABCmgbS.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/lcdon_write_timing-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/lcdon_write_timing-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/stat_irq_blocking.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/stat_irq_blocking.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/stat_lyc_onoff.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/stat_lyc_onoff.gb
--------------------------------------------------------------------------------
/roms/acceptance/ppu/vblank_stat_intr-GS.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ppu/vblank_stat_intr-GS.gb
--------------------------------------------------------------------------------
/roms/acceptance/push_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/push_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/rapid_di_ei.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/rapid_di_ei.gb
--------------------------------------------------------------------------------
/roms/acceptance/ret_cc_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ret_cc_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/ret_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/ret_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/reti_intr_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/reti_intr_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/reti_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/reti_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/rst_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/rst_timing.gb
--------------------------------------------------------------------------------
/roms/acceptance/serial/boot_sclk_align-dmgABCmgb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/serial/boot_sclk_align-dmgABCmgb.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/div_write.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/div_write.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/rapid_toggle.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/rapid_toggle.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim00.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim00.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim00_div_trigger.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim00_div_trigger.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim01.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim01.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim01_div_trigger.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim01_div_trigger.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim10.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim10.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim10_div_trigger.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim10_div_trigger.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim11.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim11.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tim11_div_trigger.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tim11_div_trigger.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tima_reload.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tima_reload.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tima_write_reloading.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tima_write_reloading.gb
--------------------------------------------------------------------------------
/roms/acceptance/timer/tma_write_reloading.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/acceptance/timer/tma_write_reloading.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/01-special.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/01-special.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/02-interrupts.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/02-interrupts.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/03-op sp,hl.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/03-op sp,hl.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/04-op r,imm.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/04-op r,imm.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/05-op rp.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/05-op rp.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/06-ld r,r.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/06-ld r,r.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/07-jr,jp,call,ret,rst.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/07-jr,jp,call,ret,rst.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/08-misc instrs.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/08-misc instrs.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/09-op r,r.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/09-op r,r.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/10-bit ops.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/10-bit ops.gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/11-op a,(hl).gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/11-op a,(hl).gb
--------------------------------------------------------------------------------
/roms/cpu_instrs/cpu_instrs.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/cpu_instrs/cpu_instrs.gb
--------------------------------------------------------------------------------
/roms/dino/dino.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/dino/dino.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/bits_bank1.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/bits_bank1.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/bits_bank2.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/bits_bank2.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/bits_mode.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/bits_mode.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/bits_ramg.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/bits_ramg.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/multicart_rom_8Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/multicart_rom_8Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/ram_256kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/ram_256kb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/ram_64kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/ram_64kb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/rom_16Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/rom_16Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/rom_1Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/rom_1Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/rom_2Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/rom_2Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/rom_4Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/rom_4Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/rom_512kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/rom_512kb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc1/rom_8Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc1/rom_8Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/bits_ramg.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/bits_ramg.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/bits_romb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/bits_romb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/bits_unused.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/bits_unused.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/ram.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/ram.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/rom_1Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/rom_1Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/rom_2Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/rom_2Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc2/rom_512kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc2/rom_512kb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_16Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_16Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_1Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_1Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_2Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_2Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_32Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_32Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_4Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_4Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_512kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_512kb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_64Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_64Mb.gb
--------------------------------------------------------------------------------
/roms/emulator-only/mbc5/rom_8Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/emulator-only/mbc5/rom_8Mb.gb
--------------------------------------------------------------------------------
/roms/hangman/Hangman (PD).gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/hangman/Hangman (PD).gb
--------------------------------------------------------------------------------
/roms/helloworld/README.md:
--------------------------------------------------------------------------------
1 | # hello 8-bit world!
2 | Example of Gameboy (DMG) "Hello world!" written in assembly language. This is simple version that doesn't use interrupts. Assemble and link with [RGBASM](https://github.com/rednex/rgbds).
3 |
4 | ```
5 | rgbasm -ohello.o hello.s
6 | rgblink -p00 -ohello.gb hello.o
7 | rgbfix -v -m00 -p00 -tHello hello.gb
8 | ```
9 |
--------------------------------------------------------------------------------
/roms/helloworld/hello.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/helloworld/hello.gb
--------------------------------------------------------------------------------
/roms/helloworld/hello.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/helloworld/hello.o
--------------------------------------------------------------------------------
/roms/helloworld/hello.s:
--------------------------------------------------------------------------------
1 | INCLUDE "hardware.inc"
2 |
3 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe
4 | nop
5 | jp Start
6 |
7 | SECTION "Example",ROM0[$150] ; code starts here
8 |
9 | Start:
10 | di ; disable interrupts
11 | ld sp,$E000 ; setup stack
12 |
13 | .wait_vbl
14 | ld a,[rLY]
15 | cp $90
16 | jr nz,.wait_vbl ; wait for vblank to properly disable lcd
17 |
18 | xor a
19 | ld [rIF],a ; reset usual regs
20 | ld [rLCDC],a
21 | ld [rSTAT],a
22 | ld [rSCX],a
23 | ld [rSCY],a
24 | ld [rLYC],a
25 | ld [rIE],a
26 |
27 | ld hl,_RAM ; fill ram with a, a = 0 here
28 | ld bc,$2000-2 ; watch out for stack ;)
29 | call fill
30 |
31 | ld hl,_HRAM
32 | ld c,$80 ; a = 0, b = 0 here, so let's save a cycle or two
33 | call fill
34 |
35 | ld hl,_VRAM
36 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so..
37 | call fill
38 |
39 | ld a,$20 ; ascii code for 'space'
40 | ; no need to setup hl since _SCRN0 and _SCRN1 are part of _VRAM, just continue
41 | ld b,8 ; bc should be $800; c = 0 here, so..
42 | call fill
43 |
44 | ld a,%10010011 ; 00 - light, 01 - gray, 10 - dark grey, 11 - dark
45 | ld [rBGP],a ; bg palette
46 | ld [rOBP0],a ; obj palettes
47 | ld [rOBP1],a
48 |
49 | ld hl,font ; font data
50 | ld de,_VRAM+$200 ; place it here to get ascii mapping
51 | ld bc,1776 ; speccy.chr file size
52 | call copy
53 |
54 | ld hl,text ; hello message
55 | ld de,_SCRN0+$100 ; center it a bit
56 | ld c,text_end-text ; b = 0, our string = 18 chars, so..
57 | call copy ; lcdc is disabled so you have 'easy' access to vram
58 |
59 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON
60 | ld [rLCDC],a ; enable lcd
61 |
62 | .the_end
63 | halt
64 | nop
65 |
66 | jr .the_end
67 |
68 | ;-------------------------------------------------------------------------------
69 | copy:
70 | ;-------------------------------------------------------------------------------
71 | ; hl - source address
72 | ; de - destination
73 | ; bc - size
74 | inc b
75 | inc c
76 | jr .skip
77 | .copy
78 | ld a,[hl+]
79 | ld [de],a
80 | inc de
81 | .skip
82 | dec c
83 | jr nz,.copy
84 | dec b
85 | jr nz,.copy
86 | ret
87 |
88 | ;-------------------------------------------------------------------------------
89 | fill:
90 | ;-------------------------------------------------------------------------------
91 | ; a - byte to fill with
92 | ; hl - destination address
93 | ; bc - size of area to fill
94 |
95 | inc b
96 | inc c
97 | jr .skip
98 | .fill
99 | ld [hl+],a
100 | .skip
101 | dec c
102 | jr nz,.fill
103 | dec b
104 | jr nz,.fill
105 | ret
106 |
107 | ;-------------------------------------------------------------------------------
108 |
109 | font:
110 | INCBIN "speccy.chr" ; created with https://github.com/gitendo/bmp2cgb
111 |
112 | text:
113 | DB " Hello 8-bit world! "
114 | text_end:
--------------------------------------------------------------------------------
/roms/helloworld/make.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | cls
3 | rgbasm -ohello.o hello.s
4 | rgblink -p00 -ohello.gb hello.o
5 | rgbfix -v -m00 -p00 -tHello hello.gb
6 |
--------------------------------------------------------------------------------
/roms/helloworld/speccy.chr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/helloworld/speccy.chr
--------------------------------------------------------------------------------
/roms/instr_timing/instr_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/instr_timing/instr_timing.gb
--------------------------------------------------------------------------------
/roms/instr_timing/readme.txt:
--------------------------------------------------------------------------------
1 | Game Boy CPU Instruction Timing Test
2 | ------------------------------------
3 | This ROM tests the timings of all CPU instructions except HALT, STOP,
4 | and the 11 illegal opcodes. For conditional instructions, it tests taken
5 | and not taken timings. This test requires proper timer operation (TAC,
6 | TIMA, TMA).
7 |
8 | Failed instructions are listed as
9 |
10 | [CB] opcode:measured time-correct time
11 |
12 | Times are in terms of instruction cycles, where NOP takes one cycle.
13 |
14 |
15 | Verified cycle timing tables
16 | ----------------------------
17 | The test internally uses a table of proper cycle times, which can be
18 | used in an emulator to ensure proper timing. The only changes below are
19 | removal of the .byte prefixes, and addition of commas at the ends, so
20 | that they can be used without changes in most programming languages. For
21 | added correctness assurance, the original tables can be found at the end
22 | of the source code.
23 |
24 | Normal instructions:
25 |
26 | 1,3,2,2,1,1,2,1,5,2,2,2,1,1,2,1,
27 | 0,3,2,2,1,1,2,1,3,2,2,2,1,1,2,1,
28 | 2,3,2,2,1,1,2,1,2,2,2,2,1,1,2,1,
29 | 2,3,2,2,3,3,3,1,2,2,2,2,1,1,2,1,
30 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
31 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
32 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
33 | 2,2,2,2,2,2,0,2,1,1,1,1,1,1,2,1,
34 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
35 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
36 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
37 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
38 | 2,3,3,4,3,4,2,4,2,4,3,0,3,6,2,4,
39 | 2,3,3,0,3,4,2,4,2,4,3,0,3,0,2,4,
40 | 3,3,2,0,0,4,2,4,4,1,4,0,0,0,2,4,
41 | 3,3,2,1,0,4,2,4,3,2,4,1,0,0,2,4
42 |
43 | CB-prefixed instructions:
44 |
45 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
46 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
47 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
48 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
49 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
50 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
51 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
52 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
53 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
54 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
55 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
56 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
57 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
58 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
59 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
60 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
61 |
62 |
63 | Internal operation
64 | ------------------
65 | Before each instruction is executed, the test sets up registers and
66 | memory in such a way that the instruction will cleanly execute and then
67 | end up at a common destination, without trashing anything important. The
68 | timing itself is done by first synchronizing to the timer via a loop,
69 | executing the instruction, then using a similar loop to determine how
70 | many clocks elapsed.
71 |
72 |
73 | Failure codes
74 | -------------
75 | Failed tests may print a failure code, and also short description of the
76 | problem. For more information about a failure code, look in the
77 | corresponding source file in source/; the point in the code where
78 | "set_test n" occurs is where that failure code will be generated.
79 | Failure code 1 is a general failure of the test; any further information
80 | will be printed.
81 |
82 | Note that once a sub-test fails, no further tests for that file are run.
83 |
84 |
85 | Console output
86 | --------------
87 | Information is printed on screen in a way that needs only minimum LCD
88 | support, and won't hang if LCD output isn't supported at all.
89 | Specifically, while polling LY to wait for vblank, it will time out if
90 | it takes too long, so LY always reading back as the same value won't
91 | hang the test. It's also OK if scrolling isn't supported; in this case,
92 | text will appear starting at the top of the screen.
93 |
94 | Everything printed on screen is also sent to the game link port by
95 | writing the character to SB, then writing $81 to SC. This is useful for
96 | tests which print lots of information that scrolls off screen.
97 |
98 |
99 | Source code
100 | -----------
101 | Source code is included for all tests, in source/. It can be used to
102 | build the individual test ROMs. Code for the multi test isn't included
103 | due to the complexity of putting everything together.
104 |
105 | Code is written for the wla-dx assembler. To assemble a particular test,
106 | execute
107 |
108 | wla -o "source_filename.s" test.o
109 | wlalink linkfile test.gb
110 |
111 | Test code uses a common shell framework contained in common/.
112 |
113 |
114 | Internal framework operation
115 | ----------------------------
116 | Tests use a common framework for setting things up, reporting results,
117 | and ending. All files first include "shell.inc", which sets up the ROM
118 | header and shell code, and includes other commonly-used modules.
119 |
120 | One oddity is that test code is first copied to internal RAM at $D000,
121 | then executed there. This allows self-modification, and ensures the code
122 | is executed the same way it is on my devcart, which doesn't have a
123 | rewritable ROM as most do.
124 |
125 | Some macros are used to simplify common tasks:
126 |
127 | Macro Behavior
128 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129 | wreg addr,data Writes data to addr using LDH
130 | lda addr Loads byte from addr into A using LDH
131 | sta addr Stores A at addr using LDH
132 | delay n Delays n cycles, where NOP = 1 cycle
133 | delay_msec n Delays n milliseconds
134 | set_test n,"Cause" Sets failure code and optional string
135 |
136 | Routines and macros are documented where they are defined.
137 |
138 | --
139 | Shay Green
140 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/build_gbs.s:
--------------------------------------------------------------------------------
1 | ; Build as GBS music file
2 |
3 | .memoryMap
4 | defaultSlot 0
5 | slot 0 $3000 size $1000
6 | slot 1 $C000 size $1000
7 | .endMe
8 |
9 | .romBankSize $1000
10 | .romBanks 2
11 |
12 |
13 | ;;;; GBS music file header
14 |
15 | .byte "GBS"
16 | .byte 1 ; vers
17 | .byte 1 ; songs
18 | .byte 1 ; first song
19 | .word load_addr
20 | .word reset
21 | .word gbs_play
22 | .word std_stack
23 | .byte 0,0 ; timer
24 | .ds $60,0
25 | load_addr:
26 |
27 | ; WLA assumes we're building ROM and messes
28 | ; with bytes at the beginning, so skip them.
29 | .ds $100,0
30 |
31 |
32 | ;;;; Shell
33 |
34 | .include "runtime.s"
35 |
36 | init_runtime:
37 | ld a,$01 ; Identify as DMG hardware
38 | ld (gb_id),a
39 | .ifdef TEST_NAME
40 | print_str TEST_NAME,newline,newline
41 | .endif
42 | ret
43 |
44 | std_print:
45 | sta SB
46 | wreg SC,$81
47 | delay 2304
48 | ret
49 |
50 | post_exit:
51 | call play_byte
52 | forever:
53 | wreg NR52,0 ; sound off
54 | - jp -
55 |
56 | .ifndef CUSTOM_RESET
57 | gbs_play:
58 | .endif
59 | console_flush:
60 | console_normal:
61 | console_inverse:
62 | console_set_mode:
63 | ret
64 |
65 | ; Reports A in binary as high and low tones, with
66 | ; leading low tone for reference. Omits leading
67 | ; zeroes.
68 | ; Preserved: AF, BC, DE, HL
69 | play_byte:
70 | push af
71 | push hl
72 |
73 | ; HL = (A << 1) | 1
74 | scf
75 | rla
76 | ld l,a
77 | ld h,0
78 | rl h
79 |
80 | ; Shift left until next-to-top bit is 1
81 | - add hl,hl
82 | bit 6,h
83 | jr z,-
84 |
85 | ; Reset sound
86 | delay_msec 400
87 | wreg NR52,0 ; sound off
88 | wreg NR52,$80 ; sound on
89 | wreg NR51,$FF ; mono
90 | wreg NR50,$77 ; volume
91 |
92 | - add hl,hl
93 |
94 | ; Low or high pitch based on bit shifted out
95 | ; of HL
96 | ld a,0
97 | jr nc,+
98 | ld a,$FF
99 | + sta NR23
100 |
101 | ; Play short tone
102 | wreg NR21,$A0
103 | wreg NR22,$F0
104 | wreg NR24,$86
105 | delay_msec 75
106 | wreg NR22,0
107 | wreg NR23,$F8
108 | wreg NR24,$87
109 | delay_msec 200
110 |
111 | ; Loop until HL = $8000
112 | ld a,h
113 | xor $80
114 | or l
115 | jr nz,-
116 |
117 | pop hl
118 | pop af
119 | ret
120 |
121 | .ends
122 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/build_rom.s:
--------------------------------------------------------------------------------
1 | ; Build as GB ROM
2 |
3 | .memoryMap
4 | defaultSlot 0
5 | slot 0 $0000 size $4000
6 | slot 1 $C000 size $4000
7 | .endMe
8 |
9 | .romBankSize $4000 ; generates $8000 byte ROM
10 | .romBanks 2
11 |
12 | .cartridgeType 1 ; MBC1
13 | .computeChecksum
14 | .computeComplementCheck
15 |
16 |
17 | ;;;; GB ROM header
18 |
19 | ; GB header read by bootrom
20 | .org $100
21 | nop
22 | jp reset
23 |
24 | ; Nintendo logo required for proper boot
25 | .byte $CE,$ED,$66,$66,$CC,$0D,$00,$0B
26 | .byte $03,$73,$00,$83,$00,$0C,$00,$0D
27 | .byte $00,$08,$11,$1F,$88,$89,$00,$0E
28 | .byte $DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
29 | .byte $BB,$BB,$67,$63,$6E,$0E,$EC,$CC
30 | .byte $DD,$DC,$99,$9F,$BB,$B9,$33,$3E
31 |
32 | ; Internal name
33 | .ifdef ROM_NAME
34 | .byte ROM_NAME
35 | .endif
36 |
37 | ; CGB/DMG requirements
38 | .org $143
39 | .ifdef REQUIRE_CGB
40 | .byte $C0
41 | .else
42 | .ifndef REQUIRE_DMG
43 | .byte $80
44 | .endif
45 | .endif
46 |
47 | .org $200
48 |
49 |
50 | ;;;; Shell
51 |
52 | .include "runtime.s"
53 | .include "console.s"
54 |
55 | init_runtime:
56 | call console_init
57 | .ifdef TEST_NAME
58 | print_str TEST_NAME,newline,newline
59 | .endif
60 | ret
61 |
62 | std_print:
63 | push af
64 | sta SB
65 | wreg SC,$81
66 | delay 2304
67 | pop af
68 | jp console_print
69 |
70 | post_exit:
71 | call console_show
72 | call play_byte
73 | forever:
74 | wreg NR52,0 ; sound off
75 | - jr -
76 |
77 | play_byte:
78 | ret
79 |
80 | .ends
81 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/console.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/instr_timing/source/common/console.bin
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/console.s:
--------------------------------------------------------------------------------
1 | ; Scrolling text console
2 |
3 | ; Console is 20x18 characters. Buffers lines, so
4 | ; output doesn't appear until a newline or flush.
5 | ; If scrolling isn't supported (i.e. SCY is treated
6 | ; as if always zero), the first 18 lines will
7 | ; still print properly). Also works properly if
8 | ; LY isn't supported (always reads back as the same
9 | ; value).
10 |
11 | .define console_width 20
12 |
13 | .define console_buf bss+0
14 | .define console_pos bss+console_width
15 | .define console_mode bss+console_width+1
16 | .define console_scroll bss+console_width+2
17 | .redefine bss bss+console_width+3
18 |
19 |
20 | ; Waits for start of LCD blanking period
21 | ; Preserved: BC, DE, HL
22 | console_wait_vbl:
23 | push bc
24 |
25 | ; Wait for start of vblank, with
26 | ; timeout in case LY doesn't work
27 | ; or LCD is disabled.
28 | ld bc,-1250
29 | - inc bc
30 | ld a,b
31 | or c
32 | jr z,@timeout
33 | lda LY
34 | cp 144
35 | jr nz,-
36 | @timeout:
37 |
38 | pop bc
39 | ret
40 |
41 |
42 | ; Initializes text console
43 | console_init:
44 | call console_hide
45 |
46 | ; CGB-specific inits
47 | ld a,(gb_id)
48 | and gb_id_cgb
49 | call nz,@init_cgb
50 |
51 | ; Clear nametable
52 | ld a,' '
53 | call @fill_nametable
54 |
55 | ; Load tiles
56 | ld hl,TILES+$200
57 | ld c,0
58 | call @load_tiles
59 | ld hl,TILES+$A00
60 | ld c,$FF
61 | call @load_tiles
62 |
63 | ; Init state
64 | ld a,console_width
65 | ld (console_pos),a
66 | ld a,0
67 | ld (console_mode),a
68 | ld a,-8
69 | ld (console_scroll),a
70 | call console_scroll_up_
71 | jr console_show
72 |
73 | @fill_nametable:
74 | ld hl,BGMAP0
75 | ld b,4
76 | - ld (hl),a
77 | inc l
78 | jr nz,-
79 | inc h
80 | dec b
81 | jr nz,-
82 | ret
83 |
84 | @init_cgb:
85 | ; Clear palette
86 | wreg $FF68,$80
87 | ld b,16
88 | - wreg $FF69,$FF
89 | wreg $FF69,$7F
90 | wreg $FF69,$00
91 | wreg $FF69,$00
92 | wreg $FF69,$00
93 | wreg $FF69,$00
94 | wreg $FF69,$00
95 | wreg $FF69,$00
96 | dec b
97 | jr nz,-
98 |
99 | ; Clear attributes
100 | ld a,1
101 | ld (VBK),a
102 | ld a,0
103 | call @fill_nametable
104 |
105 | ld a,0
106 | ld (VBK),a
107 | ret
108 |
109 | @load_tiles:
110 | ld de,ASCII
111 | ld b,96
112 | -- push bc
113 | ld b,8
114 | - ld a,(de)
115 | inc de
116 | xor c
117 | ldi (hl),a
118 | ldi (hl),a
119 | dec b
120 | jr nz,-
121 | pop bc
122 | dec b
123 | jr nz,--
124 | ret
125 |
126 |
127 | ; Shows console display
128 | ; Preserved: AF, BC, DE, HL
129 | console_show:
130 | push af
131 |
132 | ; Enable LCD
133 | call console_wait_vbl
134 | wreg LCDC,$91
135 | wreg SCX,0
136 | wreg BGP,$E4
137 |
138 | jp console_apply_scroll_
139 |
140 |
141 | ; Hides console display by turning LCD off
142 | ; Preserved: AF, BC, DE, HL
143 | console_hide:
144 | push af
145 |
146 | ; LCD off
147 | call console_wait_vbl
148 | wreg LCDC,$11
149 |
150 | pop af
151 | ret
152 |
153 |
154 | ; Changes to normal text mode
155 | ; Preserved: BC, DE, HL
156 | console_normal:
157 | xor a
158 | jr console_set_mode
159 |
160 | ; Changes to inverse text mode
161 | ; Preserved: BC, DE, HL
162 | console_inverse:
163 | ld a,$80
164 |
165 | ; Changes console mode to A.
166 | ; 0: Normal, $80: Inverse
167 | ; Preserved: BC, DE, HL
168 | console_set_mode:
169 | and $80
170 | ld (console_mode),a
171 | ret
172 |
173 |
174 | ; Prints char A to console. Will not appear until
175 | ; a newline or flush occurs.
176 | ; Preserved: AF, BC, DE, HL
177 | console_print:
178 | push af
179 |
180 | cp 10
181 | jr z,console_newline_
182 |
183 | push hl
184 | push af
185 | ld hl,console_pos
186 | ldi a,(hl)
187 | cp BGMAP0) >> 2
273 | add hl,hl
274 | add hl,hl
275 |
276 | ; Copy line
277 | ld de,console_buf + console_width
278 | - dec e
279 | ld a,(de)
280 | ldi (hl),a
281 | ld a,e
282 | cp checksum
67 | ldi (hl),a
68 | ld (hl),d
69 | inc l
70 | ld (hl),c
71 | inc l
72 | ld (hl),b
73 |
74 | pop hl
75 | pop de
76 | pop bc
77 | pop af
78 | ret
79 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/gb.inc:
--------------------------------------------------------------------------------
1 | ; Game Boy hardware addresses
2 |
3 | ; Memory
4 | .define VRAM $8000 ; video memory
5 | .define TILES $8000 ; tile images
6 | .define BGMAP0 $9800 ; first 32x32 tilemap
7 | .define BGMAP1 $9C00 ; second 32x32 tilemap
8 | .define WRAM $C000 ; internal memory
9 | .define OAM $FE00 ; sprite memory
10 | .define HRAM $FF80 ; fast memory for LDH
11 |
12 | .define P1 $FF00
13 |
14 | ; Game link I/O
15 | .define SB $FF01
16 | .define SC $FF02
17 |
18 | ; Interrupts
19 | .define DIV $FF04
20 | .define TIMA $FF05
21 | .define TMA $FF06
22 | .define TAC $FF07
23 | .define IF $FF0F
24 | .define IE $FFFF
25 |
26 | ; LCD registers
27 | .define LCDC $FF40 ; control
28 | .define STAT $FF41 ; status
29 | .define SCY $FF42 ; scroll Y
30 | .define SCX $FF43 ; scroll X
31 | .define LY $FF44 ; current Y being rendered
32 | .define BGP $FF47
33 |
34 | .define KEY1 $FF4D ; for changing CPU speed
35 | .define VBK $FF4F
36 |
37 | ; Sound registers
38 | .define NR10 $FF10
39 | .define NR11 $FF11
40 | .define NR12 $FF12
41 | .define NR13 $FF13
42 | .define NR14 $FF14
43 |
44 | .define NR21 $FF16
45 | .define NR22 $FF17
46 | .define NR23 $FF18
47 | .define NR24 $FF19
48 |
49 | .define NR30 $FF1A
50 | .define NR31 $FF1B
51 | .define NR32 $FF1C
52 | .define NR33 $FF1D
53 | .define NR34 $FF1E
54 |
55 | .define NR41 $FF20
56 | .define NR42 $FF21
57 | .define NR43 $FF22
58 | .define NR44 $FF23
59 |
60 | .define NR50 $FF24
61 | .define NR51 $FF25
62 | .define NR52 $FF26
63 |
64 | .define WAVE $FF30
65 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/macros.inc:
--------------------------------------------------------------------------------
1 | ; General macros
2 |
3 | ; Reads A from addr, from $FF00 to $FFFF
4 | ; Preserved: F, BC, DE, HL
5 | ; Time: 3 cycles
6 | .macro lda ARGS addr
7 | ldh a,(addr - $FF00)
8 | .endm
9 |
10 | ; Writes A to addr, from $FF00 to $FFFF
11 | ; Preserved: AF, BC, DE, HL
12 | ; Time: 3 cycles
13 | .macro sta ARGS addr
14 | ldh (addr - $FF00),a
15 | .endm
16 |
17 | ; Writes immediate data to addr, from $FF00 to $FFFF
18 | ; Preserved: F, BC, DE, HL
19 | ; Time: 5 cycles
20 | .macro wreg ARGS addr, data
21 | ld a,data
22 | sta addr
23 | .endm
24 |
25 | ; Calls routine multiple times, with A having the
26 | ; value 'start' the first time, 'start+step' the
27 | ; second time, up to 'end' for the last time.
28 | ; Preserved: BC, DE, HL
29 | .macro for_loop ; routine,start,end,step
30 | ld a,\2
31 |
32 | for_loop\@:
33 | push af
34 | call \1
35 | pop af
36 |
37 | add \4
38 | cp <(\3 + \4)
39 | jr nz,for_loop\@
40 | .endm
41 |
42 | ; Calls routine n times. The value of A in the routine
43 | ; counts from 0 to n-1.
44 | ; Preserved: BC, DE, HL
45 | .macro loop_n_times ; routine,n
46 | for_loop \1,0,\2 - 1,+1
47 | .endm
48 |
49 | ; Same as for_loop, but counts with 16-bit value in BC.
50 | ; Preserved: DE, HL
51 | .macro for_loop16 ; routine,start,end,step
52 | ld bc,\2
53 |
54 | for_loop16\@:
55 | push bc
56 | call \1
57 | pop bc
58 |
59 | ld a,c
60 | add <\4
61 | ld c,a
62 |
63 | ld a,b
64 | adc >\4
65 | ld b,a
66 |
67 | cp >(\3+\4)
68 | jr nz,for_loop16\@
69 |
70 | ld a,c
71 | cp <(\3+\4)
72 | jr nz,for_loop16\@
73 | .endm
74 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/numbers.s:
--------------------------------------------------------------------------------
1 | ; Printing of numeric values
2 |
3 | ; Prints value of indicated register/pair
4 | ; as 2/4 hex digits, followed by a space.
5 | ; Updates checksum with printed values.
6 | ; Preserved: AF, BC, DE, HL
7 |
8 | print_regs:
9 | call print_af
10 | call print_bc
11 | call print_de
12 | call print_hl
13 | call print_newline
14 | ret
15 |
16 | print_a:
17 | push af
18 | print_a_:
19 | call print_hex
20 | ld a,' '
21 | call print_char_nocrc
22 | pop af
23 | ret
24 |
25 | print_af:
26 | push af
27 | call print_hex
28 | pop af
29 | print_f:
30 | push bc
31 | push af
32 | pop bc
33 | call print_c
34 | pop bc
35 | ret
36 |
37 | print_b:
38 | push af
39 | ld a,b
40 | jr print_a_
41 |
42 | print_c:
43 | push af
44 | ld a,c
45 | jr print_a_
46 |
47 | print_d:
48 | push af
49 | ld a,d
50 | jr print_a_
51 |
52 | print_e:
53 | push af
54 | ld a,e
55 | jr print_a_
56 |
57 | print_h:
58 | push af
59 | ld a,h
60 | jr print_a_
61 |
62 | print_l:
63 | push af
64 | ld a,l
65 | jr print_a_
66 |
67 | print_bc:
68 | push af
69 | push bc
70 | print_bc_:
71 | ld a,b
72 | call print_hex
73 | ld a,c
74 | pop bc
75 | jr print_a_
76 |
77 | print_de:
78 | push af
79 | push bc
80 | ld b,d
81 | ld c,e
82 | jr print_bc_
83 |
84 | print_hl:
85 | push af
86 | push bc
87 | ld b,h
88 | ld c,l
89 | jr print_bc_
90 |
91 |
92 | ; Prints A as two hex chars and updates checksum
93 | ; Preserved: BC, DE, HL
94 | print_hex:
95 | call update_crc
96 | print_hex_nocrc:
97 | push af
98 | swap a
99 | call +
100 | pop af
101 |
102 | + and $0F
103 | cp 10
104 | jr c,+
105 | add 7
106 | + add '0'
107 | jp print_char_nocrc
108 |
109 |
110 | ; Prints char_nz if Z flag is clear,
111 | ; char_z if Z flag is set.
112 | ; Preserved: AF, BC, DE, HL
113 | .macro print_nz ARGS char_nz, char_z
114 | push af
115 | ld a,char_nz
116 | jr nz,print_nz\@
117 | ld a,char_z
118 | print_nz\@:
119 | call print_char
120 | pop af
121 | .endm
122 |
123 |
124 | ; Prints char_nc if C flag is clear,
125 | ; char_c if C flag is set.
126 | ; Preserved: AF, BC, DE, HL
127 | .macro print_nc ARGS char_nc, char_c
128 | push af
129 | ld a,char_nc
130 | jr nz,print_nc\@
131 | ld a,char_c
132 | print_nc\@:
133 | call print_char
134 | pop af
135 | .endm
136 |
137 |
138 | ; Prints A as 2 decimal digits
139 | ; Preserved: AF, BC, DE, HL
140 | print_dec2:
141 | push af
142 | push bc
143 | jr +
144 |
145 |
146 | ; Prints A as 1-3 digit decimal value
147 | ; Preserved: AF, BC, DE, HL
148 | print_dec:
149 | push af
150 | push bc
151 |
152 | cp 10
153 | jr c,++
154 | ld c,100
155 | cp c
156 | call nc,@digit
157 | + ld c,10
158 | call @digit
159 | ++ add '0'
160 | call print_char
161 |
162 | pop bc
163 | pop af
164 | ret
165 |
166 | @digit:
167 | ld b,'0'-1
168 | - inc b
169 | sub c
170 | jr nc,-
171 | add c
172 |
173 | ld c,a
174 | ld a,b
175 | call print_char
176 | ld a,c
177 | ret
178 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/printing.s:
--------------------------------------------------------------------------------
1 | ; Main printing routine that checksums and
2 | ; prints to output device
3 |
4 | ; Character that does equivalent of print_newline
5 | .define newline 10
6 |
7 | ; Prints char without updating checksum
8 | ; Preserved: BC, DE, HL
9 | .define print_char_nocrc bss
10 | .redefine bss bss+3
11 |
12 |
13 | ; Initializes printing. HL = print routine
14 | init_printing:
15 | ld a,l
16 | ld (print_char_nocrc+1),a
17 | ld a,h
18 | ld (print_char_nocrc+2),a
19 | jr show_printing
20 |
21 |
22 | ; Hides/shows further printing
23 | ; Preserved: BC, DE, HL
24 | hide_printing:
25 | ld a,$C9 ; RET
26 | jr +
27 | show_printing:
28 | ld a,$C3 ; JP (nn)
29 | + ld (print_char_nocrc),a
30 | ret
31 |
32 |
33 | ; Prints character and updates checksum UNLESS
34 | ; it's a newline.
35 | ; Preserved: AF, BC, DE, HL
36 | print_char:
37 | push af
38 | cp newline
39 | call nz,update_crc
40 | call print_char_nocrc
41 | pop af
42 | ret
43 |
44 |
45 | ; Prints space. Does NOT update checksum.
46 | ; Preserved: AF, BC, DE, HL
47 | print_space:
48 | push af
49 | ld a,' '
50 | call print_char_nocrc
51 | pop af
52 | ret
53 |
54 |
55 | ; Advances to next line. Does NOT update checksum.
56 | ; Preserved: AF, BC, DE, HL
57 | print_newline:
58 | push af
59 | ld a,newline
60 | call print_char_nocrc
61 | pop af
62 | ret
63 |
64 |
65 | ; Prints immediate string
66 | ; Preserved: AF, BC, DE, HL
67 | .macro print_str ; string,string2
68 | push hl
69 | call print_str_
70 | .byte \1
71 | .if NARGS > 1
72 | .byte \2
73 | .endif
74 | .if NARGS > 2
75 | .byte \3
76 | .endif
77 | .byte 0
78 | pop hl
79 | .endm
80 |
81 | print_str_:
82 | pop hl
83 | call print_str_hl
84 | jp hl
85 |
86 |
87 | ; Prints zero-terminated string pointed to by HL.
88 | ; On return, HL points to byte AFTER zero terminator.
89 | ; Preserved: AF, BC, DE
90 | print_str_hl:
91 | push af
92 | jr +
93 | - call print_char
94 | + ldi a,(hl)
95 | or a
96 | jr nz,-
97 | pop af
98 | ret
99 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/runtime.s:
--------------------------------------------------------------------------------
1 | ; Common routines and runtime
2 |
3 | ; Must be defined by target-specific runtime:
4 | ;
5 | ; init_runtime: ; target-specific inits
6 | ; std_print: ; default routine to print char A
7 | ; post_exit: ; called at end of std_exit
8 | ; report_byte: ; report A to user
9 |
10 | .define RUNTIME_INCLUDED 1
11 |
12 | .ifndef bss
13 | ; address of next normal variable
14 | .define bss $D800
15 | .endif
16 |
17 | .ifndef dp
18 | ; address of next direct-page ($FFxx) variable
19 | .define dp $FF80
20 | .endif
21 |
22 | ; DMG/CGB hardware identifier
23 | .define gb_id_cgb $10 ; mask for testing CGB bit
24 | .define gb_id_devcart $04 ; mask for testing "on devcart" bit
25 |
26 | .define gb_id bss
27 | .redefine bss bss+1
28 |
29 | ; Stack is normally here
30 | .define std_stack $DFFF
31 |
32 | ; Copies $1000 bytes from HL to $C000, then jumps to it.
33 | ; A is preserved for jumped-to code.
34 | copy_to_wram_then_run:
35 | ld b,a
36 |
37 | ld de,$C000
38 | ld c,$10
39 | - ldi a,(hl)
40 | ld (de),a
41 | inc e
42 | jr nz,-
43 | inc d
44 | dec c
45 | jr nz,-
46 |
47 | ld a,b
48 | jp $C000
49 |
50 | .ifndef CUSTOM_RESET
51 | reset:
52 | ; Run code from $C000, as is done on devcart. This
53 | ; ensures minimal difference in how it behaves.
54 | ld hl,$4000
55 | jp copy_to_wram_then_run
56 |
57 | .bank 1 slot 1
58 | .org $0 ; otherwise wla pads with lots of zeroes
59 | jp std_reset
60 | .endif
61 |
62 | ; Common routines
63 | .include "gb.inc"
64 | .include "macros.inc"
65 | .include "delay.s"
66 | .include "crc.s"
67 | .include "printing.s"
68 | .include "numbers.s"
69 | .include "testing.s"
70 |
71 | ; Sets up hardware and runs main
72 | std_reset:
73 |
74 | ; Init hardware
75 | di
76 | ld sp,std_stack
77 |
78 | ; Save DMG/CGB id
79 | ld (gb_id),a
80 |
81 | ; Init hardware
82 | .ifndef BUILD_GBS
83 | wreg TAC,$00
84 | wreg IF,$00
85 | wreg IE,$00
86 | .endif
87 |
88 | wreg NR52,0 ; sound off
89 | wreg NR52,$80 ; sound on
90 | wreg NR51,$FF ; mono
91 | wreg NR50,$77 ; volume
92 |
93 | ; TODO: clear all memory?
94 |
95 | ld hl,std_print
96 | call init_printing
97 | call init_testing
98 | call init_runtime
99 | call reset_crc ; in case init_runtime prints anything
100 |
101 | delay_msec 250
102 |
103 | ; Run user code
104 | call main
105 |
106 | ; Default is to successful exit
107 | ld a,0
108 | jp exit
109 |
110 |
111 | ; Exits code and reports value of A
112 | exit:
113 | ld sp,std_stack
114 | push af
115 | call +
116 | pop af
117 | jp post_exit
118 |
119 | + push af
120 | call print_newline
121 | call show_printing
122 | pop af
123 |
124 | ; Report exit status
125 | cp 1
126 |
127 | ; 0: ""
128 | ret c
129 |
130 | ; 1: "Failed"
131 | jr nz,+
132 | print_str "Failed",newline
133 | ret
134 |
135 | ; n: "Failed #n"
136 | + print_str "Failed #"
137 | call print_dec
138 | call print_newline
139 | ret
140 |
141 | ; returnOrg puts this code AFTER user code.
142 | .section "runtime" returnOrg
143 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/testing.s:
--------------------------------------------------------------------------------
1 | ; Diagnostic and testing utilities
2 |
3 | .define result bss+0
4 | .define test_name bss+1
5 | .redefine bss bss+3
6 |
7 |
8 | ; Sets test code and optional error text
9 | ; Preserved: AF, BC, DE, HL
10 | .macro set_test ; code[,text[,text2]]
11 | push hl
12 | call set_test_
13 | jr @set_test\@
14 | .byte \1
15 | .if NARGS > 1
16 | .byte \2
17 | .endif
18 | .if NARGS > 2
19 | .byte \3
20 | .endif
21 | .byte 0
22 | @set_test\@:
23 | pop hl
24 | .endm
25 |
26 | set_test_:
27 | pop hl
28 | push hl
29 | push af
30 | inc hl
31 | inc hl
32 | ldi a,(hl)
33 | ld (result),a
34 | ld a,l
35 | ld (test_name),a
36 | ld a,h
37 | ld (test_name+1),a
38 | pop af
39 | ret
40 |
41 |
42 | ; Initializes testing module
43 | init_testing:
44 | set_test $FF
45 | call init_crc
46 | ret
47 |
48 |
49 | ; Reports "Passed", then exits with code 0
50 | tests_passed:
51 | call print_newline
52 | print_str "Passed"
53 | ld a,0
54 | jp exit
55 |
56 |
57 | ; Reports "Done" if set_test has never been used,
58 | ; "Passed" if set_test 0 was last used, or
59 | ; failure if set_test n was last used.
60 | tests_done:
61 | ld a,(result)
62 | inc a
63 | jr z,+
64 | dec a
65 | jr z,tests_passed
66 | jr test_failed
67 | + print_str "Done"
68 | ld a,0
69 | jp exit
70 |
71 |
72 | ; Reports current error text and exits with result code
73 | test_failed:
74 | ld a,(test_name)
75 | ld l,a
76 | ld a,(test_name+1)
77 | ld h,a
78 | ld a,(hl)
79 | or a
80 | jr z,+
81 | call print_newline
82 | call print_str_hl
83 | call print_newline
84 | +
85 | ld a,(result)
86 | cp 1 ; if a = 0 then a = 1
87 | adc 0
88 | jp exit
89 |
90 |
91 | ; Prints checksum as 8-character hex value
92 | ; Preserved: AF, BC, DE, HL
93 | print_crc:
94 | push af
95 |
96 | ; Must read checksum entirely before printing,
97 | ; since printing updates it.
98 | lda checksum
99 | cpl
100 | push af
101 |
102 | lda checksum+1
103 | cpl
104 | push af
105 |
106 | lda checksum+2
107 | cpl
108 | push af
109 |
110 | lda checksum+3
111 | cpl
112 |
113 | call print_hex
114 | pop af
115 | call print_hex
116 | pop af
117 | call print_hex
118 | pop af
119 | call print_a
120 |
121 | pop af
122 | ret
123 |
124 |
125 | ; If checksum doesn't match expected, reports failed test.
126 | ; Passing 0 just prints checksum. Clears checksum afterwards.
127 | .macro check_crc ARGS crc
128 | .if crc == 0
129 | call show_printing
130 | call print_newline
131 | call print_crc
132 | .else
133 | ld bc,(crc >> 16) ~ $FFFF
134 | ld de,(crc & $FFFF) ~ $FFFF
135 | call check_crc_
136 | .endif
137 | .endm
138 |
139 | check_crc_:
140 | lda checksum+0
141 | cp e
142 | jr nz,+
143 |
144 | lda checksum+1
145 | cp d
146 | jr nz,+
147 |
148 | lda checksum+2
149 | cp c
150 | jr nz,+
151 |
152 | lda checksum+3
153 | cp b
154 | jr nz,+
155 |
156 | jp reset_crc
157 |
158 | + call print_crc
159 | jp test_failed
160 |
161 |
162 | ; Updates checksum with bytes from addr to addr+size-1
163 | .macro checksum_mem ARGS addr,size
164 | ld hl,addr
165 | ld bc,size
166 | call checksum_mem_
167 | .endm
168 |
169 | checksum_mem_:
170 | - ldi a,(hl)
171 | call update_crc
172 | dec bc
173 | ld a,b
174 | or c
175 | jr nz,-
176 | ret
177 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/common/timer.s:
--------------------------------------------------------------------------------
1 | ; Cycle-accurate timer
2 |
3 | ; TIMA is incremented every 4 cycles. Loops
4 | ; check for increment within 3-cycle window,
5 | ; so when it occurs outside this, loop is
6 | ; exactly synchronized. Loop iterations are
7 | ; one more or less than 12 cycles, so they
8 | ; will never run more than 4 times.
9 |
10 | ; Initializes timer
11 | ; Preserved: AF, BC, DE, HL
12 | init_timer:
13 | push af
14 | di
15 | lda IE ; disable timer interrupt
16 | and ~$04
17 | sta IE
18 | wreg TMA,0 ; max period
19 | wreg TAC,$05 ; 262144 Hz
20 |
21 | ; Be sure timer doesn't expire
22 | ; immediately or take too long
23 | wreg IF,0
24 | wreg TIMA,-20
25 | delay 70
26 | lda IF
27 | and $04
28 | jp nz,test_failed
29 | lda IF
30 | and $04
31 | jp z,test_failed
32 |
33 | pop af
34 | ret
35 |
36 |
37 | ; Starts timer
38 | ; Preserved: AF, BC, DE, HL
39 | start_timer:
40 | push af
41 |
42 | - xor a ; 1
43 | sta TIMA ; 3
44 | lda TIMA ; 3
45 | or a ; 1
46 | jr nz,- ; 3
47 |
48 | pop af
49 | ret
50 |
51 |
52 | ; Stops timer and determines cycles since
53 | ; it was started. A = cycles (0 to 255).
54 | ; Preserved: BC, DE, HL
55 | stop_timer:
56 | push de
57 | call stop_timer_word
58 | ld a,e
59 | sub 10
60 | pop de
61 | ret
62 |
63 |
64 | ; Same as stop_timer, but with greater range.
65 | ; DE = cycles (0 to 1019).
66 | ; Preserved: BC, HL
67 | stop_timer_word:
68 |
69 | ld d,0
70 |
71 | ; Get main count (TIMA*4)
72 | lda TIMA
73 | sub 5
74 | add a
75 | rl d
76 | add a
77 | rl d
78 | ld e,a
79 |
80 | ; One iteration per remaining cycle
81 | - xor a ; 1
82 | sta TIMA ; 3
83 | lda TIMA ; 3
84 | dec de ; 2
85 | or a ; 1
86 | jr nz,- ; 3
87 |
88 | ret
89 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/linkfile:
--------------------------------------------------------------------------------
1 | [objects]
2 | test.o
3 |
--------------------------------------------------------------------------------
/roms/instr_timing/source/shell.inc:
--------------------------------------------------------------------------------
1 | .incdir "common"
2 |
3 | ; GBS music file
4 | .ifdef BUILD_GBS
5 | .include "build_gbs.s"
6 | .endif
7 |
8 | ; Devcart
9 | .ifdef BUILD_DEVCART
10 | .include "build_devcart.s"
11 | .endif
12 |
13 | ; Sub-test in a multi-test ROM
14 | .ifdef BUILD_MULTI
15 | .include "build_multi.s"
16 | .endif
17 |
18 | ; GB ROM (default)
19 | .ifndef RUNTIME_INCLUDED
20 | .include "build_rom.s"
21 | .endif
22 |
--------------------------------------------------------------------------------
/roms/interrupt_time/interrupt_time.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/interrupt_time/interrupt_time.gb
--------------------------------------------------------------------------------
/roms/interrupt_time/interrupt_time.s:
--------------------------------------------------------------------------------
1 | ; Tests interrupt handling time for slow and fast
2 | ; CPU. First value is CPU speed (0=slow, 1=fast),
3 | ; second is number of cycles taken by interrupt.
4 | ; Should take 13 cycles at either speed.
5 | .define REQUIRE_CGB 1
6 |
7 | .include "shell.inc"
8 | .include "cpu_speed.s"
9 | .include "timer.s"
10 | .include "apu.s"
11 |
12 | ; $58: JP $DEC3
13 | ; $DEC3: RET
14 | .define sint $DEC3
15 |
16 | main:
17 | call init_timer
18 |
19 | ld d,0
20 | call test_interrupt
21 | ld d,8
22 | call test_interrupt
23 | call cpu_fast
24 | ld d,0
25 | call test_interrupt
26 | ld d,8
27 | call test_interrupt
28 |
29 | check_crc $C86CC74D
30 | jp tests_passed
31 |
32 | test_interrupt:
33 | call get_cpu_speed
34 | call print_a
35 | call print_d
36 |
37 | ld a,$C9 ; RET
38 | ld (sint),a
39 |
40 | wreg IE,$08
41 | wreg IF,$00
42 | call start_timer
43 | ei
44 | ld a,d
45 | ld (IF),a ; $00 = 0 clocks, $08 = 13 clocks
46 | di
47 | call stop_timer
48 | sub 3+4 ; instruction overhead
49 | call print_a
50 | call print_newline
51 |
52 | ret
53 |
54 | ; RST handler that matches the one in my devcart
55 | .bank 0 slot 0
56 | .org $58
57 | jp $DEC3
58 |
--------------------------------------------------------------------------------
/roms/mbc1/bits_bank1.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/bits_bank1.gb
--------------------------------------------------------------------------------
/roms/mbc1/bits_bank2.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/bits_bank2.gb
--------------------------------------------------------------------------------
/roms/mbc1/bits_mode.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/bits_mode.gb
--------------------------------------------------------------------------------
/roms/mbc1/bits_ramg.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/bits_ramg.gb
--------------------------------------------------------------------------------
/roms/mbc1/multicart_rom_8Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/multicart_rom_8Mb.gb
--------------------------------------------------------------------------------
/roms/mbc1/ram_256kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/ram_256kb.gb
--------------------------------------------------------------------------------
/roms/mbc1/ram_64kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/ram_64kb.gb
--------------------------------------------------------------------------------
/roms/mbc1/rom_16Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/rom_16Mb.gb
--------------------------------------------------------------------------------
/roms/mbc1/rom_1Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/rom_1Mb.gb
--------------------------------------------------------------------------------
/roms/mbc1/rom_2Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/rom_2Mb.gb
--------------------------------------------------------------------------------
/roms/mbc1/rom_4Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/rom_4Mb.gb
--------------------------------------------------------------------------------
/roms/mbc1/rom_512kb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/rom_512kb.gb
--------------------------------------------------------------------------------
/roms/mbc1/rom_8Mb.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mbc1/rom_8Mb.gb
--------------------------------------------------------------------------------
/roms/mem_timing/individual/01-read_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mem_timing/individual/01-read_timing.gb
--------------------------------------------------------------------------------
/roms/mem_timing/individual/02-write_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mem_timing/individual/02-write_timing.gb
--------------------------------------------------------------------------------
/roms/mem_timing/individual/03-modify_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mem_timing/individual/03-modify_timing.gb
--------------------------------------------------------------------------------
/roms/mem_timing/mem_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mem_timing/mem_timing.gb
--------------------------------------------------------------------------------
/roms/mem_timing/readme.txt:
--------------------------------------------------------------------------------
1 | Game Boy CPU Memory Access Timing Test
2 | --------------------------------------
3 | This ROM tests the timing of memory reads and writes made by
4 | instructions, except stack and program counter accesses. These tests
5 | require correct instruction timing and proper timer operation (TAC,
6 | TIMA, TMA).
7 |
8 | The read and write tests list failing instructions as
9 |
10 | [CB] opcode:tested-correct
11 |
12 | The read-modify-write test lists failing instructions as
13 |
14 | [CB] opcode:tested read/tested write-correct read/correct write
15 |
16 | The values after the opcode refer to which instruction cycle the access
17 | occurs on, with 1 being the first. If a time couldn't be determined due
18 | to some other problem, it prints 0.
19 |
20 | For instructions which either read or write, but not both, the CPU makes
21 | the access on the last cycle. For instructions which read, modify, then
22 | write back, the CPU reads on the next-to-last cycle, and writes on the
23 | last cycle.
24 |
25 |
26 | Internal operation
27 | ------------------
28 | The tests have the timer increment TIMA every 64 cycles, synchronize
29 | with this, delay a variable amount, then have the instruction under test
30 | access the timer. By varying the delay in one-cycle increments, the
31 | memory access made by the instruction can be made to fall before and
32 | after a TIMA increment. By then examining the registers and value in
33 | TIMA, it can be determined which occurred.
34 |
35 |
36 | Multi-ROM
37 | ---------
38 | In the main directory is a single ROM which runs all the tests. It
39 | prints a test's number, runs the test, then "ok" if it passes, otherwise
40 | a failure code. Once all tests have completed it either reports that all
41 | tests passed, or prints the number of failed tests. Finally, it makes
42 | several beeps. If a test fails, it can be run on its own by finding the
43 | corresponding ROM in individual/.
44 |
45 | Ths compact format on screen is to avoid having the results scroll off
46 | the top, so the test can be started and allowed to run without having to
47 | constantly monitor the display.
48 |
49 | Currently there is no well-defined way for an emulator test rig to
50 | programatically find the result of the test; contact me if you're trying
51 | to do completely automated testing of your emulator. One simple approach
52 | is to take a screenshot after all tests have run, or even just a
53 | checksum of one, and compare this with a previous run.
54 |
55 |
56 | Failure codes
57 | -------------
58 | Failed tests may print a failure code, and also short description of the
59 | problem. For more information about a failure code, look in the
60 | corresponding source file in source/; the point in the code where
61 | "set_test n" occurs is where that failure code will be generated.
62 | Failure code 1 is a general failure of the test; any further information
63 | will be printed.
64 |
65 | Note that once a sub-test fails, no further tests for that file are run.
66 |
67 |
68 | Console output
69 | --------------
70 | Information is printed on screen in a way that needs only minimum LCD
71 | support, and won't hang if LCD output isn't supported at all.
72 | Specifically, while polling LY to wait for vblank, it will time out if
73 | it takes too long, so LY always reading back as the same value won't
74 | hang the test. It's also OK if scrolling isn't supported; in this case,
75 | text will appear starting at the top of the screen.
76 |
77 | Everything printed on screen is also sent to the game link port by
78 | writing the character to SB, then writing $81 to SC. This is useful for
79 | tests which print lots of information that scrolls off screen.
80 |
81 |
82 | Source code
83 | -----------
84 | Source code is included for all tests, in source/. It can be used to
85 | build the individual test ROMs. Code for the multi test isn't included
86 | due to the complexity of putting everything together.
87 |
88 | Code is written for the wla-dx assembler. To assemble a particular test,
89 | execute
90 |
91 | wla -o "source_filename.s" test.o
92 | wlalink linkfile test.gb
93 |
94 | Test code uses a common shell framework contained in common/.
95 |
96 |
97 | Internal framework operation
98 | ----------------------------
99 | Tests use a common framework for setting things up, reporting results,
100 | and ending. All files first include "shell.inc", which sets up the ROM
101 | header and shell code, and includes other commonly-used modules.
102 |
103 | One oddity is that test code is first copied to internal RAM at $D000,
104 | then executed there. This allows self-modification, and ensures the code
105 | is executed the same way it is on my devcart, which doesn't have a
106 | rewritable ROM as most do.
107 |
108 | Some macros are used to simplify common tasks:
109 |
110 | Macro Behavior
111 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
112 | wreg addr,data Writes data to addr using LDH
113 | lda addr Loads byte from addr into A using LDH
114 | sta addr Stores A at addr using LDH
115 | delay n Delays n cycles, where NOP = 1 cycle
116 | delay_msec n Delays n milliseconds
117 | set_test n,"Cause" Sets failure code and optional string
118 |
119 | Routines and macros are documented where they are defined.
120 |
121 | --
122 | Shay Green
123 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/01-read_timing.s:
--------------------------------------------------------------------------------
1 | ; Tests timing of accesses made by
2 | ; memory read instructions
3 |
4 | .include "shell.inc"
5 | .include "tima_64.s"
6 |
7 | instructions:
8 | ; last value is time of read
9 | .byte $B6,$00,$00,2 ; OR (HL)
10 | .byte $BE,$00,$00,2 ; CP (HL)
11 | .byte $86,$00,$00,2 ; ADD (HL)
12 | .byte $8E,$00,$00,2 ; ADC (HL)
13 | .byte $96,$00,$00,2 ; SUB (HL)
14 | .byte $9E,$00,$00,2 ; SBC (HL)
15 | .byte $A6,$00,$00,2 ; AND (HL)
16 | .byte $AE,$00,$00,2 ; XOR (HL)
17 | .byte $46,$00,$00,2 ; LD B,(HL)
18 | .byte $4E,$00,$00,2 ; LD C,(HL)
19 | .byte $56,$00,$00,2 ; LD D,(HL)
20 | .byte $5E,$00,$00,2 ; LD E,(HL)
21 | .byte $66,$00,$00,2 ; LD H,(HL)
22 | .byte $6E,$00,$00,2 ; LD L,(HL)
23 | .byte $7E,$00,$00,2 ; LD A,(HL)
24 | .byte $F2,$00,$00,2 ; LDH A,(C)
25 | .byte $0A,$00,$00,2 ; LD A,(BC)
26 | .byte $1A,$00,$00,2 ; LD A,(DE)
27 | .byte $2A,$00,$00,2 ; LD A,(HL+)
28 | .byte $3A,$00,$00,2 ; LD A,(HL-)
29 | .byte $F0,tima_64,4 ; LD A,($0000)
31 |
32 | .byte $CB,$46,$00,3 ; BIT 0,(HL)
33 | .byte $CB,$4E,$00,3 ; BIT 1,(HL)
34 | .byte $CB,$56,$00,3 ; BIT 2,(HL)
35 | .byte $CB,$5E,$00,3 ; BIT 3,(HL)
36 | .byte $CB,$66,$00,3 ; BIT 4,(HL)
37 | .byte $CB,$6E,$00,3 ; BIT 5,(HL)
38 | .byte $CB,$76,$00,3 ; BIT 6,(HL)
39 | .byte $CB,$7E,$00,3 ; BIT 7,(HL)
40 | instructions_end:
41 |
42 | main:
43 | call init_tima_64
44 | set_test 0
45 |
46 | ; Test instructions
47 | ld hl,instructions
48 | - call @time_instr
49 | cp (hl)
50 | call nz,@print_failed
51 | inc hl
52 | ld a,l
53 | cp 3-byte instruction
84 | ; HL <- HL + 3
85 | @time_instr: ;
86 | ; Copy instr ; PC = 0xc353
87 | ld a,(hl+)
88 | ld (instr+0),a
89 | ld a,(hl+)
90 | ld (instr+1),a
91 | ld a,(hl+)
92 | ld (instr+2),a
93 | push hl ; PC = 0xc3a9
94 |
95 | ; Find result when access doesn't occur
96 | ld b,0
97 | call @time_access
98 | ld c,a
99 |
100 | ; Test for accesses on each cycle
101 | ld b,0
102 | - push bc
103 | call @time_access
104 | pop bc
105 | cp c
106 | jr nz,@found
107 | inc b
108 | ld a,b
109 | cp 10
110 | jr nz,-
111 | ld b,0
112 |
113 | @found:
114 | ld a,b
115 | pop hl
116 | ret
117 |
118 | ; Tests for read
119 | ; B -> which cycle to test
120 | ; A <- timer value after test
121 | @time_access:
122 | call sync_tima_64
123 | ld a,9
124 | sub b
125 | call delay_a_20_cycles
126 | xor a ; clear flags
127 | ld hl,tima_64
128 | ld (hl),$7F
129 | ld bc,tima_64
130 | ld de,tima_64
131 | ld a,$7F
132 | instr:
133 | nop
134 | nop
135 | nop
136 |
137 | ; Add all registers together to yield
138 | ; unique value that differs based on
139 | ; read occurring before or after tima_64
140 | ; increments.
141 | push af
142 | add hl,bc
143 | add hl,de
144 | pop de
145 | add hl,de
146 | ld a,h
147 | add l
148 | ret
149 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/02-write_timing.s:
--------------------------------------------------------------------------------
1 | ; Tests timing of accesses made by
2 | ; memory write instructions
3 |
4 | .include "shell.inc"
5 | .include "tima_64.s"
6 |
7 | instructions:
8 | ; last value is time of write
9 | .byte $36,$FF,$00,3 ; LD (HL),n
10 | .byte $70,$00,$00,2 ; LD (HL),B
11 | .byte $71,$00,$00,2 ; LD (HL),C
12 | .byte $72,$00,$00,2 ; LD (HL),D
13 | .byte $73,$00,$00,2 ; LD (HL),E
14 | .byte $74,$00,$00,2 ; LD (HL),H
15 | .byte $75,$00,$00,2 ; LD (HL),L
16 | .byte $77,$00,$00,2 ; LD (HL),A
17 | .byte $02,$00,$00,2 ; LD (BC),A
18 | .byte $12,$00,$00,2 ; LD (DE),A
19 | .byte $22,$00,$00,2 ; LD (HL+),A
20 | .byte $32,$00,$00,2 ; LD (HL-),A
21 | .byte $E2,$00,$00,2 ; LDH (C),A
22 | .byte $E0,tima_64,4 ; LD (nn),A
24 | instructions_end:
25 |
26 | main:
27 | call init_tima_64
28 | set_test 0
29 |
30 | ; Test instructions
31 | ld hl,instructions
32 | - call @test_instr
33 | cp (hl)
34 | call nz,@print_failed
35 | inc hl
36 | ld a,l
37 | cp 3-byte instruction
64 | ; HL <- HL + 3
65 | @test_instr:
66 | ; Copy instr
67 | ld a,(hl+)
68 | ld (instr+0),a
69 | ld a,(hl+)
70 | ld (instr+1),a
71 | ld a,(hl+)
72 | ld (instr+2),a
73 | push hl
74 |
75 | ; Test for writes on each cycle
76 | ld b,0
77 | - push bc
78 | call @time_write
79 | pop bc
80 | cp tima_64
83 | jr z,@no_write
84 | jr @found
85 | @no_write:
86 | inc b
87 | ld a,b
88 | cp 10
89 | jr nz,-
90 | ld b,0
91 |
92 | @found:
93 | ld a,b
94 | pop hl
95 | ret
96 |
97 | ; Tests for write
98 | ; B -> which cycle to test
99 | ; A <- timer value after test
100 | @time_write:
101 | call sync_tima_64
102 | ld a,13
103 | sub b
104 | call delay_a_20_cycles
105 | ld hl,tima_64
106 | ld bc,tima_64
107 | ld de,tima_64
108 | ld a, 3-byte instruction
93 | ; HL <- HL + 3
94 | @time_instr:
95 | ; Copy instr
96 | ld a,(hl+)
97 | ld (instr+0),a
98 | ld a,(hl+)
99 | ld (instr+1),a
100 | ld a,(hl+)
101 | ld (instr+2),a
102 | push hl
103 |
104 | ; Find result when access doesn't occur
105 | ld b,0
106 | call @time_access
107 |
108 | ; Find first access
109 | call @find_next_access
110 | ld d,b
111 |
112 | ; Find second access
113 | call @find_next_access
114 | ld e,b
115 |
116 | pop hl
117 | ret
118 |
119 | ; A -> current timer result
120 | ; B -> starting clock
121 | ; B <- clock next access occurs on
122 | ; A <- new timer result
123 | @find_next_access:
124 | ld c,a
125 | - call @time_access
126 | cp c
127 | ret nz
128 | inc b
129 | ld a,b
130 | cp 10
131 | jr c,-
132 |
133 | ; Couldn't find time, so return 0/0
134 | ld a,c
135 | ld b,0
136 | ld d,b
137 | ret
138 |
139 | ; Tests for access
140 | ; B -> which cycle to test
141 | ; A <- timer value after test
142 | @time_access:
143 | call sync_tima_64
144 | ld hl,tima_64
145 | ld (hl),$7F
146 | ld a,17
147 | sub b
148 | call delay_a_20_cycles
149 | xor a ; clear flags
150 | instr:
151 | nop
152 | nop
153 | nop
154 | delay 32
155 | ld a,(tima_64)
156 | ret
157 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/build_gbs.s:
--------------------------------------------------------------------------------
1 | ; Build as GBS music file
2 |
3 | .memoryMap
4 | defaultSlot 0
5 | slot 0 $3000 size $1000
6 | slot 1 $C000 size $1000
7 | .endMe
8 |
9 | .romBankSize $1000
10 | .romBanks 2
11 |
12 |
13 | ;;;; GBS music file header
14 |
15 | .byte "GBS"
16 | .byte 1 ; vers
17 | .byte 1 ; songs
18 | .byte 1 ; first song
19 | .word load_addr
20 | .word reset
21 | .word gbs_play
22 | .word std_stack
23 | .byte 0,0 ; timer
24 | .ds $60,0
25 | load_addr:
26 |
27 | ; WLA assumes we're building ROM and messes
28 | ; with bytes at the beginning, so skip them.
29 | .ds $100,0
30 |
31 |
32 | ;;;; Shell
33 |
34 | .include "runtime.s"
35 |
36 | init_runtime:
37 | ld a,$01 ; Identify as DMG hardware
38 | ld (gb_id),a
39 | .ifdef TEST_NAME
40 | print_str TEST_NAME,newline,newline
41 | .endif
42 | ret
43 |
44 | std_print:
45 | sta SB
46 | wreg SC,$81
47 | delay 2304
48 | ret
49 |
50 | post_exit:
51 | call play_byte
52 | forever:
53 | wreg NR52,0 ; sound off
54 | - jp -
55 |
56 | .ifndef CUSTOM_RESET
57 | gbs_play:
58 | .endif
59 | console_flush:
60 | console_normal:
61 | console_inverse:
62 | console_set_mode:
63 | ret
64 |
65 | ; Reports A in binary as high and low tones, with
66 | ; leading low tone for reference. Omits leading
67 | ; zeroes.
68 | ; Preserved: AF, BC, DE, HL
69 | play_byte:
70 | push af
71 | push hl
72 |
73 | ; HL = (A << 1) | 1
74 | scf
75 | rla
76 | ld l,a
77 | ld h,0
78 | rl h
79 |
80 | ; Shift left until next-to-top bit is 1
81 | - add hl,hl
82 | bit 6,h
83 | jr z,-
84 |
85 | ; Reset sound
86 | delay_msec 400
87 | wreg NR52,0 ; sound off
88 | wreg NR52,$80 ; sound on
89 | wreg NR51,$FF ; mono
90 | wreg NR50,$77 ; volume
91 |
92 | - add hl,hl
93 |
94 | ; Low or high pitch based on bit shifted out
95 | ; of HL
96 | ld a,0
97 | jr nc,+
98 | ld a,$FF
99 | + sta NR23
100 |
101 | ; Play short tone
102 | wreg NR21,$A0
103 | wreg NR22,$F0
104 | wreg NR24,$86
105 | delay_msec 75
106 | wreg NR22,0
107 | wreg NR23,$F8
108 | wreg NR24,$87
109 | delay_msec 200
110 |
111 | ; Loop until HL = $8000
112 | ld a,h
113 | xor $80
114 | or l
115 | jr nz,-
116 |
117 | pop hl
118 | pop af
119 | ret
120 |
121 | .ends
122 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/build_rom.s:
--------------------------------------------------------------------------------
1 | ; Build as GB ROM
2 |
3 | .memoryMap
4 | defaultSlot 0
5 | slot 0 $0000 size $4000
6 | slot 1 $C000 size $4000
7 | .endMe
8 |
9 | .romBankSize $4000 ; generates $8000 byte ROM
10 | .romBanks 2
11 |
12 | .cartridgeType 1 ; MBC1
13 | .computeChecksum
14 | .computeComplementCheck
15 |
16 |
17 | ;;;; GB ROM header
18 |
19 | ; GB header read by bootrom
20 | .org $100
21 | nop
22 | jp reset
23 |
24 | ; Nintendo logo required for proper boot
25 | .byte $CE,$ED,$66,$66,$CC,$0D,$00,$0B
26 | .byte $03,$73,$00,$83,$00,$0C,$00,$0D
27 | .byte $00,$08,$11,$1F,$88,$89,$00,$0E
28 | .byte $DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
29 | .byte $BB,$BB,$67,$63,$6E,$0E,$EC,$CC
30 | .byte $DD,$DC,$99,$9F,$BB,$B9,$33,$3E
31 |
32 | ; Internal name
33 | .ifdef ROM_NAME
34 | .byte ROM_NAME
35 | .endif
36 |
37 | ; CGB/DMG requirements
38 | .org $143
39 | .ifdef REQUIRE_CGB
40 | .byte $C0
41 | .else
42 | .ifndef REQUIRE_DMG
43 | .byte $80
44 | .endif
45 | .endif
46 |
47 | .org $200
48 |
49 |
50 | ;;;; Shell
51 |
52 | .include "runtime.s"
53 | .include "console.s"
54 |
55 | init_runtime:
56 | call console_init
57 | .ifdef TEST_NAME
58 | print_str TEST_NAME,newline,newline
59 | .endif
60 | ret
61 |
62 | std_print:
63 | push af
64 | sta SB
65 | wreg SC,$81
66 | delay 2304
67 | pop af
68 | jp console_print
69 |
70 | post_exit:
71 | call console_show
72 | call play_byte
73 | forever:
74 | wreg NR52,0 ; sound off
75 | - jr -
76 |
77 | play_byte:
78 | ret
79 |
80 | .ends
81 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/console.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/mem_timing/source/common/console.bin
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/console.s:
--------------------------------------------------------------------------------
1 | ; Scrolling text console
2 |
3 | ; Console is 20x18 characters. Buffers lines, so
4 | ; output doesn't appear until a newline or flush.
5 | ; If scrolling isn't supported (i.e. SCY is treated
6 | ; as if always zero), the first 18 lines will
7 | ; still print properly). Also works properly if
8 | ; LY isn't supported (always reads back as the same
9 | ; value).
10 |
11 | .define console_width 20
12 |
13 | .define console_buf bss+0
14 | .define console_pos bss+console_width
15 | .define console_mode bss+console_width+1
16 | .define console_scroll bss+console_width+2
17 | .redefine bss bss+console_width+3
18 |
19 |
20 | ; Waits for start of LCD blanking period
21 | ; Preserved: BC, DE, HL
22 | console_wait_vbl:
23 | push bc
24 |
25 | ; Wait for start of vblank, with
26 | ; timeout in case LY doesn't work
27 | ; or LCD is disabled.
28 | ld bc,-1250
29 | - inc bc
30 | ld a,b
31 | or c
32 | jr z,@timeout
33 | lda LY
34 | cp 144
35 | jr nz,-
36 | @timeout:
37 |
38 | pop bc
39 | ret
40 |
41 |
42 | ; Initializes text console
43 | console_init:
44 | call console_hide
45 |
46 | ; CGB-specific inits
47 | ld a,(gb_id)
48 | and gb_id_cgb
49 | call nz,@init_cgb
50 |
51 | ; Clear nametable
52 | ld a,' '
53 | call @fill_nametable
54 |
55 | ; Load tiles
56 | ld hl,TILES+$200
57 | ld c,0
58 | call @load_tiles
59 | ld hl,TILES+$A00
60 | ld c,$FF
61 | call @load_tiles
62 |
63 | ; Init state
64 | ld a,console_width
65 | ld (console_pos),a
66 | ld a,0
67 | ld (console_mode),a
68 | ld a,-8
69 | ld (console_scroll),a
70 | call console_scroll_up_
71 | jr console_show
72 |
73 | @fill_nametable:
74 | ld hl,BGMAP0
75 | ld b,4
76 | - ld (hl),a
77 | inc l
78 | jr nz,-
79 | inc h
80 | dec b
81 | jr nz,-
82 | ret
83 |
84 | @init_cgb:
85 | ; Clear palette
86 | wreg $FF68,$80
87 | ld b,16
88 | - wreg $FF69,$FF
89 | wreg $FF69,$7F
90 | wreg $FF69,$00
91 | wreg $FF69,$00
92 | wreg $FF69,$00
93 | wreg $FF69,$00
94 | wreg $FF69,$00
95 | wreg $FF69,$00
96 | dec b
97 | jr nz,-
98 |
99 | ; Clear attributes
100 | ld a,1
101 | ld (VBK),a
102 | ld a,0
103 | call @fill_nametable
104 |
105 | ld a,0
106 | ld (VBK),a
107 | ret
108 |
109 | @load_tiles:
110 | ld de,ASCII
111 | ld b,96
112 | -- push bc
113 | ld b,8
114 | - ld a,(de)
115 | inc de
116 | xor c
117 | ldi (hl),a
118 | ldi (hl),a
119 | dec b
120 | jr nz,-
121 | pop bc
122 | dec b
123 | jr nz,--
124 | ret
125 |
126 |
127 | ; Shows console display
128 | ; Preserved: AF, BC, DE, HL
129 | console_show:
130 | push af
131 |
132 | ; Enable LCD
133 | call console_wait_vbl
134 | wreg LCDC,$91
135 | wreg SCX,0
136 | wreg BGP,$E4
137 |
138 | jp console_apply_scroll_
139 |
140 |
141 | ; Hides console display by turning LCD off
142 | ; Preserved: AF, BC, DE, HL
143 | console_hide:
144 | push af
145 |
146 | ; LCD off
147 | call console_wait_vbl
148 | wreg LCDC,$11
149 |
150 | pop af
151 | ret
152 |
153 |
154 | ; Changes to normal text mode
155 | ; Preserved: BC, DE, HL
156 | console_normal:
157 | xor a
158 | jr console_set_mode
159 |
160 | ; Changes to inverse text mode
161 | ; Preserved: BC, DE, HL
162 | console_inverse:
163 | ld a,$80
164 |
165 | ; Changes console mode to A.
166 | ; 0: Normal, $80: Inverse
167 | ; Preserved: BC, DE, HL
168 | console_set_mode:
169 | and $80
170 | ld (console_mode),a
171 | ret
172 |
173 |
174 | ; Prints char A to console. Will not appear until
175 | ; a newline or flush occurs.
176 | ; Preserved: AF, BC, DE, HL
177 | console_print:
178 | push af
179 |
180 | cp 10
181 | jr z,console_newline_
182 |
183 | push hl
184 | push af
185 | ld hl,console_pos
186 | ldi a,(hl)
187 | cp BGMAP0) >> 2
273 | add hl,hl
274 | add hl,hl
275 |
276 | ; Copy line
277 | ld de,console_buf + console_width
278 | - dec e
279 | ld a,(de)
280 | ldi (hl),a
281 | ld a,e
282 | cp checksum
67 | ldi (hl),a
68 | ld (hl),d
69 | inc l
70 | ld (hl),c
71 | inc l
72 | ld (hl),b
73 |
74 | pop hl
75 | pop de
76 | pop bc
77 | pop af
78 | ret
79 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/gb.inc:
--------------------------------------------------------------------------------
1 | ; Game Boy hardware addresses
2 |
3 | ; Memory
4 | .define VRAM $8000 ; video memory
5 | .define TILES $8000 ; tile images
6 | .define BGMAP0 $9800 ; first 32x32 tilemap
7 | .define BGMAP1 $9C00 ; second 32x32 tilemap
8 | .define WRAM $C000 ; internal memory
9 | .define OAM $FE00 ; sprite memory
10 | .define HRAM $FF80 ; fast memory for LDH
11 |
12 | .define P1 $FF00
13 |
14 | ; Game link I/O
15 | .define SB $FF01
16 | .define SC $FF02
17 |
18 | ; Interrupts
19 | .define DIV $FF04
20 | .define TIMA $FF05
21 | .define TMA $FF06
22 | .define TAC $FF07
23 | .define IF $FF0F
24 | .define IE $FFFF
25 |
26 | ; LCD registers
27 | .define LCDC $FF40 ; control
28 | .define STAT $FF41 ; status
29 | .define SCY $FF42 ; scroll Y
30 | .define SCX $FF43 ; scroll X
31 | .define LY $FF44 ; current Y being rendered
32 | .define BGP $FF47
33 |
34 | .define KEY1 $FF4D ; for changing CPU speed
35 | .define VBK $FF4F
36 |
37 | ; Sound registers
38 | .define NR10 $FF10
39 | .define NR11 $FF11
40 | .define NR12 $FF12
41 | .define NR13 $FF13
42 | .define NR14 $FF14
43 |
44 | .define NR21 $FF16
45 | .define NR22 $FF17
46 | .define NR23 $FF18
47 | .define NR24 $FF19
48 |
49 | .define NR30 $FF1A
50 | .define NR31 $FF1B
51 | .define NR32 $FF1C
52 | .define NR33 $FF1D
53 | .define NR34 $FF1E
54 |
55 | .define NR41 $FF20
56 | .define NR42 $FF21
57 | .define NR43 $FF22
58 | .define NR44 $FF23
59 |
60 | .define NR50 $FF24
61 | .define NR51 $FF25
62 | .define NR52 $FF26
63 |
64 | .define WAVE $FF30
65 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/macros.inc:
--------------------------------------------------------------------------------
1 | ; General macros
2 |
3 | ; Reads A from addr, from $FF00 to $FFFF
4 | ; Preserved: F, BC, DE, HL
5 | ; Time: 3 cycles
6 | .macro lda ARGS addr
7 | ldh a,(addr - $FF00)
8 | .endm
9 |
10 | ; Writes A to addr, from $FF00 to $FFFF
11 | ; Preserved: AF, BC, DE, HL
12 | ; Time: 3 cycles
13 | .macro sta ARGS addr
14 | ldh (addr - $FF00),a
15 | .endm
16 |
17 | ; Writes immediate data to addr, from $FF00 to $FFFF
18 | ; Preserved: F, BC, DE, HL
19 | ; Time: 5 cycles
20 | .macro wreg ARGS addr, data
21 | ld a,data
22 | sta addr
23 | .endm
24 |
25 | ; Calls routine multiple times, with A having the
26 | ; value 'start' the first time, 'start+step' the
27 | ; second time, up to 'end' for the last time.
28 | ; Preserved: BC, DE, HL
29 | .macro for_loop ; routine,start,end,step
30 | ld a,\2
31 |
32 | for_loop\@:
33 | push af
34 | call \1
35 | pop af
36 |
37 | add \4
38 | cp <(\3 + \4)
39 | jr nz,for_loop\@
40 | .endm
41 |
42 | ; Calls routine n times. The value of A in the routine
43 | ; counts from 0 to n-1.
44 | ; Preserved: BC, DE, HL
45 | .macro loop_n_times ; routine,n
46 | for_loop \1,0,\2 - 1,+1
47 | .endm
48 |
49 | ; Same as for_loop, but counts with 16-bit value in BC.
50 | ; Preserved: DE, HL
51 | .macro for_loop16 ; routine,start,end,step
52 | ld bc,\2
53 |
54 | for_loop16\@:
55 | push bc
56 | call \1
57 | pop bc
58 |
59 | ld a,c
60 | add <\4
61 | ld c,a
62 |
63 | ld a,b
64 | adc >\4
65 | ld b,a
66 |
67 | cp >(\3+\4)
68 | jr nz,for_loop16\@
69 |
70 | ld a,c
71 | cp <(\3+\4)
72 | jr nz,for_loop16\@
73 | .endm
74 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/numbers.s:
--------------------------------------------------------------------------------
1 | ; Printing of numeric values
2 |
3 | ; Prints value of indicated register/pair
4 | ; as 2/4 hex digits, followed by a space.
5 | ; Updates checksum with printed values.
6 | ; Preserved: AF, BC, DE, HL
7 |
8 | print_regs:
9 | call print_af
10 | call print_bc
11 | call print_de
12 | call print_hl
13 | call print_newline
14 | ret
15 |
16 | print_a:
17 | push af
18 | print_a_:
19 | call print_hex
20 | ld a,' '
21 | call print_char_nocrc
22 | pop af
23 | ret
24 |
25 | print_af:
26 | push af
27 | call print_hex
28 | pop af
29 | print_f:
30 | push bc
31 | push af
32 | pop bc
33 | call print_c
34 | pop bc
35 | ret
36 |
37 | print_b:
38 | push af
39 | ld a,b
40 | jr print_a_
41 |
42 | print_c:
43 | push af
44 | ld a,c
45 | jr print_a_
46 |
47 | print_d:
48 | push af
49 | ld a,d
50 | jr print_a_
51 |
52 | print_e:
53 | push af
54 | ld a,e
55 | jr print_a_
56 |
57 | print_h:
58 | push af
59 | ld a,h
60 | jr print_a_
61 |
62 | print_l:
63 | push af
64 | ld a,l
65 | jr print_a_
66 |
67 | print_bc:
68 | push af
69 | push bc
70 | print_bc_:
71 | ld a,b
72 | call print_hex
73 | ld a,c
74 | pop bc
75 | jr print_a_
76 |
77 | print_de:
78 | push af
79 | push bc
80 | ld b,d
81 | ld c,e
82 | jr print_bc_
83 |
84 | print_hl:
85 | push af
86 | push bc
87 | ld b,h
88 | ld c,l
89 | jr print_bc_
90 |
91 |
92 | ; Prints A as two hex chars and updates checksum
93 | ; Preserved: BC, DE, HL
94 | print_hex:
95 | call update_crc
96 | print_hex_nocrc:
97 | push af
98 | swap a
99 | call +
100 | pop af
101 |
102 | + and $0F
103 | cp 10
104 | jr c,+
105 | add 7
106 | + add '0'
107 | jp print_char_nocrc
108 |
109 |
110 | ; Prints char_nz if Z flag is clear,
111 | ; char_z if Z flag is set.
112 | ; Preserved: AF, BC, DE, HL
113 | .macro print_nz ARGS char_nz, char_z
114 | push af
115 | ld a,char_nz
116 | jr nz,print_nz\@
117 | ld a,char_z
118 | print_nz\@:
119 | call print_char
120 | pop af
121 | .endm
122 |
123 |
124 | ; Prints char_nc if C flag is clear,
125 | ; char_c if C flag is set.
126 | ; Preserved: AF, BC, DE, HL
127 | .macro print_nc ARGS char_nc, char_c
128 | push af
129 | ld a,char_nc
130 | jr nz,print_nc\@
131 | ld a,char_c
132 | print_nc\@:
133 | call print_char
134 | pop af
135 | .endm
136 |
137 |
138 | ; Prints A as 2 decimal digits
139 | ; Preserved: AF, BC, DE, HL
140 | print_dec2:
141 | push af
142 | push bc
143 | jr +
144 |
145 |
146 | ; Prints A as 1-3 digit decimal value
147 | ; Preserved: AF, BC, DE, HL
148 | print_dec:
149 | push af
150 | push bc
151 |
152 | cp 10
153 | jr c,++
154 | ld c,100
155 | cp c
156 | call nc,@digit
157 | + ld c,10
158 | call @digit
159 | ++ add '0'
160 | call print_char
161 |
162 | pop bc
163 | pop af
164 | ret
165 |
166 | @digit:
167 | ld b,'0'-1
168 | - inc b
169 | sub c
170 | jr nc,-
171 | add c
172 |
173 | ld c,a
174 | ld a,b
175 | call print_char
176 | ld a,c
177 | ret
178 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/printing.s:
--------------------------------------------------------------------------------
1 | ; Main printing routine that checksums and
2 | ; prints to output device
3 |
4 | ; Character that does equivalent of print_newline
5 | .define newline 10
6 |
7 | ; Prints char without updating checksum
8 | ; Preserved: BC, DE, HL
9 | .define print_char_nocrc bss
10 | .redefine bss bss+3
11 |
12 |
13 | ; Initializes printing. HL = print routine
14 | init_printing:
15 | ld a,l
16 | ld (print_char_nocrc+1),a
17 | ld a,h
18 | ld (print_char_nocrc+2),a
19 | jr show_printing
20 |
21 |
22 | ; Hides/shows further printing
23 | ; Preserved: BC, DE, HL
24 | hide_printing:
25 | ld a,$C9 ; RET
26 | jr +
27 | show_printing:
28 | ld a,$C3 ; JP (nn)
29 | + ld (print_char_nocrc),a
30 | ret
31 |
32 |
33 | ; Prints character and updates checksum UNLESS
34 | ; it's a newline.
35 | ; Preserved: AF, BC, DE, HL
36 | print_char:
37 | push af
38 | cp newline
39 | call nz,update_crc
40 | call print_char_nocrc
41 | pop af
42 | ret
43 |
44 |
45 | ; Prints space. Does NOT update checksum.
46 | ; Preserved: AF, BC, DE, HL
47 | print_space:
48 | push af
49 | ld a,' '
50 | call print_char_nocrc
51 | pop af
52 | ret
53 |
54 |
55 | ; Advances to next line. Does NOT update checksum.
56 | ; Preserved: AF, BC, DE, HL
57 | print_newline:
58 | push af
59 | ld a,newline
60 | call print_char_nocrc
61 | pop af
62 | ret
63 |
64 |
65 | ; Prints immediate string
66 | ; Preserved: AF, BC, DE, HL
67 | .macro print_str ; string,string2
68 | push hl
69 | call print_str_
70 | .byte \1
71 | .if NARGS > 1
72 | .byte \2
73 | .endif
74 | .if NARGS > 2
75 | .byte \3
76 | .endif
77 | .byte 0
78 | pop hl
79 | .endm
80 |
81 | print_str_:
82 | pop hl
83 | call print_str_hl
84 | jp hl
85 |
86 |
87 | ; Prints zero-terminated string pointed to by HL.
88 | ; On return, HL points to byte AFTER zero terminator.
89 | ; Preserved: AF, BC, DE
90 | print_str_hl:
91 | push af
92 | jr +
93 | - call print_char
94 | + ldi a,(hl)
95 | or a
96 | jr nz,-
97 | pop af
98 | ret
99 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/runtime.s:
--------------------------------------------------------------------------------
1 | ; Common routines and runtime
2 |
3 | ; Must be defined by target-specific runtime:
4 | ;
5 | ; init_runtime: ; target-specific inits
6 | ; std_print: ; default routine to print char A
7 | ; post_exit: ; called at end of std_exit
8 | ; report_byte: ; report A to user
9 |
10 | .define RUNTIME_INCLUDED 1
11 |
12 | .ifndef bss
13 | ; address of next normal variable
14 | .define bss $D800
15 | .endif
16 |
17 | .ifndef dp
18 | ; address of next direct-page ($FFxx) variable
19 | .define dp $FF80
20 | .endif
21 |
22 | ; DMG/CGB hardware identifier
23 | .define gb_id_cgb $10 ; mask for testing CGB bit
24 | .define gb_id_devcart $04 ; mask for testing "on devcart" bit
25 |
26 | .define gb_id bss
27 | .redefine bss bss+1
28 |
29 | ; Stack is normally here
30 | .define std_stack $DFFF
31 |
32 | ; Copies $1000 bytes from HL to $C000, then jumps to it.
33 | ; A is preserved for jumped-to code.
34 | copy_to_wram_then_run:
35 | ld b,a
36 |
37 | ld de,$C000
38 | ld c,$10
39 | - ldi a,(hl)
40 | ld (de),a
41 | inc e
42 | jr nz,-
43 | inc d
44 | dec c
45 | jr nz,-
46 |
47 | ld a,b
48 | jp $C000
49 |
50 | .ifndef CUSTOM_RESET
51 | reset:
52 | ; Run code from $C000, as is done on devcart. This
53 | ; ensures minimal difference in how it behaves.
54 | ld hl,$4000
55 | jp copy_to_wram_then_run
56 |
57 | .bank 1 slot 1
58 | .org $0 ; otherwise wla pads with lots of zeroes
59 | jp std_reset
60 | .endif
61 |
62 | ; Common routines
63 | .include "gb.inc"
64 | .include "macros.inc"
65 | .include "delay.s"
66 | .include "crc.s"
67 | .include "printing.s"
68 | .include "numbers.s"
69 | .include "testing.s"
70 |
71 | ; Sets up hardware and runs main
72 | std_reset:
73 |
74 | ; Init hardware
75 | di
76 | ld sp,std_stack
77 |
78 | ; Save DMG/CGB id
79 | ld (gb_id),a
80 |
81 | ; Init hardware
82 | .ifndef BUILD_GBS
83 | wreg TAC,$00
84 | wreg IF,$00
85 | wreg IE,$00
86 | .endif
87 |
88 | wreg NR52,0 ; sound off
89 | wreg NR52,$80 ; sound on
90 | wreg NR51,$FF ; mono
91 | wreg NR50,$77 ; volume
92 |
93 | ; TODO: clear all memory?
94 |
95 | ld hl,std_print
96 | call init_printing
97 | call init_testing
98 | call init_runtime
99 | call reset_crc ; in case init_runtime prints anything
100 |
101 | delay_msec 250
102 |
103 | ; Run user code
104 | call main
105 |
106 | ; Default is to successful exit
107 | ld a,0
108 | jp exit
109 |
110 |
111 | ; Exits code and reports value of A
112 | exit:
113 | ld sp,std_stack
114 | push af
115 | call +
116 | pop af
117 | jp post_exit
118 |
119 | + push af
120 | call print_newline
121 | call show_printing
122 | pop af
123 |
124 | ; Report exit status
125 | cp 1
126 |
127 | ; 0: ""
128 | ret c
129 |
130 | ; 1: "Failed"
131 | jr nz,+
132 | print_str "Failed",newline
133 | ret
134 |
135 | ; n: "Failed #n"
136 | + print_str "Failed #"
137 | call print_dec
138 | call print_newline
139 | ret
140 |
141 | ; returnOrg puts this code AFTER user code.
142 | .section "runtime" returnOrg
143 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/testing.s:
--------------------------------------------------------------------------------
1 | ; Diagnostic and testing utilities
2 |
3 | .define result bss+0
4 | .define test_name bss+1
5 | .redefine bss bss+3
6 |
7 |
8 | ; Sets test code and optional error text
9 | ; Preserved: AF, BC, DE, HL
10 | .macro set_test ; code[,text[,text2]]
11 | push hl
12 | call set_test_
13 | jr @set_test\@
14 | .byte \1
15 | .if NARGS > 1
16 | .byte \2
17 | .endif
18 | .if NARGS > 2
19 | .byte \3
20 | .endif
21 | .byte 0
22 | @set_test\@:
23 | pop hl
24 | .endm
25 |
26 | set_test_:
27 | pop hl
28 | push hl
29 | push af
30 | inc hl
31 | inc hl
32 | ldi a,(hl)
33 | ld (result),a
34 | ld a,l
35 | ld (test_name),a
36 | ld a,h
37 | ld (test_name+1),a
38 | pop af
39 | ret
40 |
41 |
42 | ; Initializes testing module
43 | init_testing:
44 | set_test $FF
45 | call init_crc
46 | ret
47 |
48 |
49 | ; Reports "Passed", then exits with code 0
50 | tests_passed:
51 | call print_newline
52 | print_str "Passed"
53 | ld a,0
54 | jp exit
55 |
56 |
57 | ; Reports "Done" if set_test has never been used,
58 | ; "Passed" if set_test 0 was last used, or
59 | ; failure if set_test n was last used.
60 | tests_done:
61 | ld a,(result)
62 | inc a
63 | jr z,+
64 | dec a
65 | jr z,tests_passed
66 | jr test_failed
67 | + print_str "Done"
68 | ld a,0
69 | jp exit
70 |
71 |
72 | ; Reports current error text and exits with result code
73 | test_failed:
74 | ld a,(test_name)
75 | ld l,a
76 | ld a,(test_name+1)
77 | ld h,a
78 | ld a,(hl)
79 | or a
80 | jr z,+
81 | call print_newline
82 | call print_str_hl
83 | call print_newline
84 | +
85 | ld a,(result)
86 | cp 1 ; if a = 0 then a = 1
87 | adc 0
88 | jp exit
89 |
90 |
91 | ; Prints checksum as 8-character hex value
92 | ; Preserved: AF, BC, DE, HL
93 | print_crc:
94 | push af
95 |
96 | ; Must read checksum entirely before printing,
97 | ; since printing updates it.
98 | lda checksum
99 | cpl
100 | push af
101 |
102 | lda checksum+1
103 | cpl
104 | push af
105 |
106 | lda checksum+2
107 | cpl
108 | push af
109 |
110 | lda checksum+3
111 | cpl
112 |
113 | call print_hex
114 | pop af
115 | call print_hex
116 | pop af
117 | call print_hex
118 | pop af
119 | call print_a
120 |
121 | pop af
122 | ret
123 |
124 |
125 | ; If checksum doesn't match expected, reports failed test.
126 | ; Passing 0 just prints checksum. Clears checksum afterwards.
127 | .macro check_crc ARGS crc
128 | .if crc == 0
129 | call show_printing
130 | call print_newline
131 | call print_crc
132 | .else
133 | ld bc,(crc >> 16) ~ $FFFF
134 | ld de,(crc & $FFFF) ~ $FFFF
135 | call check_crc_
136 | .endif
137 | .endm
138 |
139 | check_crc_:
140 | lda checksum+0
141 | cp e
142 | jr nz,+
143 |
144 | lda checksum+1
145 | cp d
146 | jr nz,+
147 |
148 | lda checksum+2
149 | cp c
150 | jr nz,+
151 |
152 | lda checksum+3
153 | cp b
154 | jr nz,+
155 |
156 | jp reset_crc
157 |
158 | + call print_crc
159 | jp test_failed
160 |
161 |
162 | ; Updates checksum with bytes from addr to addr+size-1
163 | .macro checksum_mem ARGS addr,size
164 | ld hl,addr
165 | ld bc,size
166 | call checksum_mem_
167 | .endm
168 |
169 | checksum_mem_:
170 | - ldi a,(hl)
171 | call update_crc
172 | dec bc
173 | ld a,b
174 | or c
175 | jr nz,-
176 | ret
177 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/common/tima_64.s:
--------------------------------------------------------------------------------
1 | ; Timer that's incremented every 64 cycles
2 |
3 | ; Initializes timer for use by sync_tima_64
4 | init_tima_64:
5 | wreg TMA,0
6 | wreg TAC,$07
7 | ret
8 |
9 | ; Synchronizes to timer
10 | sync_tima_64:
11 | push af
12 | push hl
13 |
14 | ld a,0
15 | ld hl,TIMA
16 | ld (hl),a
17 | - or (hl)
18 | jr z,-
19 |
20 | - delay 65-12
21 | xor a
22 | ld (hl),a
23 | or (hl)
24 | delay 4
25 | jr z,-
26 |
27 | pop hl
28 | pop af
29 | ret
30 |
31 | ; Read from this to get count that's incremented
32 | ; every 64 cycles.
33 | .define tima_64 TIMA
34 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/linkfile:
--------------------------------------------------------------------------------
1 | [objects]
2 | test.o
3 |
--------------------------------------------------------------------------------
/roms/mem_timing/source/shell.inc:
--------------------------------------------------------------------------------
1 | .incdir "common"
2 |
3 | ; GBS music file
4 | .ifdef BUILD_GBS
5 | .include "build_gbs.s"
6 | .endif
7 |
8 | ; Devcart
9 | .ifdef BUILD_DEVCART
10 | .include "build_devcart.s"
11 | .endif
12 |
13 | ; Sub-test in a multi-test ROM
14 | .ifdef BUILD_MULTI
15 | .include "build_multi.s"
16 | .endif
17 |
18 | ; GB ROM (default)
19 | .ifndef RUNTIME_INCLUDED
20 | .include "build_rom.s"
21 | .endif
22 |
--------------------------------------------------------------------------------
/roms/opus5/opus5.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/opus5/opus5.gb
--------------------------------------------------------------------------------
/roms/tictactoe/GB-TicTacToe.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/tictactoe/GB-TicTacToe.gb
--------------------------------------------------------------------------------
/roms/tobu/tobu.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/roms/tobu/tobu.gb
--------------------------------------------------------------------------------
/screenshot/dino.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/screenshot/dino.png
--------------------------------------------------------------------------------
/screenshot/drmario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/screenshot/drmario.png
--------------------------------------------------------------------------------
/screenshot/mario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/screenshot/mario.png
--------------------------------------------------------------------------------
/screenshot/tetris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/screenshot/tetris.png
--------------------------------------------------------------------------------
/screenshot/tobu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/screenshot/tobu.png
--------------------------------------------------------------------------------
/test/actual/bits_bank1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/bits_bank1.png
--------------------------------------------------------------------------------
/test/actual/cpu_instr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/cpu_instr.png
--------------------------------------------------------------------------------
/test/actual/daa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/daa.png
--------------------------------------------------------------------------------
/test/actual/div_write.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/div_write.png
--------------------------------------------------------------------------------
/test/actual/hello_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/hello_world.png
--------------------------------------------------------------------------------
/test/actual/if_ie_registers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/if_ie_registers.png
--------------------------------------------------------------------------------
/test/actual/mem_oam.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/mem_oam.png
--------------------------------------------------------------------------------
/test/actual/opus5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/opus5.png
--------------------------------------------------------------------------------
/test/actual/reg_f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/reg_f.png
--------------------------------------------------------------------------------
/test/actual/tictactoe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/tictactoe.png
--------------------------------------------------------------------------------
/test/actual/tim00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/tim00.png
--------------------------------------------------------------------------------
/test/actual/tim01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/tim01.png
--------------------------------------------------------------------------------
/test/actual/tim10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/tim10.png
--------------------------------------------------------------------------------
/test/actual/tim11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/tim11.png
--------------------------------------------------------------------------------
/test/actual/tobu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/actual/tobu.png
--------------------------------------------------------------------------------
/test/diff/bits_bank1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/diff/bits_bank1.png
--------------------------------------------------------------------------------
/test/diff/cpu_instr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/diff/cpu_instr.png
--------------------------------------------------------------------------------
/test/diff/hello_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/diff/hello_world.png
--------------------------------------------------------------------------------
/test/diff/opus5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/diff/opus5.png
--------------------------------------------------------------------------------
/test/diff/tictactoe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/diff/tictactoe.png
--------------------------------------------------------------------------------
/test/expect/bits_bank1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/bits_bank1.png
--------------------------------------------------------------------------------
/test/expect/cpu_instr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/cpu_instr.png
--------------------------------------------------------------------------------
/test/expect/daa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/daa.png
--------------------------------------------------------------------------------
/test/expect/div_write.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/div_write.png
--------------------------------------------------------------------------------
/test/expect/hello_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/hello_world.png
--------------------------------------------------------------------------------
/test/expect/if_ie_registers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/if_ie_registers.png
--------------------------------------------------------------------------------
/test/expect/mem_oam.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/mem_oam.png
--------------------------------------------------------------------------------
/test/expect/opus5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/opus5.png
--------------------------------------------------------------------------------
/test/expect/reg_f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/reg_f.png
--------------------------------------------------------------------------------
/test/expect/tictactoe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/tictactoe.png
--------------------------------------------------------------------------------
/test/expect/tim00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/tim00.png
--------------------------------------------------------------------------------
/test/expect/tim01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/tim01.png
--------------------------------------------------------------------------------
/test/expect/tim10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/tim10.png
--------------------------------------------------------------------------------
/test/expect/tim11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/tim11.png
--------------------------------------------------------------------------------
/test/expect/tobu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bokuweb/gopher-boy/556099049402dd19f5e6b0841d7dcc2be78a2c9d/test/expect/tobu.png
--------------------------------------------------------------------------------