├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── MetroBoy.code-workspace ├── README.md ├── SDL2.dll ├── build.hancho ├── docs ├── TemporalProgramming.md └── gateboy fails ├── images ├── ASUR_code.png ├── ASUR_context1.png ├── ASUR_context2.png ├── ASUR_die.png ├── ASUR_schematic.png ├── ASUR_traced.png ├── MetroBoy.png ├── furrtek_traces.png └── gateboy.png ├── notes ├── Traces.txt ├── _junk.txt ├── audio_test.txt ├── audio_testbench.txt ├── failing test notes.cpp └── gb cpu ports.txt ├── src ├── GateBoyApp │ ├── GateBoyApp.cpp │ ├── GateBoyApp.h │ ├── TraceViewer.cpp │ └── TraceViewer.h ├── GateBoyLib │ ├── GateBoy.cpp │ ├── GateBoy.h │ ├── GateBoyCh1.cpp │ ├── GateBoyCh1.h │ ├── GateBoyCh2.cpp │ ├── GateBoyCh2.h │ ├── GateBoyCh3.cpp │ ├── GateBoyCh3.h │ ├── GateBoyCh4.cpp │ ├── GateBoyCh4.h │ ├── GateBoyClocks.cpp │ ├── GateBoyClocks.h │ ├── GateBoyConfig.h │ ├── GateBoyCpuBus.cpp │ ├── GateBoyCpuBus.h │ ├── GateBoyDMA.cpp │ ├── GateBoyDMA.h │ ├── GateBoyDumper.cpp │ ├── GateBoyDumper.h │ ├── GateBoyExtBus.cpp │ ├── GateBoyExtBus.h │ ├── GateBoyInterrupts.cpp │ ├── GateBoyInterrupts.h │ ├── GateBoyJoypad.cpp │ ├── GateBoyJoypad.h │ ├── GateBoyLCD.cpp │ ├── GateBoyLCD.h │ ├── GateBoyLatches.cpp │ ├── GateBoyOamBus.cpp │ ├── GateBoyOamBus.h │ ├── GateBoyPair.cpp │ ├── GateBoyPair.h │ ├── GateBoyPins.cpp │ ├── GateBoyPins.h │ ├── GateBoyPixPipe.cpp │ ├── GateBoyPixPipe.h │ ├── GateBoyRegisters.cpp │ ├── GateBoyRegisters.h │ ├── GateBoyReset.cpp │ ├── GateBoyReset.h │ ├── GateBoySPU.cpp │ ├── GateBoySPU.h │ ├── GateBoySerial.cpp │ ├── GateBoySerial.h │ ├── GateBoySpriteFetcher.cpp │ ├── GateBoySpriteFetcher.h │ ├── GateBoySpriteScanner.cpp │ ├── GateBoySpriteScanner.h │ ├── GateBoySpriteStore.cpp │ ├── GateBoySpriteStore.h │ ├── GateBoyState.cpp │ ├── GateBoyState.h │ ├── GateBoyThread.cpp │ ├── GateBoyThread.h │ ├── GateBoyTileFetcher.cpp │ ├── GateBoyTileFetcher.h │ ├── GateBoyTimer.cpp │ ├── GateBoyTimer.h │ ├── GateBoyVramBus.cpp │ ├── GateBoyVramBus.h │ ├── GateBoyWaveBus.cpp │ ├── GateBoyWaveBus.h │ ├── GateBoyZramBus.cpp │ ├── GateBoyZramBus.h │ ├── Gates.cpp │ ├── Gates.h │ ├── IGateBoy.h │ ├── LogicBoy.cpp │ ├── LogicBoy.h │ ├── LogicBoyCh1.cpp │ ├── LogicBoyCh2.cpp │ ├── LogicBoyCh3.cpp │ ├── LogicBoyCh4.cpp │ ├── LogicBoyReset.h │ ├── LogicBoySPU.cpp │ ├── LogicBoyState.cpp │ ├── LogicBoyState.h │ ├── Probe.cpp │ ├── Probe.h │ ├── Regs.cpp │ ├── Regs.h │ ├── Utils.cpp │ └── Utils.h ├── GateBoyTests │ ├── GateBoyTests.cpp │ └── GateBoyTests.h ├── MetroBoyApp │ ├── MetroBoyApp.cpp │ └── MetroBoyApp.h ├── MetroBoyLib │ ├── MetroBoy.cpp │ ├── MetroBoy.h │ ├── MetroBoyBootrom.cpp │ ├── MetroBoyBootrom.h │ ├── MetroBoyCart.cpp │ ├── MetroBoyCart.h │ ├── MetroBoyDMA.cpp │ ├── MetroBoyDMA.h │ ├── MetroBoyInterrupts.cpp │ ├── MetroBoyInterrupts.h │ ├── MetroBoyJoypad.cpp │ ├── MetroBoyJoypad.h │ ├── MetroBoyOAM.cpp │ ├── MetroBoyOAM.h │ ├── MetroBoyPPU.cpp │ ├── MetroBoyPPU.h │ ├── MetroBoySPU2.cpp │ ├── MetroBoySPU2.h │ ├── MetroBoySerial.cpp │ ├── MetroBoySerial.h │ ├── MetroBoyTimer.cpp │ ├── MetroBoyTimer.h │ ├── MetroBoyVRAM.cpp │ ├── MetroBoyVRAM.h │ ├── MetroBoyZRAM.cpp │ └── MetroBoyZRAM.h ├── MetroBoyTests │ ├── MetroBoyTests.cpp │ ├── test_codegen.cpp │ ├── test_mooneye.cpp │ └── test_screenshot.cpp ├── Metronica │ ├── MetronicaApp.cpp │ └── MetronicaApp.h └── verilog │ ├── .gitignore │ ├── CPU.sv │ ├── Constants.sv │ ├── IRAM.sv │ ├── PPU.sv │ ├── SPU.sv │ ├── SPU.v │ ├── VRAM.sv │ ├── VRAM_test.cpp │ ├── VRAM_test.sh │ ├── classtest.sv │ ├── types.sv │ ├── types.v │ ├── wishbone_device.sv │ └── wishbone_host.sv ├── symlinks ├── gbmicrotest ├── metrolib ├── metron └── third_party ├── test.hancho └── tests ├── cpu_instrs ├── readme.txt └── source │ ├── 01-special.s │ ├── 02-interrupts.s │ ├── 03-op sp,hl.s │ ├── 04-op r,imm.s │ ├── 05-op rp.s │ ├── 06-ld r,r.s │ ├── 07-jr,jp,call,ret,rst.s │ ├── 08-misc instrs.s │ ├── 09-op r,r.s │ ├── 10-bit ops.s │ ├── 11-op a,(hl).s │ ├── common │ ├── apu.s │ ├── build_gbs.s │ ├── build_rom.s │ ├── checksums.s │ ├── console.bin │ ├── console.s │ ├── cpu_speed.s │ ├── crc.s │ ├── crc_fast.s │ ├── delay.s │ ├── gb.inc │ ├── instr_test.s │ ├── macros.inc │ ├── multi_custom.s │ ├── numbers.s │ ├── printing.s │ ├── runtime.s │ └── testing.s │ ├── linkfile │ └── shell.inc ├── micro_audio ├── audio1.s ├── audio2.s ├── audio3.s ├── audio4.s ├── makefile └── spu_env_change.s ├── micro_cpu ├── .gitignore ├── cpu_mov.s ├── cpu_nop.s ├── cpu_zeropage.s ├── makefile ├── rst_0x00.s ├── rst_0x08.s ├── rst_0x10.s ├── rst_0x18.s ├── rst_0x20.s ├── rst_0x28.s ├── rst_0x30.s ├── rst_0x38.s └── temp.link └── scanner ├── batch.py ├── header.inc ├── macros.inc ├── sample.link └── sample.s /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vs 3 | x64 4 | *.vcxproj.user 5 | *.vcxproj.filters 6 | imgui.ini 7 | .ninja_deps 8 | .ninja_log 9 | obj 10 | bin 11 | *.o 12 | *.gb 13 | *.exe 14 | __pycache__ 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | /* 4 | { 5 | "name": "GateBoyApp W32", 6 | "request": "launch", 7 | "type": "cppvsdbg", 8 | "program": "${workspaceFolder}/bin/GateBoyApp.exe", 9 | "preLaunchTask": "build_all", 10 | "args": [], 11 | "stopAtEntry": true, 12 | "cwd": "${workspaceFolder}", 13 | "environment": [], 14 | "console": "integratedTerminal", 15 | "internalConsoleOptions": "neverOpen", 16 | }, 17 | */ 18 | { 19 | "name": "GateBoyApp LIN", 20 | "preLaunchTask": "build_all", 21 | "request": "launch", 22 | "type": "cppdbg", 23 | "program": "${workspaceFolder}/bin/GateBoyApp", 24 | "cwd": "${workspaceFolder}", 25 | "args": [], 26 | }, 27 | { 28 | "name": "GateBoyTests LIN", 29 | "preLaunchTask": "build_all", 30 | "type": "cppdbg", 31 | "request": "launch", 32 | "program": "${workspaceFolder}/bin/GateBoyTests", 33 | "cwd": "${workspaceFolder}", 34 | "args": [], 35 | }, 36 | { 37 | "name": "MetronicaApp LIN", 38 | "preLaunchTask": "build_all", 39 | "type": "cppdbg", 40 | "request": "launch", 41 | "program": "${workspaceFolder}/bin/MetronicaApp", 42 | "cwd": "${workspaceFolder}", 43 | "args": [], 44 | }, 45 | { 46 | "name": "MetronicaApp W32", 47 | "preLaunchTask": "build_all", 48 | "type": "cppvsdbg", 49 | "request": "launch", 50 | "program": "${workspaceFolder}/bin/MetronicaApp.exe", 51 | "cwd": "${workspaceFolder}", 52 | "args": [], 53 | }, 54 | ] 55 | } 56 | 57 | 58 | /* 59 | //"visualizerFile": "${workspaceFolder}/src/GateBoyLib/GateBoy.natvis", 60 | "setupCommands": [ 61 | // Display content in STL containers pretty 62 | { 63 | "description": "Enable pretty-printing for gdb", 64 | "text": "-enable-pretty-printing", 65 | "ignoreFailures": true 66 | } 67 | ] 68 | */ 69 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.configureOnOpen": false, 3 | "C_Cpp.default.configurationProvider": "ms-vscode.makefile-tools" 4 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // clang-format off 2 | { 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "shell", 7 | "label": "build_all", 8 | "windows": { 9 | "command": "ninja -f build.windows.ninja", 10 | "problemMatcher": "$msCompile", 11 | }, 12 | "linux": { 13 | "command": "ninja", 14 | "problemMatcher": "$gcc", 15 | }, 16 | "args": [], 17 | "group": { 18 | "kind": "build", 19 | "isDefault": true 20 | } 21 | }, 22 | /* 23 | { 24 | "type": "shell", 25 | "label": "build_all", 26 | "command": "ninja", 27 | "args": [ 28 | ], 29 | "problemMatcher": "$gcc", 30 | "group": { 31 | "kind": "build", 32 | "isDefault": true 33 | } 34 | } 35 | */ 36 | ] 37 | } 38 | // clang-format on 39 | -------------------------------------------------------------------------------- /MetroBoy.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "ratio": "cpp", 10 | "semaphore": "cpp", 11 | "*.tcc": "cpp", 12 | "cstdlib": "cpp", 13 | "mutex": "cpp", 14 | "cctype": "cpp", 15 | "clocale": "cpp", 16 | "cmath": "cpp", 17 | "csignal": "cpp", 18 | "cstdarg": "cpp", 19 | "cstddef": "cpp", 20 | "cstdio": "cpp", 21 | "cstring": "cpp", 22 | "ctime": "cpp", 23 | "cwchar": "cpp", 24 | "cwctype": "cpp", 25 | "any": "cpp", 26 | "array": "cpp", 27 | "atomic": "cpp", 28 | "strstream": "cpp", 29 | "bit": "cpp", 30 | "bitset": "cpp", 31 | "chrono": "cpp", 32 | "cinttypes": "cpp", 33 | "codecvt": "cpp", 34 | "compare": "cpp", 35 | "complex": "cpp", 36 | "concepts": "cpp", 37 | "condition_variable": "cpp", 38 | "cstdint": "cpp", 39 | "deque": "cpp", 40 | "forward_list": "cpp", 41 | "list": "cpp", 42 | "map": "cpp", 43 | "set": "cpp", 44 | "string": "cpp", 45 | "unordered_map": "cpp", 46 | "unordered_set": "cpp", 47 | "vector": "cpp", 48 | "exception": "cpp", 49 | "algorithm": "cpp", 50 | "functional": "cpp", 51 | "iterator": "cpp", 52 | "memory": "cpp", 53 | "memory_resource": "cpp", 54 | "numeric": "cpp", 55 | "optional": "cpp", 56 | "random": "cpp", 57 | "regex": "cpp", 58 | "string_view": "cpp", 59 | "system_error": "cpp", 60 | "tuple": "cpp", 61 | "type_traits": "cpp", 62 | "utility": "cpp", 63 | "fstream": "cpp", 64 | "initializer_list": "cpp", 65 | "iomanip": "cpp", 66 | "iosfwd": "cpp", 67 | "iostream": "cpp", 68 | "istream": "cpp", 69 | "limits": "cpp", 70 | "new": "cpp", 71 | "numbers": "cpp", 72 | "ostream": "cpp", 73 | "ranges": "cpp", 74 | "shared_mutex": "cpp", 75 | "span": "cpp", 76 | "sstream": "cpp", 77 | "stdexcept": "cpp", 78 | "stop_token": "cpp", 79 | "streambuf": "cpp", 80 | "thread": "cpp", 81 | "cfenv": "cpp", 82 | "typeindex": "cpp", 83 | "typeinfo": "cpp", 84 | "valarray": "cpp", 85 | "variant": "cpp", 86 | "*.ipp": "cpp" 87 | }, 88 | "terminal.integrated.env.windows": { 89 | }, 90 | "terminal.integrated.env.linux": { 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/SDL2.dll -------------------------------------------------------------------------------- /build.hancho: -------------------------------------------------------------------------------- 1 | import glob 2 | 3 | hancho.base_rules = hancho.repo("{hancho_dir}/base_rules.hancho") 4 | hancho.metrolib = hancho.repo("symlinks/metrolib/build.hancho") 5 | hancho.glad = hancho.repo("symlinks/third_party/glad.hancho") 6 | hancho.imgui = hancho.repo("symlinks/third_party/imgui.hancho") 7 | 8 | hancho.context.defines = ["CONFIG_DEBUG"] 9 | hancho.context.includes = ["src", "symlinks", "symlinks/metrolib"] 10 | 11 | libs = [ 12 | hancho.metrolib.libappbase, 13 | hancho.metrolib.libaudio, 14 | hancho.metrolib.libcore, 15 | hancho.metrolib.libgameboy, 16 | hancho.glad.lib, 17 | hancho.imgui.lib, 18 | ] 19 | 20 | gateboy_app = hancho( 21 | hancho.base_rules.cpp_bin, 22 | in_srcs = [glob.glob("src/GateBoyLib/*.cpp"), "src/GateBoyApp/GateBoyApp.cpp"], 23 | in_libs = libs, 24 | sys_libs = ["-lSDL2", "-ldl", "-lpthread"], 25 | out_bin = "gateboy_app", 26 | ) 27 | 28 | metroboy_app = hancho( 29 | hancho.base_rules.cpp_bin, 30 | in_srcs = [glob.glob("src/MetroBoyLib/*.cpp"), "src/MetroBoyApp/MetroBoyApp.cpp"], 31 | in_libs = libs, 32 | sys_libs = ["-lSDL2"], 33 | out_bin = "metroboy_app" 34 | ) 35 | 36 | """ 37 | # Windows build 38 | 39 | ################################################################################ 40 | 41 | # we define SIM_AUDIO in config.h otherwise VSCode gets confused about what's 42 | # enabled and what's not 43 | #-DSIM_AUDIO 44 | 45 | # not having NDEBUG turns assert()s on 46 | 47 | build_mode = /O2 /D CONFIG_RELEASE /D NDEBUG /D _CRT_SECURE_NO_WARNINGS 48 | #build_mode = /D CONFIG_RELEASE /D _CRT_SECURE_NO_WARNINGS 49 | #build_mode = /D CONFIG_DEBUG /D _CRT_SECURE_NO_WARNINGS 50 | 51 | default_gpp = CL.exe /nologo /std:c++latest /EHsc /showIncludes /W4 /WX /diagnostics:caret /fp:fast /arch:AVX2 /sdl- /Gm- /GS- /Z7 /DEBUG /FS /GL 52 | default_gcc = CL.exe /nologo /std:c17 /showIncludes 53 | 54 | default_includes = /Isrc /Isymlinks /Isymlinks/glad 55 | global_libs = src/SDL2.lib 56 | 57 | """ -------------------------------------------------------------------------------- /docs/gateboy fails: -------------------------------------------------------------------------------- 1 | need to trace clocks for RUPO_LYC_MATCH_LATCH 2 | inverting ROPO clock delays it even more 3 | 4 | //---------- 5 | // PPU 6 | 7 | ppu_sprite0_scx3_a.gb 8 | read happens waaaay after we hit hblank, wtf 9 | dmg pass, ags fail 10 | 11 | ppu_sprite0_scx7_a.gb 12 | dmg pass, ags fail 13 | 14 | //---------- 15 | // STAT 16 | 17 | poweron_006_stat.gb / poweron_120_stat.gb 18 | dmg pass, ags fail 19 | cpu_data_latch is storing the 0 on the bus before oam lock drives it high. 20 | should be latching _after_ oam drives it high. 21 | 22 | lcdon_to_stat2_a.gb 23 | dmg pass, ags fail 24 | 25 | stat_write_glitch_l0_a.gb 26 | dmg pass, ags fail 27 | stat_write_glitch_l0_b.gb 28 | dmg pass, ags fail 29 | stat_write_glitch_l1_b.gb 30 | dmg pass, ags fail 31 | stat_write_glitch_l1_c.gb 32 | dmg pass, ags fail 33 | 34 | //---------- 35 | // OAM 36 | 37 | oam_read_l0_d.gb 38 | dmg pass, ags pass 39 | PIN_OAM_OE goes low in AB 40 | CPU_OAM_RDn goes low in GH 41 | PIN_CPU_LATCH_EXT goes high in DE 42 | if CPU_RD was delayed we might be able to latch oam in HA 43 | 44 | oam_read_l1_a.gb 45 | dmg pass, ags pass 46 | 47 | oam_read_l1_f.gb 48 | dmg pass, ags pass 49 | 50 | oam_write_l0_d.gb 51 | dmg pass, ags pass 52 | CPU_WRp goes low before rendering finishes 53 | 54 | oam_write_l1_c.gb 55 | dmg pass, ags fail 56 | CPU_WRp pulse happens too far before the scan/render gap 57 | 58 | lcdon_to_oam_unlock_d.gb 59 | dmg pass, ags pass 60 | should PIN_OAM_OE be low on EFGH? 61 | 62 | poweron_006_oam.gb / poweron_120_oam.gb / poweron_234_oam.gb 63 | no gap between mode 1 and mode 2? 64 | checked gates and i seem to have verified all of them 65 | dmg pass, ags fail 66 | 67 | //---------- 68 | // VRAM 69 | 70 | poweron_026_vram.gb 71 | dmg pass, ags fail 72 | poweron_140_vram.gb 73 | dmg pass, ags fail 74 | 75 | //---------- 76 | // MEALYBUG 77 | 78 | pass w/ sprite latch clock delayed 6 passes 79 | m3_lcdc_obj_size_change.gb 80 | m3_lcdc_obj_size_change_scx.gb 81 | 82 | pass w pix delay 1 pass 83 | m3_lcdc_bg_en_change.gb 84 | m3_bgp_change.gb 85 | m3_lcdc_obj_en_change.gb 86 | m3_lcdc_obj_en_change_variant.gb 87 | 88 | pass 89 | m3_lcdc_win_en_change_multiple_wx.gb - image from mealybug wrong 90 | m3_bgp_change_sprites.gb 91 | m3_obp0_change.gb 92 | m3_lcdc_bg_map_change.gb 93 | m3_lcdc_tile_sel_change.gb 94 | m3_lcdc_tile_sel_win_change.gb 95 | m3_lcdc_win_en_change_multiple.gb 96 | m3_lcdc_win_map_change.gb 97 | m3_scx_low_3_bits.gb 98 | m3_window_timing.gb 99 | m3_window_timing_wx_0.gb 100 | m3_wx_4_change.gb 101 | m3_wx_4_change_sprites.gb 102 | m3_wx_5_change.gb 103 | m3_wx_6_change.gb -------------------------------------------------------------------------------- /images/ASUR_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/ASUR_code.png -------------------------------------------------------------------------------- /images/ASUR_context1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/ASUR_context1.png -------------------------------------------------------------------------------- /images/ASUR_context2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/ASUR_context2.png -------------------------------------------------------------------------------- /images/ASUR_die.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/ASUR_die.png -------------------------------------------------------------------------------- /images/ASUR_schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/ASUR_schematic.png -------------------------------------------------------------------------------- /images/ASUR_traced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/ASUR_traced.png -------------------------------------------------------------------------------- /images/MetroBoy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/MetroBoy.png -------------------------------------------------------------------------------- /images/furrtek_traces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/furrtek_traces.png -------------------------------------------------------------------------------- /images/gateboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/images/gateboy.png -------------------------------------------------------------------------------- /notes/audio_testbench.txt: -------------------------------------------------------------------------------- 1 | 0x02f49000 0xff24 0x77 // vol 7/7 2 | 0x02f49010 0xff25 0xFF // all enable l/r 3 | 0x02f49020 0xff26 0x80 // enable spu 4 | 5 | 0x02f49020 0xff30 0x12 // wave ram 6 | 0x02f49030 0xff31 0x34 // wave ram 7 | 0x02f49040 0xff32 0x56 // wave ram 8 | 0x02f49050 0xff33 0x22 // wave ram 9 | 0x02f49060 0xff34 0x9a // wave ram 10 | 0x02f49070 0xff35 0xbc // wave ram 11 | 0x02f49080 0xff36 0xde // wave ram 12 | 0x02f49090 0xff37 0xfe // wave ram 13 | 0x02f490a0 0xff38 0xdc // wave ram 14 | 0x02f490b0 0xff39 0xba // wave ram 15 | 0x02f490c0 0xff3a 0x98 // wave ram 16 | 0x02f490d0 0xff3b 0x76 // wave ram 17 | 0x02f490e0 0xff3c 0x54 // wave ram 18 | 0x02f490f0 0xff3d 0x32 // wave ram 19 | 0x02f49100 0xff3e 0x10 // wave ram 20 | 0x02f49110 0xff3f 0x00 // wave ram 21 | 22 | // ch1 23 | //0x02f4a000 0xff10 0x00 // sweep period, sweep negate, sweep shift 24 | //0x02f4a010 0xff11 0x80 // duty len 25 | //0x02f4a020 0xff12 0x0F // vol, env add, env period 26 | //0x02f4a030 0xff13 0x0F // freq lo 27 | //0x02f4a040 0xff14 0x87 // trigger, len en, freq hi 28 | 29 | // ch2 30 | //0x02f4b000 0xff15 0x00 // unused 31 | //0x02f4b010 0xff16 0xB0 // duty len 32 | //0x02f4b020 0xff17 0xF7 // vol, env add, env period 33 | //0x02f4b030 0xff18 0x10 // freq lo 34 | //0x02f4b040 0xff19 0xC7 // trigger, len en, freq hi 35 | 36 | // ch3 37 | //0x02f4c000 0xff1a 0x80 // dac power 38 | //0x02f4c010 0xff1b 0x00 // 256-len 39 | //0x02f4c020 0xff1c 0x20 // vol code 40 | //0x02f4c030 0xff1d 0x80 // freq lo 41 | //0x02f4c040 0xff1e 0x87 // trigger, len en, freq hi 42 | 43 | 44 | // ch4 45 | 0x02f4d000 0xff1f 0x00 // unused 46 | 0x02f4d010 0xff20 0x00 // 64-len 47 | 0x02f4d020 0xff21 0xF0 // vol, env add, env period 48 | 0x02f4d030 0xff22 0x5F // clock mux, lfsr mode, lfsr divisor 49 | 0x02f4d040 0xff23 0x87 // trigger, len en, freq hi 50 | 51 | // ch1 duty ok 52 | // ch1 env ok 53 | // ch1 len ok 54 | // ch1 sweep seems ok? seems like period 0 should stop sweep and not shift 0 55 | 56 | // ch2 duty ok 57 | // ch2 env ok 58 | // ch2 len ok 59 | 60 | // ch3 len ok 61 | // ch3 vol ok -------------------------------------------------------------------------------- /notes/failing test notes.cpp: -------------------------------------------------------------------------------- 1 | poweron_stat_006.gb 2 | poweron_stat_120.gb 3 | lcdon_to_stat1_d.gb 4 | lcdon_to_stat2_a.gb 5 | // stat should read 84, but it reads 85 6 | // low bits of stat go from 101 -> 111 -> 110, so there _must_ be some sort of latch sampling the bus 7 | // stat low nibble goes 5-7-6, but it's supposed to read 4 - SADU cleared too late? 8 | // stat low nibble goes 4-6-2 but it's supposed to read 0 - RUPO cleared too late? 9 | // RUPO cleared too late? 10 | 11 | poweron_oam_006.gb 12 | poweron_oam_120.gb 13 | poweron_oam_234.gb 14 | lcdon_to_oam_unlock_d.gb 15 | // should be locked but wasn't - scan started late - oam locked the A after the read 16 | // so oam should be locked but its not? 17 | // hblank late 18 | // SIG_OAM_OEn goes back to 1 on E after the read starts, so we have to latch before that? 19 | // but the oam address doesn't show up until H? 20 | // sig_oam_clkn goes low EFGH during read 21 | 22 | 23 | 24 | poweron_vram_026.gb 25 | poweron_vram_140.gb 26 | // should be locked but wasn't - rendering started late? 27 | // Address is on the bus for B->A, XYMU goes low on A - should we be latching data on AB? 28 | 29 | 30 | 31 | 32 | oam_read_l0_d.gb 33 | // address does show up on the oam bus for two phases HA but OEn is high 34 | 35 | oam_read_l1_a.gb 36 | // oam OEn goes high on DE, CPU bus sees the value from D? interaction with oam latch? 37 | 38 | oam_read_l1_f.gb 39 | // should be locked but wasn't - scan started late? 40 | // if it was a data latch issue reading stat, it wouldn't also affect oam read...? 41 | 42 | // I should check that these oam tests are writing to 0xFE30 and not 0xFE00 or something 43 | 44 | // does the OAM memory latch the read address? 45 | 46 | 47 | // so we have a cpu data latch... we reset it on FG and GH 48 | // but we're tocking the CPU on AB _before_ tock_gates 49 | // so we need to send the CPU the new CPU bus on AB instead of the old? 50 | 51 | 52 | oam_write_l0_d.gb 53 | // should be unlocked but wasn't - hblank started late? 54 | 55 | oam_write_l1_c.gb 56 | // this is the scan/render gap 57 | 58 | line_153_lyc0_int_inc_sled.gb 59 | // the isr seems to fire at a reasonable time, but it interrupts the very last inc 60 | // ly changes from 153 to 0 on BC, and then there are 8 phases before ROPO latches 61 | // it, so it's not like that could be later 62 | // so like the last inc executes anyway somehow? 63 | // what do the other inc sleds do? 64 | // oam_int_inc_sled - 65 | // inc on AB, int on FG 66 | // vblank2_int_inc_sled - 67 | // inc on AB, int on BC 68 | // vblank2_int_inc_sled - 69 | // inc on AB, int on BC 70 | 71 | 72 | ppu_sprite0_scx3_b.gb 73 | // read goes on the bus on AB 74 | // stat goes to 00 on the _next_ AB SUGGESTS LATCH AT THE END OF AB 75 | // WODU goes high on HA 76 | // VOGA goes high on AB 77 | // the stat bits are driven off XYMU, which is driven off VOGA 78 | 79 | ppu_sprite0_scx7_b.gb 80 | // pix goes to 167 on HA 81 | // same as above? looks like it. 82 | 83 | 84 | int_hblank_halt_bug_b.gb 85 | // this feels like it's related to the inc sled failure 86 | // we should do another inc sled where the last instruction is a nop and see 87 | // if the inc is duplicated like in the halt bug 88 | 89 | 90 | // haven't looked at these yet 91 | stat_write_glitch_l0_a.gb 92 | stat_write_glitch_l0_b.gb 93 | stat_write_glitch_l1_b.gb 94 | stat_write_glitch_l1_c.gb 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | //load_rom("tests/mooneye-gb/tests/build/acceptance/" "ppu/lcdon_write_timing-GS.gb"); // dmg pass, gateboy fail 111 | //load_rom("tests/mooneye-gb/tests/build/acceptance/ppu/lcdon_timing-GS.gb"); // dmg pass, gateboy fail 112 | //load_rom("tests/mooneye-gb/tests/build/acceptance/ppu/intr_2_mode0_timing_sprites.gb"); // dmg pass, gateboy fail 113 | //load_rom("tests/gb-test-roms/cpu_instrs/cpu_instrs.gb"); // pass 114 | //load_rom("tests/gb-test-roms/instr_timing/instr_timing.gb"); // pass 115 | //load_rom("tests/gb-test-roms/mem_timing/mem_timing.gb"); // pass 116 | //load_rom("tests/gb-test-roms/mem_timing-2/mem_timing.gb"); // pass 117 | //load_rom("tests/gb-test-roms/halt_bug.gb"); // fail 118 | //load_rom("tests/gb-test-roms/oam_bug/oam_bug.gb"); // fail 119 | //load_rom("tests/scribbltests/lycscx/lycscx.gb"); // pass 120 | //load_rom("tests/scribbltests/lycscy/lycscy.gb"); // pass 121 | //load_rom("tests/scribbltests/palettely/palettely.gb"); 122 | //load_rom("tests/scribbltests/scxly/scxly.gb"); 123 | //load_rom("tests/scribbltests/statcount/statcount-auto.gb"); // pass 124 | 125 | -------------------------------------------------------------------------------- /notes/gb cpu ports.txt: -------------------------------------------------------------------------------- 1 | top left port 2 | 3 | yellow -> M1 SIG_CPU_6; -> LEXY -> unconnected pad on bottom side 4 | cyan <- CLK_N SIG_CPU_BOWA_Axxxxxxx <- this is the "put address on bus" clock 5 | cyan <- CLK_P SIG_CPU_BEDO_xBCDEFGH <- 6 | cyan <- PHI_P SIG_CPU_BEKO_ABCDxxxx <- this is the "reset for next cycle" clock 7 | cyan <- PHI_N SIG_CPU_BUDE_xxxxEFGH <- this is the "put write data on bus" clock 8 | ?cyan <- WRITEBACK_N SIG_CPU_BOLO_ABCDEFxx <- 9 | ?cyan <- WRITEBACK_P SIG_IN_CPU_DBUS_FREE -> ANUJ, DECY, LAVO, MUZU. GB and Gekkio disagree on the direction of this pin. 10 | ?cyan <- WRITEBACK_EXT SIG_CPU_BUKE_AxxxxxGH <- this is probably the "latch bus data" clock 11 | cyan <- MCLK_PULSE_N SIG_CPU_BOMA_xBCDEFGH <- These two clocks are the only ones that run before SIG_CPU_READYp is asserted. 12 | cyan <- MCLK_PULSE_P SIG_CPU_BOGA_Axxxxxxx <- These two clocks are the only ones that run before SIG_CPU_READYp is asserted. 13 | 14 | top middle port 15 | 16 | yellow -> CPUCLK_EN SIG_CPU_CLKREQ -> ABOL, TUBO 17 | cyan <- RESET_SYNC SIG_CPU_INT_RESETp <- P01.AFER , reset related state 18 | cyan <- RESET_ASYNC SIG_CPU_EXT_RESETp <- PIN_71_RST directly connected to the pad 19 | yellow -> SYSCLK_EN SIG_CPU_EXT_CLKGOOD <- chip.CLKIN_A top wire on PAD_XI, SIG_CPU_EXT_CLKGOOD should be CLK_EN instead, Gekkio's right 20 | cyan <- RESET_ACK SIG_CPU_STARTp <- P01.CPU_RESET 21 | 22 | top right port 23 | 24 | cyan <- NMI <- to unconnected pad on top side 25 | yellow -> RD SIG_IN_CPU_RDp -> LAGU, LAVO, TEDO 26 | yellow -> WR SIG_IN_CPU_WRp -> TEGU, LAGU, AREV 27 | cyan <- TEST_T1 SIG_CPU_UNOR_DBG <- P07.UNOR_MODE_DBG2 28 | cyan <- FExxFFxx SIG_CPU_ADDR_HIp <- P25.SYRO_FE00_FFFFp 29 | cyan <- BOOT_EN SIG_CPU_BOOTp <- P07.READ_BOOTROM tutu? 30 | cyan <- TEST_T2 SIG_CPU_UMUT_DBG <- P07.UMUT_MODE_DBG1 31 | yellow -> MEMRQ SIG_IN_CPU_EXT_BUSp -> TEXO, AGUT 32 | 33 | memrq = decoder.addr_valid and ((not boot_en and not fexx_ffxx) or (test_t2 and not fexx_ffxx)); 34 | 35 | bottom 36 | address bus 37 | WAKE 38 | 39 | 40 | 41 | 42 | xurg_inst: entity work.ssdff 43 | port map ( 44 | clk => decoder.m1, 45 | en => writeback, 46 | *res => cpuclk_shdn, 47 | *d => decoder.op_cb_s0xx, 48 | q => xurg 49 | ); 50 | 51 | is XURG driving WRITEBACK_P/SIG_IN_CPU_DBUS_FREE? 52 | -------------------------------------------------------------------------------- /src/GateBoyApp/GateBoyApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GateBoyLib/GateBoy.h" 4 | #include "GateBoyLib/GateBoyThread.h" 5 | 6 | #include "metrolib/appbase/App.h" 7 | #include "metrolib/appbase/Blitter.h" 8 | #include "metrolib/appbase/DumpPainter.h" 9 | #include "metrolib/appbase/GridPainter.h" 10 | #include "metrolib/appbase/TextPainter.h" 11 | #include "metrolib/core/Dumper.h" // for StringDumper 12 | #include "metrolib/core/StateStack.h" 13 | #include "metrolib/gameboy/Assembler.h" 14 | #include "metrolib/gameboy/GBBlitter.h" 15 | 16 | class GateBoyApp : public App { 17 | public: 18 | 19 | const char* app_get_title() override { return "GateBoyApp"; } 20 | void app_init(int screen_w, int screen_h) override; 21 | void app_close() override; 22 | bool pause_when_idle() override { return false; } 23 | 24 | void app_update(dvec2 screen_size, double delta) override; 25 | void app_render_frame(dvec2 screen_size, double delta) override; 26 | void app_render_ui(dvec2 /*screen_size*/, double /*delta*/) override {}; 27 | 28 | //---------- 29 | 30 | private: 31 | 32 | void load_golden(const char* filename); 33 | 34 | ViewController view_control; 35 | 36 | const uint8_t* keyboard_state = nullptr; 37 | 38 | GateBoyThread* gb_thread; 39 | 40 | GateBoy temp_gb; 41 | 42 | GridPainter grid_painter; 43 | TextPainter text_painter; 44 | DumpPainter dump_painter; 45 | GBBlitter gb_blitter; 46 | Blitter blitter; 47 | 48 | int frame_count = 0; 49 | int replay_cursor = 0; 50 | 51 | double frame_begin = 0; 52 | double frame_end = 0; 53 | double frame_time = 0; 54 | double frame_time_smooth = 0; 55 | 56 | uint32_t trace[912 * 154]; 57 | int trace_tex; 58 | 59 | uint32_t overlay[160 * 144]; 60 | int overlay_tex; 61 | 62 | uint8_t golden_u8[160 * 144]; 63 | bool has_golden = false; 64 | bool show_diff = false; 65 | bool show_golden = false; 66 | bool show_gb_ab = false; 67 | 68 | bool app_paused = false; 69 | 70 | int ram_tex; 71 | int wave_tex; 72 | 73 | Assembler assembler; 74 | 75 | 76 | blob temp_buf; 77 | Probes temp_probes; 78 | const char* temp_gb_id = nullptr; 79 | StringDumper d; 80 | uint8_t disasm_buf[64]; 81 | uint8_t stack_buf[8]; 82 | }; 83 | -------------------------------------------------------------------------------- /src/GateBoyApp/TraceViewer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #if 0 5 | #include "metrolib/core/SignalTree.h" 6 | 7 | class TraceViewer { 8 | public: 9 | 10 | uint32_t* framebuffer; 11 | int fb_width; 12 | int fb_height; 13 | 14 | 15 | int screen_x; 16 | int screen_y; 17 | 18 | void render(void* blob, int count, int size, const std::vector& signals, double center, double span); 19 | 20 | int render_signals(const std::vector& signals, void* blob, int x, int y); 21 | }; 22 | #endif 23 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyClocks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | #include "GateBoyLib/Gates.h" 4 | 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma pack(push, 1) 8 | struct GateBoyClock { 9 | void reset(); 10 | 11 | //---------------------------------------- 12 | 13 | /*_p01.ANOS*/ Gate ANOS_xBxDxFxH; // NAND latch deglitcher 14 | /*_p01.AVET*/ Gate AVET_AxCxExGx; // NAND latch deglitcher 15 | 16 | /*_p01.AFUR*/ DFF9 AFUR_ABCDxxxx; 17 | /*_p01.ALEF*/ DFF9 ALEF_xBCDExxx; 18 | /*_p01.APUK*/ DFF9 APUK_xxCDEFxx; 19 | /*_p01.ADYK*/ DFF9 ADYK_xxxDEFGx; 20 | 21 | /*_p29.WUVU*/ DFF17 WUVU_ABxxEFxx; 22 | /*_p21.VENA*/ DFF17 VENA_xxCDEFxx; 23 | /*_p29.WOSU*/ DFF17 WOSU_AxxDExxH; 24 | 25 | /*_SIG_CPU_CLKREQ */ SigIn SIG_CPU_CLKREQ; 26 | /*_SIG_CPU_BOWA_Axxxxxxx*/ SigOut SIG_CPU_BOWA_Axxxxxxx; // top left port PORTD_01: <- 27 | /*_SIG_CPU_BEDO_xBCDEFGH*/ SigOut SIG_CPU_BEDO_xBCDEFGH; // top left port PORTD_02: <- this is the "put address on bus" clock 28 | 29 | /*_SIG_CPU_BEKO_ABCDxxxx*/ SigOut SIG_CPU_BEKO_ABCDxxxx; // top left port PORTD_03: <- this is the "reset for next cycle" clock 30 | /*_SIG_CPU_BUDE_xxxxEFGH*/ SigOut SIG_CPU_BUDE_xxxxEFGH; // top left port PORTD_04: <- this is the "put write data on bus" clock 31 | 32 | /*_SIG_CPU_BOLO_ABCDEFxx*/ SigOut SIG_CPU_BOLO_ABCDEFxx; // top left port PORTD_05: <- 33 | /*_SIG_CPU_BUKE_AxxxxxGH*/ SigOut SIG_CPU_BUKE_AxxxxxGH; // top left port PORTD_07: <- this is probably the "latch bus data" clock 34 | 35 | /*_SIG_CPU_BOMA_xBCDEFGH*/ SigOut SIG_CPU_BOMA_xBCDEFGH; // top left port PORTD_08: <- (RESET_CLK) // These two clocks are the only ones that run before SIG_CPU_READYp is asserted. 36 | /*_SIG_CPU_BOGA_Axxxxxxx*/ SigOut SIG_CPU_BOGA_Axxxxxxx; // top left port PORTD_09: <- test pad 3 37 | }; 38 | #pragma pack(pop) 39 | 40 | //----------------------------------------------------------------------------- 41 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyConfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma warning(disable:4189) // unused local variable 4 | #pragma warning(disable:4100) // unused param 5 | #pragma warning(disable:4127) // conditional expression is constant 6 | #pragma warning(disable:4201) // anonymous struct/union 7 | 8 | #define SIM_AUDIO 9 | 10 | #if 1 11 | // Debug builds - painfully, ungodly slow. 12 | #ifdef CONFIG_DEBUG 13 | constexpr bool config_debug = true; 14 | #else 15 | constexpr bool config_debug = false; 16 | #endif 17 | 18 | // Regression test the LogicBoy implementation against GateBoy 19 | #ifdef CONFIG_REGRESSION 20 | constexpr bool config_regression = true; 21 | #else 22 | constexpr bool config_regression = false; 23 | #endif 24 | 25 | // Running tests in optimized mode build - don't bother with CHECK()s or hash 26 | // tests, but do use all the gate bits as they get folded into the memory dump. 27 | #ifdef CONFIG_RELEASE 28 | constexpr bool config_release = true; 29 | #else 30 | constexpr bool config_release = false; 31 | #endif 32 | 33 | // As-fast-as-possible build - turn all error checking off. 34 | #ifdef CONFIG_FASTMODE 35 | constexpr bool config_fastmode = true; 36 | #else 37 | constexpr bool config_fastmode = false; 38 | #endif 39 | 40 | static_assert(((int)config_debug + (int)config_regression + (int)config_release + (int)config_fastmode) == 1, "Bad build config"); 41 | #endif 42 | 43 | #if 0 44 | constexpr bool config_debug = true; 45 | constexpr bool config_regression = false; 46 | constexpr bool config_release = false; 47 | constexpr bool config_fastmode = false; 48 | #endif 49 | 50 | // debug - use flags, dchecks, idempotence checks 51 | // release - use flags, no dchecks 52 | // regression - no flags, no dchecks, regression checks against release 53 | // fast - no flags, no dchecks 54 | 55 | // Evaluate all DCHECK() statements (basically assert) 56 | constexpr bool config_dcheck = config_debug; 57 | constexpr bool config_use_flags = config_debug || config_release; 58 | constexpr bool config_check_flags = config_debug; 59 | 60 | // In debug builds we check that tock_slow() is idempotent. 61 | constexpr bool config_idempotence = config_debug; 62 | //constexpr bool config_idempotence = false; 63 | 64 | static_assert((config_check_flags && config_use_flags) || !config_check_flags, "Bad config_use/check_flags"); 65 | 66 | //----------------------------------------------------------------------------- 67 | // Audio config 68 | 69 | constexpr int64_t input_hz = 154 * 114 * 60; 70 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyDMA.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct RegDmaLo { 7 | void reset(); 8 | 9 | /*#p04.NAKY*/ DFF17 NAKY_DMA_A00p_odd; // Axxxxxxx 10 | /*#p04.PYRO*/ DFF17 PYRO_DMA_A01p_odd; // Axxxxxxx 11 | /*_p04.NEFY*/ DFF17 NEFY_DMA_A02p_odd; // Axxxxxxx 12 | /*_p04.MUTY*/ DFF17 MUTY_DMA_A03p_odd; // Axxxxxxx 13 | /*_p04.NYKO*/ DFF17 NYKO_DMA_A04p_odd; // Axxxxxxx 14 | /*_p04.PYLO*/ DFF17 PYLO_DMA_A05p_odd; // Axxxxxxx 15 | /*_p04.NUTO*/ DFF17 NUTO_DMA_A06p_odd; // Axxxxxxx 16 | /*_p04.MUGU*/ DFF17 MUGU_DMA_A07p_odd; // Axxxxxxx 17 | }; 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | struct RegDmaHi { 22 | void reset(); 23 | 24 | /*#p04.NAFA*/ DFF8 NAFA_DMA_A08p; // xxxxxxxH 25 | /*_p04.PYNE*/ DFF8 PYNE_DMA_A09p; // xxxxxxxH 26 | /*_p04.PARA*/ DFF8 PARA_DMA_A10p; // xxxxxxxH 27 | /*_p04.NYDO*/ DFF8 NYDO_DMA_A11p; // xxxxxxxH 28 | /*_p04.NYGY*/ DFF8 NYGY_DMA_A12p; // xxxxxxxH 29 | /*_p04.PULA*/ DFF8 PULA_DMA_A13p; // xxxxxxxH 30 | /*_p04.POKU*/ DFF8 POKU_DMA_A14p; // xxxxxxxH 31 | /*_p04.MARU*/ DFF8 MARU_DMA_A15p; // xxxxxxxH 32 | }; 33 | 34 | //----------------------------------------------------------------------------- 35 | 36 | struct DmaControl { 37 | void reset(); 38 | 39 | /*#p04.LYXE*/ NorLatch LYXE_DMA_LATCHp; // xxxxExxx 40 | /*#p04.MYTE*/ DFF17 MYTE_DMA_DONE_odd; // xxxxExxx 41 | /*#p04.LUVY*/ DFF17 LUVY_DMA_TRIG_d0_odd; // Axxxxxxx 42 | /*#p04.LENE*/ DFF17 LENE_DMA_TRIG_d4_odd; // xxxxExxx 43 | /*_p04.LARA*/ Gate LARA_DMA_LATCHn_odd; // xxxxExxx - NAND latch w/ LOKY 44 | /*#p04.LOKY*/ Gate LOKY_DMA_LATCHp_odd; // xxxxExxx - NAND latch w/ LARA 45 | }; 46 | 47 | //----------------------------------------------------------------------------- 48 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyDumper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Dumper; 4 | struct GateBoySys; 5 | struct GateBoyState; 6 | struct GateBoyPins; 7 | 8 | class GateBoyDumper { 9 | public: 10 | void dump_pins(const GateBoyPins& p, Dumper& d); 11 | void dump_sys(const GateBoySys& s, Dumper& d); 12 | void dump_cpu_bus(const GateBoyState& s, Dumper& d); 13 | void dump_ext_bus(const GateBoyState& s, Dumper& d); 14 | void dump_vram_bus(const GateBoyState& s, Dumper& d); 15 | void dump_oam_bus(const GateBoyState& s, Dumper& d); 16 | void dump_sprite_scanner(const GateBoyState& s, Dumper& d); 17 | void dump_tile_fetcher(const GateBoyState& s, Dumper& d); 18 | void dump_sprite_fetcher(const GateBoyState& s, Dumper& d); 19 | void dump_resets(const GateBoyState& s, Dumper& d); 20 | void dump_timer(const GateBoyState& s, Dumper& d); 21 | void dump_clocks(const GateBoyState& s, Dumper& d); 22 | void dump_interrupts(const GateBoyState& s, Dumper& d); 23 | void dump_joypad(const GateBoyState& s, Dumper& d); 24 | void dump_lcd(const GateBoyState& s, Dumper& d); 25 | void dump_sprite_store(const GateBoyState& s, Dumper& d); 26 | void dump_mbc1(const GateBoyState& s, Dumper& d); 27 | void dump_dma(const GateBoyState& s, Dumper& d); 28 | void dump_serial(const GateBoyState& s, Dumper& d); 29 | void dump_ppu(const GateBoyState& s, Dumper& d); 30 | void dump_spu(const GateBoyState& s, Dumper& d); 31 | }; 32 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyExtBus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct ExtDataLatch { 7 | void reset(); 8 | 9 | /*#p08.SOMA*/ TpLatch SOMA_EXT_DATA_LATCH_D0n; // AxCxExxx 10 | /*_p08.RONY*/ TpLatch RONY_EXT_DATA_LATCH_D1n; // AxCxExxx 11 | /*_p08.RAXY*/ TpLatch RAXY_EXT_DATA_LATCH_D2n; // AxCxExxx 12 | /*_p08.SELO*/ TpLatch SELO_EXT_DATA_LATCH_D3n; // AxCxExxx 13 | /*_p08.SODY*/ TpLatch SODY_EXT_DATA_LATCH_D4n; // AxCxExxx 14 | /*_p08.SAGO*/ TpLatch SAGO_EXT_DATA_LATCH_D5n; // AxCxExxx 15 | /*_p08.RUPA*/ TpLatch RUPA_EXT_DATA_LATCH_D6n; // AxCxExxx 16 | /*_p08.SAZY*/ TpLatch SAZY_EXT_DATA_LATCH_D7n; // AxCxExxx 17 | }; 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | struct ExtAddrLatch { 22 | void reset(); 23 | 24 | /*_p08.ALOR*/ TpLatch ALOR_EXT_ADDR_LATCH_00p; // xBxxxxxx 25 | /*_p08.APUR*/ TpLatch APUR_EXT_ADDR_LATCH_01p; // xBxxxxxx 26 | /*_p08.ALYR*/ TpLatch ALYR_EXT_ADDR_LATCH_02p; // xBxxxxxx 27 | /*_p08.ARET*/ TpLatch ARET_EXT_ADDR_LATCH_03p; // xBxxxxxx 28 | /*_p08.AVYS*/ TpLatch AVYS_EXT_ADDR_LATCH_04p; // xBxxxxxx 29 | /*_p08.ATEV*/ TpLatch ATEV_EXT_ADDR_LATCH_05p; // xBxxxxxx 30 | /*_p08.AROS*/ TpLatch AROS_EXT_ADDR_LATCH_06p; // xBxxxxxx 31 | /*_p08.ARYM*/ TpLatch ARYM_EXT_ADDR_LATCH_07p; // xBxxxxxx 32 | /*_p08.LUNO*/ TpLatch LUNO_EXT_ADDR_LATCH_08p; // ABxxxxxx 33 | /*_p08.LYSA*/ TpLatch LYSA_EXT_ADDR_LATCH_09p; // ABxxxxxx 34 | /*_p08.PATE*/ TpLatch PATE_EXT_ADDR_LATCH_10p; // ABxxxxxx 35 | /*_p08.LUMY*/ TpLatch LUMY_EXT_ADDR_LATCH_11p; // ABxxxxxx 36 | /*_p08.LOBU*/ TpLatch LOBU_EXT_ADDR_LATCH_12p; // ABxxxxxx 37 | /*_p08.LONU*/ TpLatch LONU_EXT_ADDR_LATCH_13p; // ABxxxxxx 38 | /*_p08.NYRE*/ TpLatch NYRE_EXT_ADDR_LATCH_14p; // ABxxxxxx 39 | }; 40 | 41 | //----------------------------------------------------------------------------- 42 | 43 | struct GateBoyMBC { 44 | void reset(); 45 | 46 | Gate MBC1_RAM_EN; 47 | Gate MBC1_MODE; 48 | 49 | Gate MBC1_BANK0; 50 | Gate MBC1_BANK1; 51 | Gate MBC1_BANK2; 52 | Gate MBC1_BANK3; 53 | Gate MBC1_BANK4; 54 | Gate MBC1_BANK5; 55 | Gate MBC1_BANK6; 56 | }; 57 | 58 | //----------------------------------------------------------------------------- 59 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyInterrupts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct RegIF { 7 | void reset(); 8 | 9 | /*_p02.LOPE*/ DFF22 LOPE_FF0F_D0p; 10 | /*_p02.LALU*/ DFF22 LALU_FF0F_D1p; 11 | /*_p02.NYBO*/ DFF22 NYBO_FF0F_D2p; 12 | /*_p02.UBUL*/ DFF22 UBUL_FF0F_D3p; 13 | /*_p02.ULAK*/ DFF22 ULAK_FF0F_D4p; 14 | }; 15 | 16 | //----------------------------------------------------------------------------- 17 | // This is technically in the CPU, but we're going to implement it here for now. 18 | 19 | struct RegIE { 20 | void reset(); 21 | 22 | DFF IE_D0; 23 | DFF IE_D1; 24 | DFF IE_D2; 25 | DFF IE_D3; 26 | DFF IE_D4; 27 | }; 28 | 29 | //----------------------------------------------------------------------------- 30 | 31 | struct InterruptLatch { 32 | void reset(); 33 | 34 | /*_p02.MATY*/ TpLatch MATY_FF0F_L0p; 35 | /*_p02.MOPO*/ TpLatch MOPO_FF0F_L1p; 36 | /*_p02.PAVY*/ TpLatch PAVY_FF0F_L2p; 37 | /*_p02.NEJY*/ TpLatch NEJY_FF0F_L3p; 38 | /*_p02.NUTY*/ TpLatch NUTY_FF0F_L4p; 39 | }; 40 | 41 | //----------------------------------------------------------------------------- 42 | 43 | struct CpuInt { 44 | void reset(); 45 | 46 | /*_SIG_CPU_INT_VBLANK*/ SigOut SIG_CPU_INT_VBLANK; // bottom right port PORTB_03: <- P02.LOPE, vblank int 47 | /*_SIG_CPU_INT_STAT */ SigOut SIG_CPU_INT_STAT ; // bottom right port PORTB_07: <- P02.LALU, stat int 48 | /*_SIG_CPU_INT_TIMER */ SigOut SIG_CPU_INT_TIMER ; // bottom right port PORTB_11: <- P02.NYBO, timer int 49 | /*_SIG_CPU_INT_SERIAL*/ SigOut SIG_CPU_INT_SERIAL; // bottom right port PORTB_15: <- P02.UBUL, serial int 50 | /*_SIG_CPU_INT_JOYPAD*/ SigOut SIG_CPU_INT_JOYPAD; // bottom right port PORTB_19: <- P02.ULAK, joy int 51 | }; 52 | 53 | //----------------------------------------------------------------------------- 54 | 55 | struct CpuAck { 56 | void reset(); 57 | 58 | /*_SIG_CPU_ACK_VBLANK*/ SigIn SIG_CPU_ACK_VBLANK; // bottom right port PORTB_01: -> P02.LETY, vblank int ack 59 | /*_SIG_CPU_ACK_STAT */ SigIn SIG_CPU_ACK_STAT ; // bottom right port PORTB_05: -> P02.LEJA, stat int ack 60 | /*_SIG_CPU_ACK_TIMER */ SigIn SIG_CPU_ACK_TIMER ; // bottom right port PORTB_09: -> P02.LESA, timer int ack 61 | /*_SIG_CPU_ACK_SERIAL*/ SigIn SIG_CPU_ACK_SERIAL; // bottom right port PORTB_13: -> P02.LUFE, serial int ack 62 | /*_SIG_CPU_ACK_JOYPAD*/ SigIn SIG_CPU_ACK_JOYPAD; // bottom right port PORTB_17: -> P02.LAMO, joy int ack 63 | }; 64 | 65 | //----------------------------------------------------------------------------- 66 | 67 | struct InterruptControl { 68 | void reset(); 69 | 70 | // This is driven by what we think is a latch and it goes straight to the CPU - maybe there's a pull-down? 71 | /*_p02.AWOB*/ TpLatch AWOB_WAKE_CPU; 72 | /*_SIG_CPU_WAKE*/ SigOut SIG_CPU_WAKE; // top right wire by itself <- P02.AWOB 73 | 74 | /*#p03.NYDU*/ DFF17 NYDU_TIMA7p_DELAY; // Axxxxxxx 75 | /*#p03.MOBA*/ DFF17 MOBA_TIMER_OVERFLOWp; // AxxxExxx 76 | 77 | /*_p21.RUPO*/ NorLatch RUPO_LYC_MATCHn; 78 | /*#p21.ROPO*/ DFF17 ROPO_LY_MATCH_SYNCp; // xxCxxxxx 79 | }; 80 | 81 | //----------------------------------------------------------------------------- 82 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyJoypad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct JoyInt { 7 | void reset(); 8 | 9 | /*_p02.BATU*/ DFF17 BATU_JP_GLITCH0; 10 | /*_p02.ACEF*/ DFF17 ACEF_JP_GLITCH1; 11 | /*_p02.AGEM*/ DFF17 AGEM_JP_GLITCH2; 12 | /*#p02.APUG*/ DFF17 APUG_JP_GLITCH3; 13 | }; 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | struct RegJoy { 18 | void reset(); 19 | 20 | // Ignoring debug stuff for now 21 | ///*_p05.JUTE*/ DFF17 JUTE_DBG_D0; 22 | ///*_p05.KECY*/ DFF17 KECY_DBG_D1; 23 | ///*_p05.JALE*/ DFF17 JALE_DBG_D2; 24 | ///*_p05.KYME*/ DFF17 KYME_DBG_D3; 25 | /*#p05.KELY*/ DFF17 KELY_JOYP_UDLRp; // these two _must_ be 1 on reset or we read button states before we've written these regs 26 | /*#p05.COFY*/ DFF17 COFY_JOYP_ABCSp; // or wait am i getting polarity wrong? 27 | ///*_p05.KUKO*/ DFF17 KUKO_DBG_D6; 28 | ///*_p05.KERU*/ DFF17 KERU_DBG_D7; 29 | }; 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | struct JoyLatch { 34 | void reset(); 35 | 36 | /*#p05.KEVU*/ TpLatch KEVU_JOYP_L0n; 37 | /*#p05.KAPA*/ TpLatch KAPA_JOYP_L1n; 38 | /*#p05.KEJA*/ TpLatch KEJA_JOYP_L2n; 39 | /*#p05.KOLO*/ TpLatch KOLO_JOYP_L3n; 40 | }; 41 | 42 | //----------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyLCD.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct LCDControl { 7 | void reset(); 8 | 9 | // H deltas are due to reg writes 10 | /*#p29.CATU*/ DFF17 CATU_LINE_ENDp_odd; // Axxxxxxx 11 | /*#p28.ANEL*/ DFF17 ANEL_LINE_ENDp_odd; // xxCxxxxx 12 | /*#p21.POPU*/ DFF17 POPU_VBLANKp_odd; // xxCxxxxH 13 | /*#p21.MYTA*/ DFF17 MYTA_FRAME_ENDp_odd; // xxCxxxxH 14 | /*#p21.RUTU*/ DFF17 RUTU_LINE_ENDp_odd; // xxxxxxGx 15 | /*#p21.NYPE*/ DFF17 NYPE_LINE_ENDp_odd; // xxCxxxxx 16 | 17 | /*#p21.SYGU*/ DFF17 SYGU_LINE_STROBE; // xxxxxxGH 18 | /*#p24.MEDA*/ DFF17 MEDA_VSYNC_OUTn; // xxCxxxxH 19 | /*#p24.LUCA*/ DFF17 LUCA_LINE_EVENp; // xxxxxxGH 20 | /*#p21.NAPO*/ DFF17 NAPO_FRAME_EVENp; // xxCxxxxx 21 | 22 | // RUJU+POFY+POME form a nor latch 23 | /*#p24.RUJU*/ Gate RUJU; // AxxxxFxx 24 | /*#p24.POFY*/ Gate POFY; // AxxxxFxx 25 | /*#p24.POME*/ Gate POME_X8_LATCH; // AxxxxFxx 26 | /*_p24.PAHO*/ DFF17 PAHO_X8_SYNC; // xBxDxFxH 27 | /*#p21.WUSA*/ NorLatch WUSA_LCD_CLOCK_GATE; // xBxDxFGH High on G at beginning of line, low on H at end of line. Not sure what's up with the others. Scroll/sprite count? 28 | 29 | Gate REMY_LD0n; 30 | Gate RAVO_LD1n; 31 | }; 32 | 33 | //----------------------------------------------------------------------------- 34 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyOamBus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GateBoyLib/Regs.h" 4 | 5 | //----------------------------------------------------------------------------- 6 | 7 | struct OamControl { 8 | void reset(); 9 | 10 | /*_p04.MAKA*/ DFF17 MAKA_LATCH_EXTp; // AxxxExxx 11 | /*_p28.WUJE*/ NorLatch WUJE_CPU_OAM_WRn; // AxxxExxx 12 | 13 | /*_SIG_OAM_CLKn */ SigOut SIG_OAM_CLKn; // ABCDEFGH 14 | /*_SIG_OAM_WRn_A*/ SigOut SIG_OAM_WRn_A; // AxxxExxH 15 | /*_SIG_OAM_WRn_B*/ SigOut SIG_OAM_WRn_B; // AxxxExxH 16 | /*_SIG_OAM_OEn */ SigOut SIG_OAM_OEn; // ABCDEFGH 17 | 18 | Gate old_oam_clk; 19 | }; 20 | 21 | //----------------------------------------------------------------------------- 22 | 23 | struct OamABus { 24 | void reset(); 25 | 26 | /*_BUS_OAM_A00n*/ Bus BUS_OAM_A00n; // ABCDEFGH 27 | /*_BUS_OAM_A01n*/ Bus BUS_OAM_A01n; // ABCDEFGH 28 | /*_BUS_OAM_A02n*/ Bus BUS_OAM_A02n; // ABCDEFGH 29 | /*_BUS_OAM_A03n*/ Bus BUS_OAM_A03n; // ABCDEFGH 30 | /*_BUS_OAM_A04n*/ Bus BUS_OAM_A04n; // ABCDEFGH 31 | /*_BUS_OAM_A05n*/ Bus BUS_OAM_A05n; // ABCDEFGH 32 | /*_BUS_OAM_A06n*/ Bus BUS_OAM_A06n; // ABCDEFGH 33 | /*_BUS_OAM_A07n*/ Bus BUS_OAM_A07n; // ABCDEFGH 34 | }; 35 | 36 | //----------------------------------------------------------------------------- 37 | 38 | struct OamDBusA { 39 | void reset(); 40 | 41 | /*_BUS_OAM_DA00n*/ Bus BUS_OAM_DA00n; 42 | /*_BUS_OAM_DA01n*/ Bus BUS_OAM_DA01n; 43 | /*_BUS_OAM_DA02n*/ Bus BUS_OAM_DA02n; 44 | /*_BUS_OAM_DA03n*/ Bus BUS_OAM_DA03n; 45 | /*_BUS_OAM_DA04n*/ Bus BUS_OAM_DA04n; 46 | /*_BUS_OAM_DA05n*/ Bus BUS_OAM_DA05n; 47 | /*_BUS_OAM_DA06n*/ Bus BUS_OAM_DA06n; 48 | /*_BUS_OAM_DA07n*/ Bus BUS_OAM_DA07n; 49 | }; 50 | 51 | //----------------------------------------------------------------------------- 52 | 53 | struct OamDBusB { 54 | void reset(); 55 | 56 | /*_BUS_OAM_DB00n*/ Bus BUS_OAM_DB00n; 57 | /*_BUS_OAM_DB01n*/ Bus BUS_OAM_DB01n; 58 | /*_BUS_OAM_DB02n*/ Bus BUS_OAM_DB02n; 59 | /*_BUS_OAM_DB03n*/ Bus BUS_OAM_DB03n; 60 | /*_BUS_OAM_DB04n*/ Bus BUS_OAM_DB04n; 61 | /*_BUS_OAM_DB05n*/ Bus BUS_OAM_DB05n; 62 | /*_BUS_OAM_DB06n*/ Bus BUS_OAM_DB06n; 63 | /*_BUS_OAM_DB07n*/ Bus BUS_OAM_DB07n; 64 | }; 65 | 66 | //----------------------------------------------------------------------------- 67 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyPair.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/IGateBoy.h" 3 | #include "metrolib/core/Result.h" 4 | 5 | struct GateBoy; 6 | struct LogicBoy; 7 | 8 | void print_field_at(int offset); 9 | 10 | //--------------------------------------------------------------------------------------------------------------------- 11 | 12 | struct GateBoyPair : public IGateBoy { 13 | GateBoyPair(); 14 | GateBoyPair(GateBoy* gb1, LogicBoy* gb2); 15 | 16 | IGateBoy* get_a() override; 17 | IGateBoy* get_b() override; 18 | 19 | const char* get_id() const override; 20 | 21 | IGateBoy* clone() const override; 22 | int size_bytes() override; 23 | GBResult get_flags() const override; 24 | GBResult load_bootrom(const uint8_t* data, int size) override; 25 | GBResult load_raw_dump(BlobStream& dump_in) override; 26 | GBResult save_raw_dump(BlobStream& dump_out) const override; 27 | GBResult poweron(bool fastboot) override; 28 | GBResult reset() override; 29 | GBResult peek(int addr) const override; 30 | GBResult poke(int addr, uint8_t data_in) override; 31 | GBResult dbg_req(uint16_t addr, uint8_t data, bool write) override; 32 | GBResult dbg_read(const blob& cart_blob, int addr) override; 33 | GBResult dbg_write (const blob& cart_blob, int addr, uint8_t data_in) override; 34 | 35 | GBResult run_phases(const blob& cart_blob, int phase_count) override; 36 | GBResult next_phase(const blob& cart_blob) override; 37 | GBResult run_to(const blob& cart_blob, int phase) override; 38 | 39 | GBResult set_buttons(uint8_t buttons) override; 40 | GBResult set_cpu_en(bool enabled) override; 41 | 42 | const GateBoyCpu& get_cpu() const override; 43 | const GateBoyMem& get_mem() const override; 44 | const GateBoyState& get_state() const override; 45 | const GateBoySys& get_sys() const override; 46 | const GateBoyPins& get_pins() const override; 47 | const Probes& get_probes() const override; 48 | 49 | void get_flat_blob(const blob& cart_blob, int addr, int size, uint8_t *out) const override; 50 | 51 | GateBoy* gb; 52 | LogicBoy* lb; 53 | 54 | GBResult check_results(GBResult r1, GBResult r2) const; 55 | bool check_sync() const; 56 | }; 57 | 58 | //--------------------------------------------------------------------------------------------------------------------- 59 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyReset.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/GateBoyReset.h" 2 | 3 | #include "GateBoyLib/GateBoy.h" 4 | #include "GateBoyLib/Gates.h" 5 | 6 | //----------------------------------------------------------------------------- 7 | 8 | void GateBoy::tock_reset_gates(const GateBoyState& reg_old, DFF17 UPOF_DIV15p) { 9 | auto& reg_new = gb_state; 10 | 11 | /*_p01.ABOL*/ wire ABOL_CLKREQn = not1(reg_new.sys_clk.SIG_CPU_CLKREQ.out_new()); 12 | /*#p01.AROV*/ wire AROV_xxCDEFxx = not1(reg_new.sys_clk.APUK_xxCDEFxx.qn_new()); 13 | /*#p01.AFEP*/ wire AFEP_AxxxxFGH = not1(reg_new.sys_clk.ALEF_xBCDExxx.qp_new()); 14 | /*#p01.ATYP*/ wire ATYP_ABCDxxxx = not1(reg_new.sys_clk.AFUR_ABCDxxxx.qn_new()); 15 | 16 | /*#p01.BELU*/ wire BELU_xxxxEFGH = nor2(ATYP_ABCDxxxx, ABOL_CLKREQn); 17 | /*#p01.BYRY*/ wire BYRY_ABCDxxxx = not1(BELU_xxxxEFGH); 18 | /*#p01.BUDE*/ wire BUDE_xxxxEFGH = not1(BYRY_ABCDxxxx); 19 | /*#p01.BAPY*/ wire BAPY_xxxxxxGH = nor3(ABOL_CLKREQn, AROV_xxCDEFxx, ATYP_ABCDxxxx); 20 | /*#p01.BERU*/ wire BERU_ABCDEFxx = not1(BAPY_xxxxxxGH); 21 | /*#p01.BUFA*/ wire BUFA_xxxxxxGH = not1(BERU_ABCDEFxx); 22 | /*#p01.BOLO*/ wire BOLO_ABCDEFxx = not1(BUFA_xxxxxxGH); 23 | /*#p01.BEKO*/ wire BEKO_ABCDxxxx = not1(BUDE_xxxxEFGH); // BEKO+BAVY parallel 24 | /*#p01.BEJA*/ wire BEJA_xxxxEFGH = nand4(BOLO_ABCDEFxx, BOLO_ABCDEFxx, BEKO_ABCDxxxx, BEKO_ABCDxxxx); 25 | /*#p01.BANE*/ wire BANE_ABCDxxxx = not1(BEJA_xxxxEFGH); 26 | /*#p01.BELO*/ wire BELO_xxxxEFGH = not1(BANE_ABCDxxxx); 27 | /*#p01.BAZE*/ wire BAZE_ABCDxxxx = not1(BELO_xxxxEFGH); 28 | /*#p01.BUTO*/ wire BUTO_xBCDEFGH = nand3(AFEP_AxxxxFGH, ATYP_ABCDxxxx, BAZE_ABCDxxxx); 29 | /*#p01.BELE*/ wire BELE_Axxxxxxx_new = not1(BUTO_xBCDEFGH); 30 | /*#p01.BYJU*/ wire BYJU_Axxxxxxx_new = or2(BELE_Axxxxxxx_new, pins.sys.ATEZ_CLKBADp_new()); 31 | /*#p01.BALY*/ wire BALY_xBCDEFGH_new = not1(BYJU_Axxxxxxx_new); 32 | /*_p01.BOGA*/ wire BOGA_Axxxxxxx_new = not1(BALY_xBCDEFGH_new); 33 | 34 | /*_p01.AFER*/ reg_new.sys_rst.AFER_SYS_RSTp.dff13(BOGA_Axxxxxxx_new, pins.sys.UPOJ_MODE_PRODn_new(), reg_old.sys_rst.ASOL_POR_DONEn.qp_old()); 35 | 36 | /*_p01.UPYF*/ wire UPYF_new = or2(pins.sys.PIN_71_RST.qp_int_new(), pins.sys.UCOB_CLKBADp_new()); 37 | /*_p01.TUBO*/ reg_new.sys_rst.TUBO_WAITINGp.nor_latch(UPYF_new, reg_new.sys_clk.SIG_CPU_CLKREQ.out_new()); 38 | /*_p01.UNUT*/ wire UNUT_POR_TRIGn_new = and2(reg_new.sys_rst.TUBO_WAITINGp.qp_new(), UPOF_DIV15p.qp_new()); 39 | /*_p01.TABA*/ wire TABA_POR_TRIGn_new = or3(pins.sys.UNOR_MODE_DBG2p_new(), pins.sys.UMUT_MODE_DBG1p_new(), UNUT_POR_TRIGn_new); 40 | /*#p01.ALYP*/ wire ALYP_RSTn_new = not1(TABA_POR_TRIGn_new); 41 | /*#p01.AFAR*/ wire AFAR_RSTp_new = nor2(pins.sys.PIN_71_RST.qp_int_new(), ALYP_RSTn_new); 42 | /*_p01.ASOL*/ reg_new.sys_rst.ASOL_POR_DONEn.nor_latch(pins.sys.PIN_71_RST.qp_int_new(), AFAR_RSTp_new); // Schematic wrong, this is a latch. 43 | 44 | /*_SIG_CPU_EXT_CLKGOOD*/ reg_new.sys_rst.SIG_CPU_EXT_CLKGOOD.sig_out(pins.sys.PIN_74_CLK.clkgood_new()); 45 | /*_SIG_CPU_EXT_RESETp */ reg_new.sys_rst.SIG_CPU_EXT_RESETp.sig_out(pins.sys.PIN_71_RST.qp_int_new()); 46 | /*_SIG_CPU_STARTp */ reg_new.sys_rst.SIG_CPU_STARTp.sig_out(TABA_POR_TRIGn_new); 47 | /*_SIG_CPU_INT_RESETp */ reg_new.sys_rst.SIG_CPU_INT_RESETp.sig_out(reg_new.sys_rst.AFER_SYS_RSTp.qp_new()); 48 | 49 | /*#p25.SYCY*/ wire SYCY_MODE_DBG2n_new = not1(pins.sys.UNOR_MODE_DBG2p_new()); 50 | 51 | /*#p01.AVOR*/ wire AVOR_SYS_RSTp = or2(reg_new.sys_rst.AFER_SYS_RSTp.qp_new(), reg_new.sys_rst.ASOL_POR_DONEn.qp_new()); 52 | /*#p01.ALUR*/ wire ALUR_SYS_RSTn = not1(AVOR_SYS_RSTp); 53 | /*#p01.DULA*/ wire DULA_SYS_RSTp = not1(ALUR_SYS_RSTn); /*#p01.CUNU*/ wire CUNU_SYS_RSTn = not1(DULA_SYS_RSTp); 54 | 55 | /*#p25.SOTO*/ reg_new.sys_rst.SOTO_DBG_VRAMp.dff17(SYCY_MODE_DBG2n_new, CUNU_SYS_RSTn, reg_old.sys_rst.SOTO_DBG_VRAMp.qn_old()); 56 | } 57 | 58 | //----------------------------------------------------------------------------- 59 | 60 | void GateBoyReset::reset() { 61 | TUBO_WAITINGp.state = 0b00011000; 62 | ASOL_POR_DONEn.state = 0b00011000; 63 | AFER_SYS_RSTp.state = 0b00011000; 64 | SOTO_DBG_VRAMp.state = 0b00011010; 65 | 66 | SIG_CPU_EXT_CLKGOOD.state = 0b00011001; 67 | SIG_CPU_EXT_RESETp.state = 0b00011000; 68 | SIG_CPU_STARTp.state = 0b00011000; 69 | SIG_CPU_INT_RESETp.state = 0b00011000; 70 | } 71 | 72 | ///*_p25.TUTO*/ wire TUTO_VRAM_DBGp() const { return and2(UNOR_MODE_DBG2p_new(), SOTO_DBG_VRAMp.qn_new()); } 73 | 74 | //----------------------------------------------------------------------------- 75 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyReset.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GateBoyLib/Gates.h" 4 | #include "GateBoyLib/Regs.h" 5 | 6 | //----------------------------------------------------------------------------- 7 | 8 | struct GateBoyReset { 9 | void reset(); 10 | 11 | //---------------------------------------- 12 | 13 | /*_p01.TUBO*/ NorLatch TUBO_WAITINGp; // Must be 0 in run mode, otherwise we'd ping SIG_CPU_INT_RESETp when UPOF_DIV_15 changed 14 | /*_p01.ASOL*/ NorLatch ASOL_POR_DONEn; // Schematic wrong, this is a latch. 15 | /*_p01.AFER*/ DFF13 AFER_SYS_RSTp; // AFER should keep clocking even if SIG_CPU_CLKREQ = 0 16 | /*#p25.SOTO*/ DFF17 SOTO_DBG_VRAMp; 17 | 18 | /*_SIG_CPU_EXT_CLKGOOD*/ SigOut SIG_CPU_EXT_CLKGOOD; // top center port PORTC_03: <- chip.CLKIN_A top wire on PAD_XI, 19 | /*_SIG_CPU_EXT_RESETp */ SigOut SIG_CPU_EXT_RESETp; // top center port PORTC_02: <- PIN_71_RST directly connected to the pad 20 | /*_SIG_CPU_STARTp */ SigOut SIG_CPU_STARTp; // top center port PORTC_04: <- P01.CPU_RESET 21 | /*_SIG_CPU_INT_RESETp */ SigOut SIG_CPU_INT_RESETp; // top center port PORTC_01: <- P01.AFER , reset related state 22 | }; 23 | 24 | //----------------------------------------------------------------------------- 25 | 26 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoySerial.h: -------------------------------------------------------------------------------- 1 | /// plait_noparse 2 | #pragma once 3 | #include "GateBoyLib/Regs.h" 4 | 5 | //----------------------------------------------------------------------------- 6 | // FIXME split into smaller structs 7 | 8 | struct GateBoySerial { 9 | void reset(); 10 | 11 | /*#p06.ETAF*/ DFF17 ETAF_SER_RUN; // xxxxxxxH ? 12 | /*#p06.CULY*/ DFF17 CULY_SER_DIR; // AxxxDxxH ? 13 | /*#p06.COTY*/ DFF17 COTY_SER_CLK; // AxxxDxxH ? schematic wrong, clock is UVYN 14 | /*#p06.ELYS*/ DFF17 ELYS_SER_OUT; // AxxxDxxH ? 15 | 16 | /*#p06.CAFA*/ DFF17 CAFA_SER_CNT0; // ???????? 17 | /*#p06.CYLO*/ DFF17 CYLO_SER_CNT1; // ???????? 18 | /*#p06.CYDE*/ DFF17 CYDE_SER_CNT2; // ???????? 19 | /*#p06.CALY*/ DFF17 CALY_SER_CNT3; // ???????? 20 | 21 | /*_p06.CUBA*/ DFF22 CUBA_SER_DATA0; // xxxxExxx 22 | /*_p06.DEGU*/ DFF22 DEGU_SER_DATA1; // xxxxExxx 23 | /*_p06.DYRA*/ DFF22 DYRA_SER_DATA2; // xxxxExxx 24 | /*_p06.DOJO*/ DFF22 DOJO_SER_DATA3; // xxxxExxx 25 | /*_p06.DOVU*/ DFF22 DOVU_SER_DATA4; // xxxxExxx 26 | /*_p06.EJAB*/ DFF22 EJAB_SER_DATA5; // xxxxExxx 27 | /*_p06.EROD*/ DFF22 EROD_SER_DATA6; // xxxxExxx 28 | /*_p06.EDER*/ DFF22 EDER_SER_DATA7; // xxxxExxx 29 | }; 30 | 31 | //----------------------------------------------------------------------------- 32 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoySpriteFetcher.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/GateBoySpriteFetcher.h" 2 | 3 | #include "GateBoyLib/GateBoy.h" 4 | #include "GateBoyLib/Gates.h" 5 | 6 | //----------------------------------------------------------------------------- 7 | 8 | void SpriteFetchCounter::reset() { 9 | TOXE_SFETCH_S0p_evn.state = 0b00011011; 10 | TULY_SFETCH_S1p_evn.state = 0b00011000; 11 | TESE_SFETCH_S2p_evn.state = 0b00011011; 12 | } 13 | 14 | //----------------------------------------------------------------------------- 15 | 16 | void SpriteFetchControl::reset() { 17 | WUTY_SFETCH_DONE_TRIGp_odd.state = 0b00011000; 18 | TEXY_SFETCHINGp_evn.state = 0b00011000; 19 | TAKA_SFETCH_RUNNINGp_evn.state = 0b00011000; 20 | SOBU_SFETCH_REQp_evn.state = 0b00011010; 21 | SUDA_SFETCH_REQp_odd.state = 0b00011000; 22 | TYFO_SFETCH_S0p_D1_odd.state = 0b00011001; 23 | TOBU_SFETCH_S1p_D2_evn.state = 0b00011010; 24 | VONU_SFETCH_S1p_D4_evn.state = 0b00011010; 25 | SEBA_SFETCH_S1p_D5_odd.state = 0b00011000; 26 | } 27 | 28 | //----------------------------------------------------------------------------- 29 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoySpriteFetcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct SpriteFetchCounter { 7 | void reset(); 8 | 9 | /*#p29.TOXE*/ DFF17 TOXE_SFETCH_S0p_evn; // ABxDxFxH Beginning of line, reset on A. During line, ticks and resets on odd. 10 | /*#p29.TULY*/ DFF17 TULY_SFETCH_S1p_evn; // ABxDxFxH Beginning of line, reset on A. During line, ticks and resets on odd. 11 | /*#p29.TESE*/ DFF17 TESE_SFETCH_S2p_evn; // ABxDxFxH Beginning of line, reset on A. During line, ticks and resets on odd. 12 | }; 13 | 14 | //----------------------------------------------------------------------------- 15 | 16 | struct SpriteFetchControl { 17 | void reset(); 18 | 19 | /*_p29.WUTY*/ Gate WUTY_SFETCH_DONE_TRIGp_odd; // new used 20 | /*_p29.TEXY*/ Gate TEXY_SFETCHINGp_evn; // new used 21 | /*_p27.TAKA*/ NandLatch TAKA_SFETCH_RUNNINGp_evn; // ABCDEFGH Set on odd, cleared on even 22 | /*_p27.SOBU*/ DFF17 SOBU_SFETCH_REQp_evn; // xBxDxFxH 23 | /*_p27.SUDA*/ DFF17 SUDA_SFETCH_REQp_odd; // AxBxExGx 24 | 25 | /*#p29.TYFO*/ DFF17 TYFO_SFETCH_S0p_D1_odd; // AxCxExGx 26 | /*#p29.TOBU*/ DFF17 TOBU_SFETCH_S1p_D2_evn; // xBxDxFxH 27 | /*#p29.VONU*/ DFF17 VONU_SFETCH_S1p_D4_evn; // xBxDxFxH 28 | /*#p29.SEBA*/ DFF17 SEBA_SFETCH_S1p_D5_odd; // AxCxExGx 29 | }; 30 | 31 | //----------------------------------------------------------------------------- 32 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoySpriteScanner.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/GateBoySpriteScanner.h" 2 | 3 | #include "GateBoyLib/Utils.h" 4 | 5 | //----------------------------------------------------------------------------- 6 | 7 | void SpriteScanner::reset() { 8 | FETO_SCAN_DONEp.state = 0b00011001; 9 | AVAP_SCAN_DONE_tp_odd.state = 0b00011000; 10 | BESU_SCAN_DONEn_odd.state = 0b00011000; 11 | CENO_SCAN_DONEn_odd.state = 0b00011000; 12 | DEZY_INC_COUNTn_odd.state = 0b00011001; 13 | BYBA_SCAN_DONEp_odd.state = 0b00011001; 14 | DOBA_SCAN_DONEp_evn.state = 0b00011011; 15 | } 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | void ScanCounter::reset() { 20 | YFEL_SCAN0_odd.state = 0b00011011; 21 | WEWY_SCAN1_odd.state = 0b00011001; 22 | GOSO_SCAN2_odd.state = 0b00011001; 23 | ELYN_SCAN3_odd.state = 0b00011000; 24 | FAHA_SCAN4_odd.state = 0b00011010; 25 | FONY_SCAN5_odd.state = 0b00011011; 26 | } 27 | 28 | //----------------------------------------------------------------------------- 29 | 30 | void SpriteIndex::reset() { 31 | XADU_SPRITE_IDX0p_odd.state = 0b00011000; 32 | XEDY_SPRITE_IDX1p_odd.state = 0b00011000; 33 | ZUZE_SPRITE_IDX2p_odd.state = 0b00011001; 34 | XOBE_SPRITE_IDX3p_odd.state = 0b00011000; 35 | YDUF_SPRITE_IDX4p_odd.state = 0b00011001; 36 | XECU_SPRITE_IDX5p_odd.state = 0b00011000; 37 | } 38 | 39 | //----------------------------------------------------------------------------- 40 | 41 | void SpriteCounter::reset() { 42 | BESE_SPRITE_COUNT0_odd.state = BIT_OLD | BIT_DRIVEN | BIT_CLOCK; 43 | CUXY_SPRITE_COUNT1_odd.state = BIT_OLD | BIT_DRIVEN | BIT_CLOCK; 44 | BEGO_SPRITE_COUNT2_odd.state = BIT_OLD | BIT_DRIVEN | BIT_CLOCK; 45 | DYBE_SPRITE_COUNT3_odd.state = BIT_OLD | BIT_DRIVEN | BIT_CLOCK; 46 | } 47 | 48 | //----------------------------------------------------------------------------- 49 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoySpriteScanner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct SpriteScanner { 7 | void reset(); 8 | 9 | /*#p28.FETO*/ Gate FETO_SCAN_DONEp; 10 | /*#p29.AVAP*/ Gate AVAP_SCAN_DONE_tp_odd; 11 | /*#p28.BESU*/ NorLatch BESU_SCAN_DONEn_odd; 12 | /*#p29.CENO*/ DFF17 CENO_SCAN_DONEn_odd; 13 | /*_p29.DEZY*/ DFF17 DEZY_INC_COUNTn_odd; 14 | /*#p29.BYBA*/ DFF17 BYBA_SCAN_DONEp_odd; 15 | /*#p29.DOBA*/ DFF17 DOBA_SCAN_DONEp_evn; 16 | }; 17 | 18 | //----------------------------------------------------------------------------- 19 | // Scan counter tracks which of the 40 sprites in OAM we're scanning. 20 | 21 | struct ScanCounter { 22 | void reset(); 23 | 24 | /*#p28.YFEL*/ DFF17 YFEL_SCAN0_odd; 25 | /*_p28.WEWY*/ DFF17 WEWY_SCAN1_odd; 26 | /*_p28.GOSO*/ DFF17 GOSO_SCAN2_odd; 27 | /*_p28.ELYN*/ DFF17 ELYN_SCAN3_odd; 28 | /*_p28.FAHA*/ DFF17 FAHA_SCAN4_odd; 29 | /*_p28.FONY*/ DFF17 FONY_SCAN5_odd; 30 | }; 31 | 32 | //----------------------------------------------------------------------------- 33 | // Sprite index selects which store to write 34 | 35 | struct SpriteIndex { 36 | void reset(); 37 | 38 | /*_p30.XADU*/ DFF13 XADU_SPRITE_IDX0p_odd; 39 | /*_p30.XEDY*/ DFF13 XEDY_SPRITE_IDX1p_odd; 40 | /*_p30.ZUZE*/ DFF13 ZUZE_SPRITE_IDX2p_odd; 41 | /*_p30.XOBE*/ DFF13 XOBE_SPRITE_IDX3p_odd; 42 | /*_p30.YDUF*/ DFF13 YDUF_SPRITE_IDX4p_odd; 43 | /*_p30.XECU*/ DFF13 XECU_SPRITE_IDX5p_odd; 44 | }; 45 | 46 | //----------------------------------------------------------------------------- 47 | // Sprite counter tracks how many sprites have been selected for this line. 48 | 49 | struct SpriteCounter { 50 | void reset(); 51 | 52 | /*_p29.BESE*/ DFF17 BESE_SPRITE_COUNT0_odd; // AxxxExxx 53 | /*_p29.CUXY*/ DFF17 CUXY_SPRITE_COUNT1_odd; // AxxxExxx 54 | /*_p29.BEGO*/ DFF17 BEGO_SPRITE_COUNT2_odd; // AxxxExxx 55 | /*_p29.DYBE*/ DFF17 DYBE_SPRITE_COUNT3_odd; // AxxxExxx 56 | }; 57 | 58 | //----------------------------------------------------------------------------- 59 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyThread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "metrolib/gameboy/Assembler.h" 4 | #include "metrolib/core/Types.h" 5 | #include "metrolib/core/StateStack.h" 6 | #include "metrolib/core/File.h" 7 | 8 | #include "GateBoyLib/GateBoyPair.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | //-------------------------------------------------------------------------------- 17 | 18 | struct AtomicFlags { 19 | 20 | void set(int mask) { 21 | flags |= mask; 22 | cond.notify_all(); 23 | } 24 | 25 | void clear(int mask) { 26 | flags &= ~mask; 27 | cond.notify_all(); 28 | } 29 | 30 | bool test(int mask) const { 31 | return (flags & mask) == mask; 32 | } 33 | 34 | bool test_any(int mask) const { 35 | return (flags & mask) != 0; 36 | } 37 | 38 | bool test_none(int mask) const { 39 | return (flags & mask) == 0; 40 | } 41 | 42 | /* 43 | void wait(int mask) { 44 | std::unique_lock lock(mut); 45 | cond.wait(lock, [this, mask] { return test(mask); }); 46 | } 47 | 48 | void wait_any(int mask) { 49 | std::unique_lock lock(mut); 50 | cond.wait(lock, [this, mask] { return test_any(mask); }); 51 | } 52 | 53 | void wait_none(int mask) { 54 | std::unique_lock lock(mut); 55 | cond.wait(lock, [this, mask] { return test_none(mask); }); 56 | } 57 | */ 58 | 59 | std::atomic_int flags; 60 | std::mutex mut; 61 | std::condition_variable cond; 62 | }; 63 | 64 | //-------------------------------------------------------------------------------- 65 | 66 | struct GateBoyThread { 67 | 68 | GateBoyThread(IGateBoy* prototype); 69 | 70 | void start(); 71 | void stop(); 72 | void pause(); 73 | void resume(); 74 | 75 | void poweron(bool fastboot); 76 | void reset(); 77 | 78 | void add_steps(int64_t steps); 79 | void run_to(uint64_t phase); 80 | void clear_steps(); 81 | int64_t get_steps() const; 82 | 83 | void rewind(int steps); 84 | 85 | void load_raw_dump(BlobStream& bs); 86 | void save_raw_dump(BlobStream& bs); 87 | void load_flat_dump(BlobStream& bs); 88 | 89 | void load_cart_blob(blob& bs); 90 | void load_program(const char* source); 91 | void load_bootrom(const char* source); 92 | void set_buttons(uint8_t buttons); 93 | 94 | const int REQ_PAUSE = 0b0001; 95 | const int ACK_PAUSE = 0b0010; 96 | const int REQ_EXIT = 0b0100; 97 | const int ACK_EXIT = 0b1000; 98 | 99 | bool sim_paused() const; 100 | bool has_work() const; 101 | blob& get_cart(); 102 | const blob& get_cart() const; 103 | 104 | void dump(Dumper& d); 105 | 106 | StatePointerStack gb; 107 | 108 | void run_steps(); 109 | 110 | void thread_main(); 111 | 112 | void next_phase(); 113 | 114 | void run_sync(); 115 | void run_normal(); 116 | void panic(); 117 | 118 | private: 119 | 120 | std::thread* main = nullptr; 121 | AtomicFlags sync; 122 | 123 | blob cart_blob; 124 | std::atomic_int64_t step_count = 0; 125 | 126 | double sim_time = 0; 127 | double old_sim_time = 0; 128 | uint64_t prev_phase_total = 0; 129 | uint64_t next_phase_total = 0; 130 | double phase_rate_smooth = 0; 131 | 132 | }; 133 | 134 | //-------------------------------------------------------------------------------- 135 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyTileFetcher.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/GateBoyTileFetcher.h" 2 | 3 | #include "GateBoyLib/GateBoy.h" 4 | #include "GateBoyLib/Gates.h" 5 | 6 | //----------------------------------------------------------------------------- 7 | 8 | void TileFetchCounter::reset() { 9 | LAXU_BFETCH_S0p_odd.state = 0b00011011; 10 | MESU_BFETCH_S1p_odd.state = 0b00011000; 11 | NYVA_BFETCH_S2p_odd.state = 0b00011011; 12 | } 13 | 14 | //----------------------------------------------------------------------------- 15 | 16 | void TileFetchControl::reset() { 17 | LYRY_BFETCH_DONEp_odd.state = 0b00011001; 18 | POKY_PRELOAD_LATCHp_evn.state = 0b00011000; 19 | LONY_TFETCHINGp.state = 0b00011000; 20 | LOVY_TFETCH_DONEp.state = 0b00011001; 21 | NYKA_FETCH_DONEp_evn.state = 0b00011010; 22 | PORY_FETCH_DONEp_odd.state = 0b00011000; 23 | PYGO_FETCH_DONEp_evn.state = 0b00011010; 24 | LYZU_BFETCH_S0p_D1.state = 0b00011010; 25 | } 26 | 27 | //----------------------------------------------------------------------------- 28 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyTileFetcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct TileFetchCounter { 7 | void reset(); 8 | 9 | /*_p27.LAXU*/ DFF17 LAXU_BFETCH_S0p_odd; // AxCxExGx 10 | /*_p27.MESU*/ DFF17 MESU_BFETCH_S1p_odd; // AxCxExGx 11 | /*_p27.NYVA*/ DFF17 NYVA_BFETCH_S2p_odd; // AxCxExGx 12 | }; 13 | 14 | //----------------------------------------------------------------------------- 15 | 16 | struct TileFetchControl { 17 | void reset(); 18 | 19 | /*_p27.LYRY*/ Gate LYRY_BFETCH_DONEp_odd; // old used 20 | /*_p24.POKY*/ NorLatch POKY_PRELOAD_LATCHp_evn; // xBxDxFxG 21 | /*_p27.LONY*/ NandLatch LONY_TFETCHINGp; // Usually changes on even. Changes on odd phase at end of line if we're in a window? 22 | 23 | /*_p27.LOVY*/ DFF17 LOVY_TFETCH_DONEp; // AxCxExGx 24 | /*_p24.NYKA*/ DFF17 NYKA_FETCH_DONEp_evn; // Usually changes on even. Changes on odd phase at end of line if we're in a window? 25 | /*_p24.PORY*/ DFF17 PORY_FETCH_DONEp_odd; // Usually changes on even. Changes on odd phase at end of line if we're in a window? 26 | /*_p24.PYGO*/ DFF17 PYGO_FETCH_DONEp_evn; // xBxDxFxG 27 | 28 | /*_p27.LYZU*/ DFF17 LYZU_BFETCH_S0p_D1; // xBxDxFxG 29 | }; 30 | 31 | //----------------------------------------------------------------------------- 32 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyTimer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct RegDIV { 7 | void reset(); 8 | 9 | void force_set_div(uint16_t div); 10 | 11 | /*_p01.UKUP*/ DFF17 UKUP_DIV00p; // AxxxExxx 12 | /*_p01.UFOR*/ DFF17 UFOR_DIV01p; // AxxxExxx 13 | /*_p01.UNER*/ DFF17 UNER_DIV02p; // AxxxExxx 14 | /*#p01.TERO*/ DFF17 TERO_DIV03p; // AxxxExxx 15 | /*_p01.UNYK*/ DFF17 UNYK_DIV04p; // AxxxExxx 16 | /*_p01.TAMA*/ DFF17 TAMA_DIV05p; // AxxxExxx 17 | /*_p01.UGOT*/ DFF17 UGOT_DIV06p; // AxxxExxx 18 | /*_p01.TULU*/ DFF17 TULU_DIV07p; // AxxxExxx 19 | /*_p01.TUGO*/ DFF17 TUGO_DIV08p; // AxxxExxx 20 | /*_p01.TOFE*/ DFF17 TOFE_DIV09p; // AxxxExxx 21 | /*_p01.TERU*/ DFF17 TERU_DIV10p; // AxxxExxx 22 | /*_p01.SOLA*/ DFF17 SOLA_DIV11p; // AxxxExxx 23 | /*_p01.SUBU*/ DFF17 SUBU_DIV12p; // AxxxExxx 24 | /*_p01.TEKA*/ DFF17 TEKA_DIV13p; // AxxxExxx 25 | /*_p01.UKET*/ DFF17 UKET_DIV14p; // AxxxExxx 26 | /*_p01.UPOF*/ DFF17 UPOF_DIV15p; // AxxxExxx 27 | }; 28 | 29 | //----------------------------------------------------------------------------- 30 | 31 | struct RegTIMA { 32 | void reset(); 33 | 34 | void force_set_tima(uint8_t tima); 35 | 36 | /*#p03.REGA*/ DFF20 REGA_TIMA0p; // AxxxExxH 37 | /*#p03.POVY*/ DFF20 POVY_TIMA1p; // AxxxExxH 38 | /*#p03.PERU*/ DFF20 PERU_TIMA2p; // AxxxExxH 39 | /*#p03.RATE*/ DFF20 RATE_TIMA3p; // AxxxExxH 40 | /*#p03.RUBY*/ DFF20 RUBY_TIMA4p; // AxxxExxH 41 | /*#p03.RAGE*/ DFF20 RAGE_TIMA5p; // AxxxExxH 42 | /*#p03.PEDA*/ DFF20 PEDA_TIMA6p; // AxxxExxH 43 | /*#p03.NUGA*/ DFF20 NUGA_TIMA7p; // AxxxExxH 44 | }; 45 | 46 | //----------------------------------------------------------------------------- 47 | 48 | struct RegTMA { 49 | void reset(); 50 | 51 | /*_p03.SABU*/ DFF17 SABU_TMA0p; // xxxxxxxH 52 | /*_p03.NYKE*/ DFF17 NYKE_TMA1p; // xxxxxxxH 53 | /*_p03.MURU*/ DFF17 MURU_TMA2p; // xxxxxxxH 54 | /*_p03.TYVA*/ DFF17 TYVA_TMA3p; // xxxxxxxH 55 | /*_p03.TYRU*/ DFF17 TYRU_TMA4p; // xxxxxxxH 56 | /*_p03.SUFY*/ DFF17 SUFY_TMA5p; // xxxxxxxH 57 | /*_p03.PETO*/ DFF17 PETO_TMA6p; // xxxxxxxH 58 | /*_p03.SETA*/ DFF17 SETA_TMA7p; // xxxxxxxH 59 | }; 60 | 61 | //----------------------------------------------------------------------------- 62 | 63 | struct RegTAC { 64 | void reset(); 65 | 66 | /*_p03.SOPU*/ DFF17 SOPU_TAC0p; // xxxxxxxH 67 | /*_p03.SAMY*/ DFF17 SAMY_TAC1p; // xxxxxxxH 68 | /*_p03.SABO*/ DFF17 SABO_TAC2p; // xxxxxxxH 69 | }; 70 | 71 | //----------------------------------------------------------------------------- 72 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyVramBus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct VramABusLo { 7 | void reset(); 8 | 9 | /*_BUS_VRAM_A00n*/ Bus BUS_VRAM_A00n; 10 | /*_BUS_VRAM_A01n*/ Bus BUS_VRAM_A01n; 11 | /*_BUS_VRAM_A02n*/ Bus BUS_VRAM_A02n; 12 | /*_BUS_VRAM_A03n*/ Bus BUS_VRAM_A03n; 13 | /*_BUS_VRAM_A04n*/ Bus BUS_VRAM_A04n; 14 | /*_BUS_VRAM_A05n*/ Bus BUS_VRAM_A05n; 15 | /*_BUS_VRAM_A06n*/ Bus BUS_VRAM_A06n; 16 | /*_BUS_VRAM_A07n*/ Bus BUS_VRAM_A07n; 17 | }; 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | struct VramABusHi { 22 | void reset(); 23 | 24 | /*_BUS_VRAM_A08n*/ Bus BUS_VRAM_A08n; 25 | /*_BUS_VRAM_A09n*/ Bus BUS_VRAM_A09n; 26 | /*_BUS_VRAM_A10n*/ Bus BUS_VRAM_A10n; 27 | /*_BUS_VRAM_A11n*/ Bus BUS_VRAM_A11n; 28 | /*_BUS_VRAM_A12n*/ Bus BUS_VRAM_A12n; 29 | }; 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | struct VramDBus { 34 | void reset(); 35 | 36 | /*_BUS_VRAM_D00p*/ Bus BUS_VRAM_D00p; 37 | /*_BUS_VRAM_D01p*/ Bus BUS_VRAM_D01p; 38 | /*_BUS_VRAM_D02p*/ Bus BUS_VRAM_D02p; 39 | /*_BUS_VRAM_D03p*/ Bus BUS_VRAM_D03p; 40 | /*_BUS_VRAM_D04p*/ Bus BUS_VRAM_D04p; 41 | /*_BUS_VRAM_D05p*/ Bus BUS_VRAM_D05p; 42 | /*_BUS_VRAM_D06p*/ Bus BUS_VRAM_D06p; 43 | /*_BUS_VRAM_D07p*/ Bus BUS_VRAM_D07p; 44 | }; 45 | 46 | //----------------------------------------------------------------------------- 47 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyWaveBus.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyWaveBus.h" 2 | 3 | #include 4 | 5 | void GateBoyWaveBus::reset() { 6 | memset(this, BIT_OLD | BIT_PULLED | 1, sizeof(*this)); 7 | } 8 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyWaveBus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GateBoyLib/Regs.h" 4 | 5 | //----------------------------------------------------------------------------- 6 | 7 | struct GateBoyWaveBus { 8 | void reset(); 9 | 10 | #if 0 11 | /*_BUS_WAVE_A00*/ Bus BUS_WAVE_A00; 12 | /*_BUS_WAVE_A01*/ Bus BUS_WAVE_A01; 13 | /*_BUS_WAVE_A02*/ Bus BUS_WAVE_A02; 14 | /*_BUS_WAVE_A03*/ Bus BUS_WAVE_A03; 15 | #endif 16 | 17 | /*_BUS_WAVE_D00*/ Bus BUS_WAVE_D00; 18 | /*_BUS_WAVE_D01*/ Bus BUS_WAVE_D01; 19 | /*_BUS_WAVE_D02*/ Bus BUS_WAVE_D02; 20 | /*_BUS_WAVE_D03*/ Bus BUS_WAVE_D03; 21 | /*_BUS_WAVE_D04*/ Bus BUS_WAVE_D04; 22 | /*_BUS_WAVE_D05*/ Bus BUS_WAVE_D05; 23 | /*_BUS_WAVE_D06*/ Bus BUS_WAVE_D06; 24 | /*_BUS_WAVE_D07*/ Bus BUS_WAVE_D07; 25 | }; 26 | 27 | //----------------------------------------------------------------------------- 28 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyZramBus.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/GateBoyZramBus.h" 2 | 3 | #include "GateBoyLib/GateBoy.h" 4 | #include "GateBoyLib/Gates.h" 5 | 6 | // ZRAM control signals are 7 | // clk_reg.SIG_CPU_BUKE_AxxxxxGH 8 | // TEDO_CPU_RDp(); 9 | // TAPU_CPU_WRp_xxxxEFGx() 10 | // SYKE_FF00_FFFFp 11 | // and there's somes gates WUTA/WOLY/WALE that do the check for FFXX && !FFFF 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | void tock_zram_gates(const GateBoyState& reg_old, GateBoyState& reg_new, uint8_t zero_ram[128]) { 16 | auto addr = bit_pack(reg_new.cpu_abus); 17 | wire CSp = (addr >= 0xFF80) && (addr <= 0xFFFE); 18 | 19 | if (bit0(reg_old.zram_bus.clk_old.out_old() & ~reg_new.cpu_signals.TAPU_CPU_WRp.out_new() & CSp)) { 20 | zero_ram[addr & 0x007F] = (uint8_t)bit_pack(reg_old.cpu_dbus); 21 | } 22 | reg_new.zram_bus.clk_old <<= reg_new.cpu_signals.TAPU_CPU_WRp.out_new(); 23 | 24 | uint8_t data = zero_ram[addr & 0x007F]; 25 | 26 | triwire tri0 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 0)); 27 | triwire tri1 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 1)); 28 | triwire tri2 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 2)); 29 | triwire tri3 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 3)); 30 | triwire tri4 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 4)); 31 | triwire tri5 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 5)); 32 | triwire tri6 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 6)); 33 | triwire tri7 = tri_pp(CSp && bit0(reg_new.cpu_signals.TEDO_CPU_RDp.out_new()), bit(data, 7)); 34 | 35 | reg_new.cpu_dbus.BUS_CPU_D00p.tri_bus(tri0); 36 | reg_new.cpu_dbus.BUS_CPU_D01p.tri_bus(tri1); 37 | reg_new.cpu_dbus.BUS_CPU_D02p.tri_bus(tri2); 38 | reg_new.cpu_dbus.BUS_CPU_D03p.tri_bus(tri3); 39 | reg_new.cpu_dbus.BUS_CPU_D04p.tri_bus(tri4); 40 | reg_new.cpu_dbus.BUS_CPU_D05p.tri_bus(tri5); 41 | reg_new.cpu_dbus.BUS_CPU_D06p.tri_bus(tri6); 42 | reg_new.cpu_dbus.BUS_CPU_D07p.tri_bus(tri7); 43 | } 44 | 45 | //----------------------------------------------------------------------------- 46 | 47 | void GateBoyZram::reset() { 48 | } 49 | 50 | //----------------------------------------------------------------------------- 51 | -------------------------------------------------------------------------------- /src/GateBoyLib/GateBoyZramBus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GateBoyLib/Regs.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | struct GateBoyZram { 7 | void reset(); 8 | 9 | Gate clk_old; 10 | }; 11 | 12 | //----------------------------------------------------------------------------- 13 | -------------------------------------------------------------------------------- /src/GateBoyLib/Gates.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/Gates.h" 2 | -------------------------------------------------------------------------------- /src/GateBoyLib/Gates.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "metrolib/core/Types.h" 3 | #include "metrolib/core/Tests.h" 4 | 5 | #include "GateBoyLib/Utils.h" 6 | 7 | //----------------------------------------------------------------------------- 8 | 9 | /*__attribute__((always_inline))*/ inline wire not1(wire a) { return ~a; } 10 | 11 | inline wire and2(wire a, wire b) { return a & b; } 12 | inline wire and3(wire a, wire b, wire c) { return (a & b & c); } 13 | 14 | inline wire and4(wire a, wire b, wire c, wire d) { return (a & b & c & d); } 15 | inline wire and5(wire a, wire b, wire c, wire d, wire e) { return (a & b & c & d & e); } 16 | inline wire and6(wire a, wire b, wire c, wire d, wire e, wire f) { return (a & b & c & d & e & f); } 17 | inline wire and7(wire a, wire b, wire c, wire d, wire e, wire f, wire g) { return (a & b & c & d & e & f & g); } 18 | 19 | inline wire or2(wire a, wire b) { return a | b; } 20 | inline wire or3(wire a, wire b, wire c) { return (a | b | c); } 21 | inline wire or4(wire a, wire b, wire c, wire d) { return (a | b | c | d); } 22 | inline wire or5(wire a, wire b, wire c, wire d, wire e) { return (a | b | c | d | e); } 23 | inline wire or6(wire a, wire b, wire c, wire d, wire e, wire f) { return a | b | c | d | e | f; } 24 | 25 | inline wire xor2(wire a, wire b) { return a ^ b; } 26 | 27 | // XNOR01 >> D 28 | // XNOR02 << A 29 | // XNOR03 << B 30 | // XNOR04 31 | // XNOR05 32 | // XNOR06 33 | inline wire xnor2(wire a, wire b) { return ~(a ^ b); } 34 | 35 | inline wire nor2(wire a, wire b) { return ~(a | b); } 36 | inline wire nor3(wire a, wire b, wire c) { return ~(a | b | c); } 37 | inline wire nor4(wire a, wire b, wire c, wire d) { return ~(a | b | c | d); } 38 | inline wire nor5(wire a, wire b, wire c, wire d, wire e) { return ~(a | b | c | d | e); } 39 | inline wire nor6(wire a, wire b, wire c, wire d, wire e, wire f) { return ~(a | b | c | d | e | f); } 40 | inline wire nor8(wire a, wire b, wire c, wire d, wire e, wire f, wire g, wire h) { return ~(a | b | c | d | e | f | g | h); } 41 | 42 | inline wire nand2(wire a, wire b) { return ~(a & b); } 43 | inline wire nand3(wire a, wire b, wire c) { return ~(a & b & c); } 44 | inline wire nand4(wire a, wire b, wire c, wire d) { return ~(a & b & c & d); } 45 | inline wire nand5(wire a, wire b, wire c, wire d, wire e) { return ~(a & b & c & d & e); } 46 | inline wire nand6(wire a, wire b, wire c, wire d, wire e, wire f) { return ~(a & b & c & d & e & f); } 47 | inline wire nand7(wire a, wire b, wire c, wire d, wire e, wire f, wire g) { return ~(a & b & c & d & e & f & g); } 48 | 49 | inline wire and_or3(wire a, wire b, wire c) { return (a & b) | c; } 50 | inline wire or_and3(wire a, wire b, wire c) { return (a | b) & c; } 51 | 52 | inline wire not_or_and3(wire a, wire b, wire c) { return ~or_and3(a, b, c); } 53 | 54 | //----------------------------------------------------------------------------- 55 | 56 | // Six-rung mux cells are _non_inverting_. m = 1 selects input A 57 | inline wire mux2p(wire m, wire a, wire b) { 58 | return bit0(m) ? a : b; 59 | } 60 | 61 | // Five-rung mux cells are _inverting_. m = 1 selects input A 62 | inline wire mux2n(wire m, wire a, wire b) { 63 | return ~(bit0(m) ? a : b); 64 | } 65 | 66 | inline wire amux2(wire a0, wire b0, wire a1, wire b1) { 67 | return (b0 & a0) | (b1 & a1); 68 | } 69 | 70 | inline wire amux3(wire a0, wire b0, wire a1, wire b1, wire a2, wire b2) { 71 | return (b0 & a0) | (b1 & a1) | (b2 & a2); 72 | } 73 | 74 | inline wire amux4(wire a0, wire b0, wire a1, wire b1, wire a2, wire b2, wire a3, wire b3) { 75 | return (b0 & a0) | (b1 & a1) | (b2 & a2) | (b3 & a3); 76 | } 77 | 78 | inline wire amux6(wire a0, wire b0, wire a1, wire b1, wire a2, wire b2, wire a3, wire b3, wire a4, wire b4, wire a5, wire b5) { 79 | return (b0 & a0) | (b1 & a1) | (b2 & a2) | (b3 & a3) | (b4 & a4) | (b5 & a5); 80 | } 81 | 82 | //----------------------------------------------------------------------------- 83 | -------------------------------------------------------------------------------- /src/GateBoyLib/IGateBoy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "metrolib/core/Blobs.h" 3 | #include "metrolib/core/Result.h" 4 | 5 | struct GateBoyCpu; 6 | struct GateBoyMem; 7 | struct GateBoyState; 8 | struct GateBoySys; 9 | struct GateBoyPins; 10 | struct Probes; 11 | struct BlobStream; 12 | 13 | class IGateBoy { 14 | public: 15 | virtual ~IGateBoy() {} 16 | 17 | virtual IGateBoy* get_a() = 0; 18 | virtual IGateBoy* get_b() = 0; 19 | 20 | virtual const char* get_id() const = 0; 21 | 22 | virtual GBResult poweron(bool fastboot) = 0; 23 | virtual GBResult reset() = 0; 24 | 25 | virtual IGateBoy* clone() const = 0; 26 | virtual int size_bytes() = 0; 27 | 28 | virtual GBResult get_flags() const = 0; 29 | 30 | virtual GBResult load_bootrom(const uint8_t* data, int size) = 0; 31 | virtual GBResult load_raw_dump(BlobStream& dump_in) = 0; 32 | virtual GBResult save_raw_dump(BlobStream& dump_out) const = 0; 33 | 34 | virtual GBResult peek(int addr) const = 0; 35 | virtual GBResult poke(int addr, uint8_t data_in) = 0; 36 | 37 | virtual GBResult dbg_req (uint16_t addr, uint8_t data, bool write) = 0; 38 | virtual GBResult dbg_read (const blob& cart_blob, int addr) = 0; 39 | virtual GBResult dbg_write(const blob& cart_blob, int addr, uint8_t data) = 0; 40 | 41 | virtual GBResult run_phases(const blob& cart_blob, int phase_count) = 0; 42 | virtual GBResult next_phase(const blob& cart_blob) = 0; 43 | virtual GBResult run_to(const blob& cart_blob, int phase) = 0; 44 | 45 | virtual GBResult set_buttons(uint8_t buttons) = 0; 46 | virtual GBResult set_cpu_en(bool enabled) { return GBResult::ok(); }; 47 | 48 | virtual const GateBoyCpu& get_cpu() const = 0; 49 | virtual const GateBoyMem& get_mem() const = 0; 50 | virtual const GateBoyState& get_state() const = 0; 51 | virtual const GateBoySys& get_sys() const = 0; 52 | virtual const GateBoyPins& get_pins() const = 0; 53 | virtual const Probes& get_probes() const = 0; 54 | 55 | virtual void get_flat_blob(const blob& cart_blob, int addr, int size, uint8_t* out) const = 0; 56 | }; 57 | -------------------------------------------------------------------------------- /src/GateBoyLib/LogicBoyReset.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #if 0 5 | 6 | struct LogicBoyReset { 7 | void poweron() { 8 | memset(this, 0, sizeof(*this)); 9 | } 10 | 11 | void reset() { 12 | TUBO_WAITINGp = 0; 13 | ASOL_POR_DONEn = 0; 14 | AFER_SYS_RSTp = 0; 15 | SOTO_DBG_VRAMp = 0; 16 | 17 | SIG_CPU_EXT_CLKGOOD = 1; 18 | SIG_CPU_EXT_RESETp = 0; 19 | SIG_CPU_STARTp = 0; 20 | SIG_CPU_INT_RESETp = 0; 21 | } 22 | 23 | uint8_t TUBO_WAITINGp; 24 | uint8_t ASOL_POR_DONEn; 25 | uint8_t AFER_SYS_RSTp; 26 | uint8_t SOTO_DBG_VRAMp; 27 | 28 | uint8_t SIG_CPU_EXT_CLKGOOD; 29 | uint8_t SIG_CPU_EXT_RESETp; 30 | uint8_t SIG_CPU_STARTp; 31 | uint8_t SIG_CPU_INT_RESETp; 32 | }; 33 | 34 | #endif -------------------------------------------------------------------------------- /src/GateBoyLib/Probe.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/Probe.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "GateBoyLib/Gates.h" 8 | #include "GateBoyLib/Regs.h" 9 | #include "metrolib/core/Dumper.h" 10 | 11 | #pragma warning(disable: 4996) 12 | 13 | thread_local Probes* thread_probes = nullptr; 14 | 15 | static char cp_ext(uint8_t state) { 16 | if ((state & (BIT_DRIVEN | BIT_PULLED)) == BIT_DRIVEN) return bit0(state) ? '0' : '1'; 17 | if ((state & (BIT_DRIVEN | BIT_PULLED)) == BIT_PULLED) return bit0(state) ? 'v' : '^'; 18 | return 'X'; 19 | } 20 | 21 | Probes::Probes() { 22 | reset(); 23 | } 24 | 25 | void Probes::reset() { 26 | phase_cursor = 0; 27 | channel_cursor = 0; 28 | for (int i = 0; i < channel_count; i++) { 29 | memset(names[i], 0, 32); 30 | sprintf(names[i], "", i); 31 | } 32 | 33 | memset(phase_samples, '_', channel_count * sample_count); 34 | memset(phase_tags, '_', sample_count); 35 | } 36 | 37 | void Probes::probe_wire(const char* signal_name, char s) { 38 | int index = channel_cursor++; 39 | if (index >= channel_count) return; 40 | 41 | strcpy(names[index], signal_name); 42 | if (s <= 1) { 43 | phase_samples[index][phase_cursor] = s + 30; //? '#' : '_'; 44 | } 45 | else { 46 | phase_samples[index][phase_cursor] = cp_ext(s); 47 | } 48 | } 49 | 50 | void Probes::begin_pass(int phase_tag) { 51 | CHECK_P(thread_probes == nullptr); 52 | thread_probes = this; 53 | phase_cursor = (phase_cursor + 1) % sample_count; 54 | phase_tags[phase_cursor] = phase_tag; 55 | channel_cursor = 0; 56 | } 57 | 58 | void Probes::end_pass() { 59 | CHECK_P(thread_probes == this); 60 | thread_probes = nullptr; 61 | } 62 | 63 | void Probes::dump(Dumper& d) const { 64 | for (int y = 0; y < channel_count; y++) { 65 | 66 | //d("\001%02d %-22s : ", y, names[y]); 67 | d("\001%-22s : ", names[y]); 68 | auto s = phase_samples[y]; 69 | for (int x = 0; x < sample_count; x++) { 70 | int idx = (phase_cursor + x + 1) % sample_count; 71 | 72 | d.add_char(phase_tags[idx] == 0 ? '\004' : '\001'); 73 | d.add_char(s[idx]); 74 | } 75 | d.add_char('\001'); 76 | d.add_char('\n'); 77 | } 78 | } 79 | 80 | void probe(const char* signal_name, bool s) { 81 | if (thread_probes) { 82 | thread_probes->probe_wire(signal_name, s); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/GateBoyLib/Probe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "metrolib/core/Types.h" 3 | 4 | struct Dumper; 5 | 6 | struct Probes { 7 | Probes(); 8 | void reset(); 9 | 10 | void probe_wire(const char* signal_name, char s); 11 | void begin_pass(int phase_tag); 12 | void end_pass(); 13 | 14 | void dump(Dumper& d) const; 15 | 16 | static const int channel_count = 48; 17 | static const int sample_count = 40; 18 | 19 | int phase_cursor; 20 | int channel_cursor; 21 | char names[channel_count][sample_count]; 22 | char phase_samples[channel_count][sample_count]; 23 | int phase_tags[sample_count]; 24 | }; 25 | 26 | void probe(const char* signal_name, bool s); 27 | -------------------------------------------------------------------------------- /src/GateBoyLib/Regs.cpp: -------------------------------------------------------------------------------- 1 | #include "GateBoyLib/Regs.h" -------------------------------------------------------------------------------- /src/GateBoyTests/GateBoyTests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "metrolib/core/File.h" 4 | #include "metrolib/core/Tests.h" 5 | #include "GateBoyLib/GateBoyPair.h" 6 | #include 7 | 8 | //----------------------------------------------------------------------------- 9 | 10 | struct GateBoyTests { 11 | GateBoyTests(); 12 | 13 | TestResults test_generic(const IGateBoy* proto); 14 | TestResults test_regs(const IGateBoy* proto); 15 | TestResults test_spu_regs(const IGateBoy* proto); 16 | 17 | TestResults test_regression_cart(const char* filename, int cycles); 18 | TestResults test_regression_dump(const char* filename, int cycles); 19 | 20 | TestResults test_reset(const IGateBoy* proto, uint8_t mask); 21 | 22 | TestResults test_first_op(const IGateBoy* proto); 23 | TestResults test_bootrom(const IGateBoy* proto); 24 | TestResults test_clk(const IGateBoy* proto); 25 | TestResults test_ext_bus(const IGateBoy* proto); 26 | TestResults test_mem(const IGateBoy* proto); 27 | TestResults test_dma(const IGateBoy* proto); 28 | TestResults test_ppu(const IGateBoy* proto); 29 | TestResults test_timer(const IGateBoy* proto); 30 | 31 | TestResults test_micro_poweron(const IGateBoy* proto); 32 | TestResults test_micro_lcden(const IGateBoy* proto); 33 | TestResults test_micro_timer(const IGateBoy* proto); 34 | 35 | TestResults test_micro_int_vblank(const IGateBoy* proto); 36 | TestResults test_micro_int_stat(const IGateBoy* proto); 37 | TestResults test_micro_int_timer(const IGateBoy* proto); 38 | TestResults test_micro_int_serial(const IGateBoy* proto); 39 | TestResults test_micro_int_joypad(const IGateBoy* proto); 40 | 41 | TestResults test_micro_lock_oam(const IGateBoy* proto); 42 | TestResults test_micro_lock_vram(const IGateBoy* proto); 43 | TestResults test_micro_window(const IGateBoy* proto); 44 | TestResults test_micro_dma(const IGateBoy* proto); 45 | TestResults test_micro_ppu(const IGateBoy* proto); 46 | TestResults test_micro_mbc1(const IGateBoy* proto); 47 | 48 | TestResults run_microtest(const IGateBoy* proto, const char* filename, bool verbose = false); 49 | 50 | TestResults test_dma(const IGateBoy* proto, uint16_t src); 51 | 52 | TestResults test_fuzz_reg(const IGateBoy* proto, uint16_t addr, int reps); 53 | TestResults test_fuzz_reg2(const IGateBoy* proto, int reps); 54 | TestResults test_fuzz_spu(const IGateBoy* proto, int reps); 55 | void save_fuzz_dump(const IGateBoy* proto, int rep_j, int rep_i); 56 | 57 | TestResults test_reg(const IGateBoy* proto, const char* tag, uint16_t addr, uint8_t data_in); 58 | TestResults test_mem(const IGateBoy* proto, const char* tag, uint16_t addr_start, uint16_t addr_end, uint16_t step, bool test_write); 59 | TestResults test_spu_reg(const IGateBoy* proto, const char* tag, uint16_t addr, uint8_t data_in); 60 | 61 | void run_benchmark(const IGateBoy* proto); 62 | 63 | TestResults test_mooneye_generic(const IGateBoy* proto); 64 | TestResults test_mooneye_mbc1(const IGateBoy* proto); 65 | TestResults test_mooneye_timer(const IGateBoy* proto); 66 | TestResults test_mooneye_ppu(const IGateBoy* proto); 67 | TestResults run_mooneye_test(const IGateBoy* proto, const char* path, const char* filename); 68 | 69 | TestResults diff_gb(IGateBoy* gb1, IGateBoy* gb2, uint8_t mask); 70 | 71 | //std::unique_ptr create_debug_gb(const blob& cart_blob, bool cpu_en); 72 | 73 | //bool verbose = false; 74 | const blob dummy_cart; 75 | }; 76 | 77 | //----------------------------------------------------------------------------- 78 | -------------------------------------------------------------------------------- /src/MetroBoyApp/MetroBoyApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "MetroBoyLib/MetroBoy.h" 4 | 5 | #include "metrolib/core/StateStack.h" 6 | #include "metrolib/core/File.h" 7 | #include "metrolib/appbase/App.h" 8 | #include "metrolib/appbase/GridPainter.h" 9 | #include "metrolib/gameboy/GBBlitter.h" 10 | #include "metrolib/appbase/DumpPainter.h" 11 | #include "metrolib/appbase/Blitter.h" 12 | #include "metrolib/appbase/GridPainter.h" 13 | #include "metrolib/appbase/TextPainter.h" 14 | 15 | #include 16 | 17 | class MetroBoyApp : public App { 18 | public: 19 | 20 | virtual const char* app_get_title() override; 21 | virtual void app_init(int _screen_w, int _screen_h) override; 22 | virtual void app_close() override; 23 | 24 | virtual void app_update(dvec2 screen_size, double delta) override; 25 | virtual void app_render_frame(dvec2 screen_size, double delta) override; 26 | virtual void app_render_ui(dvec2 screen_size, double delta) override; 27 | 28 | void post(); 29 | void load_rom(const std::string& prefix, const std::string& name); 30 | void load_rom(const std::string& name) { load_rom("./", name); } 31 | 32 | void load_memdump(const std::string& prefix, const std::string& name); 33 | 34 | //---------------------------------------- 35 | 36 | void sync_to_vblank() { 37 | while(gb->ppu.line != 144) step_cycle(); 38 | } 39 | 40 | void run_to_breakpoint(uint16_t breakpoint) { 41 | while (gb->gb_cpu.get_op_addr() != breakpoint) step_cycle(); 42 | } 43 | 44 | void step_phase(int count = 1) { 45 | for (int i = 0; i < count; i++) { 46 | gb->next_phase(); 47 | } 48 | } 49 | 50 | void step_cycle(int count = 1) { 51 | for (int i = 0; i < count; i++) { 52 | step_phase(); 53 | while(gb->phase_total & 7) step_phase(); 54 | } 55 | } 56 | 57 | void step_line(int count = 1) { 58 | for (int i = 0; i < count; i++) { 59 | int old_line = gb->ppu.line; 60 | while(gb->ppu.line == old_line) step_cycle(); 61 | } 62 | } 63 | 64 | void step_frame(int count = 1) { 65 | for (int i = 0; i < count; i++) { 66 | while (gb->ppu.line == 144) step_cycle(); 67 | while (gb->ppu.line != 144) step_cycle(); 68 | } 69 | } 70 | 71 | //---------------------------------------- 72 | 73 | double app_start = 0; 74 | 75 | Blitter blitter; 76 | GridPainter grid_painter; 77 | TextPainter text_painter; 78 | 79 | //---------- 80 | // controls 81 | 82 | enum RunMode { 83 | RUN_STOP = 0, // don't run 84 | RUN_STEP = 1, // run one step 85 | RUN_FAST = 2, // run steps as fast as possible 86 | RUN_SYNC = 3, // run until end of frame, then wait for vsync signal 87 | }; 88 | 89 | RunMode runmode = RUN_STEP; 90 | 91 | bool rom_loaded = false; 92 | int overlay_mode = 0; 93 | const uint8_t* keyboard_state = nullptr; 94 | uint8_t buttons = 0; 95 | 96 | double sim_time = 0; 97 | double sim_rate = 0; 98 | 99 | double frame_begin = 0; 100 | double frame_end = 0; 101 | double frame_time = 0; 102 | 103 | //---------- 104 | // viz 105 | 106 | GBBlitter gb_blitter; 107 | DumpPainter dump_painter; 108 | 109 | uint32_t gb_tex = 0; 110 | uint32_t vram_ubo = 0; 111 | uint32_t trace_tex = 0; 112 | uint32_t ram_tex = 0; 113 | 114 | //---------- 115 | // gb state 116 | 117 | StateStack gb; 118 | 119 | //---------- 120 | // debug 121 | 122 | blob rom; 123 | uint8_t golden[160 * 144] = {0}; 124 | uint32_t tracebuffer[456 * 154] = {0}; 125 | int tracecursor = 0; 126 | }; 127 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "metrolib/core/Types.h" 4 | #include "metrolib/gameboy/MetroBoyCPU.h" 5 | 6 | #include "MetroBoyLib/MetroBoyBootrom.h" 7 | #include "MetroBoyLib/MetroBoyCart.h" 8 | #include "MetroBoyLib/MetroBoyDMA.h" 9 | #include "MetroBoyLib/MetroBoyInterrupts.h" 10 | #include "MetroBoyLib/MetroBoyJoypad.h" 11 | #include "MetroBoyLib/MetroBoyOAM.h" 12 | #include "MetroBoyLib/MetroBoyPPU.h" 13 | #include "MetroBoyLib/MetroBoySerial.h" 14 | //#include "MetroBoyLib/MetroBoySPU2.h" 15 | #include "MetroBoyLib/MetroBoyTimer.h" 16 | #include "MetroBoyLib/MetroBoyVRAM.h" 17 | #include "MetroBoyLib/MetroBoyZRAM.h" 18 | 19 | //----------------------------------------------------------------------------- 20 | 21 | struct MetroBoy { 22 | MetroBoy() = default; 23 | 24 | void reset(uint8_t* new_rom, size_t new_rom_size); 25 | void reset_to_bootrom(uint8_t* new_rom, size_t new_rom_size); 26 | 27 | void next_phase(); 28 | 29 | void dump_bus (Dumper& d); 30 | void dump_disasm(Dumper& d); 31 | 32 | //---------------------------------------- 33 | 34 | void check_sentinel() const { 35 | if (sentinel1 != 0xDEADBEEFBAADF00D) *reinterpret_cast(0xDEADC0DEDEADC0DE) = 1; 36 | if (sentinel2 != 0xF00DCAFEBAADC0DE) *reinterpret_cast(0xDEADC0DEDEADC0DE) = 1; 37 | } 38 | 39 | uint64_t sentinel1 = 0xDEADBEEFBAADF00D; 40 | 41 | //---------- 42 | 43 | MetroBoyCPU gb_cpu; 44 | MetroBoyTimer timer; 45 | MetroBoyCart cart; 46 | MetroBoyVRAM vram; 47 | MetroBoyOAM oam; 48 | MetroBoyZRAM zram; 49 | MetroBoyJoypad joy; 50 | MetroBoySerial serial; 51 | MetroBoyPPU ppu; 52 | //MetroBoySPU2 spu; 53 | MetroBoyDMA dma; 54 | MetroBoyBootrom boot; 55 | MetroBoyInterrupts ints; 56 | 57 | uint8_t intf_delay = 0; 58 | uint8_t imask_delay = 0; 59 | 60 | int64_t phase_total = 0; 61 | 62 | Req cpu_req = {0}; 63 | 64 | Req ebus_req = {0}; // 0x0000 - 0x7FFF, 0xA000 - 0xFFFF 65 | Ack ebus_ack = {0}; 66 | 67 | Req vbus_req = {0}; // 0x8000 - 0x9FFF 68 | Ack vbus_ack = {0}; 69 | 70 | Req obus_req = {0}; // 0xFE00 - 0xFEFF 71 | Ack obus_ack = {0}; 72 | 73 | Req ibus_req = {0}; // 0xFF00 - 0xFFFF 74 | Ack ibus_ack = {0}; 75 | 76 | uint8_t dma_data_latch; 77 | 78 | uint8_t framebuffer[160*144] = {0}; 79 | 80 | //---------- 81 | 82 | uint64_t sentinel2 = 0xF00DCAFEBAADC0DE; 83 | }; 84 | 85 | //----------------------------------------------------------------------------- 86 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyBootrom.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyBootrom.h" 2 | 3 | #include "metrolib/gameboy/MetroBoyCPU.h" 4 | #include "metrolib/gameboy/GBConstants.h" 5 | 6 | #include "metrolib/core/Constants.h" 7 | #include 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | void MetroBoyBootrom::tick(int phase_total, const Req& req, Ack& ack) const { 12 | (void)phase_total; 13 | if (req.read && req.addr <= 0x00FF && !disable_bootrom) { 14 | ack.addr = req.addr; 15 | ack.data_lo = get_DMG_ROM()[req.addr]; 16 | ack.read++; 17 | } 18 | } 19 | 20 | void MetroBoyBootrom::tock(int phase_total, const Req& req) { 21 | if (MB_DELTA_GH && req.write && req.addr == ADDR_DISABLE_BOOTROM) { 22 | disable_bootrom |= (req.data_lo != 0); 23 | } 24 | } 25 | 26 | //----------------------------------------------------------------------------- 27 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyBootrom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "metrolib/core/Types.h" 3 | 4 | struct Req; 5 | struct Ack; 6 | 7 | //----------------------------------------------------------------------------- 8 | 9 | struct MetroBoyBootrom { 10 | MetroBoyBootrom() = default; 11 | 12 | void tick(int phase_total, const Req& req, Ack& ack) const; 13 | void tock(int phase_total, const Req& req); 14 | 15 | int disable_bootrom = 1; 16 | }; 17 | 18 | //----------------------------------------------------------------------------- 19 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyCart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct Dumper; 7 | struct Req; 8 | struct Ack; 9 | 10 | //----------------------------------------------------------------------------- 11 | 12 | struct MetroBoyCart { 13 | void set_rom(uint8_t* new_rom, size_t new_rom_size); 14 | void reset(); 15 | 16 | void tick(int phase_total, const Req& req, Ack& ebus_ack) const; 17 | void tock(int phase_total, const Req& req); 18 | 19 | void dump(Dumper& dump) const; 20 | 21 | uint8_t* get_flat_ptr(uint16_t addr); 22 | 23 | uint8_t* cart_rom = 0; 24 | size_t cart_size = 0; 25 | int rom_bank_count = 0; 26 | int ram_bank_count = 0; 27 | bool ram_enable = 0; 28 | int mode = 0; 29 | int bank_latch1 = 0; 30 | int bank_latch2 = 0; 31 | 32 | uint8_t main_ram[8192] = {}; 33 | uint8_t cart_ram[8192] = {}; 34 | }; 35 | 36 | //----------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyDMA.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyDMA.h" 2 | 3 | #include "metrolib/gameboy/MetroBoyCPU.h" 4 | #include "metrolib/gameboy/GBConstants.h" 5 | 6 | #include 7 | #include 8 | 9 | #if 0 10 | 11 | void DMA1::get_ebus_req(Req& r) const { 12 | if ((mode_a != Mode::NONE) && ((source_a >> 5) != 4)) { 13 | r.addr = uint16_t((source_a << 8) | count_a); 14 | r.data = 0; 15 | r.read = 1; 16 | r.write = 0; 17 | } 18 | } 19 | 20 | //---------------------------------------- 21 | 22 | void DMA1::get_vbus_req(Req& r) const { 23 | if ((mode_a != Mode::NONE) && ((source_a >> 5) == 4)) { 24 | r.addr = uint16_t((source_a << 8) | count_a); 25 | r.data = 0; 26 | r.read = 1; 27 | r.write = 0; 28 | } 29 | } 30 | 31 | //---------------------------------------- 32 | 33 | void DMA1::get_obus_req(Req& r) const { 34 | if (mode_b != Mode::NONE) { 35 | r.addr = uint16_t(ADDR_OAM_BEGIN + count_b); 36 | r.data = data_b; 37 | r.read = 0; 38 | r.write = 1; 39 | } 40 | } 41 | 42 | #endif 43 | 44 | //----------------------------------------------------------------------------- 45 | // FIXME the phases are totally off here 46 | 47 | void MetroBoyDMA::tick(int phase_total, const Req& req, Ack& ack) { 48 | (void)phase_total; 49 | 50 | if (req.read && req.addr == 0xFF46) { 51 | ack.addr = req.addr; 52 | ack.data_lo = uint8_t(addr >> 8); 53 | ack.read++; 54 | } 55 | } 56 | 57 | //----------------------------------------------------------------------------- 58 | 59 | void MetroBoyDMA::tock(int phase_total, const Req& req) { 60 | bool DMA_WR = (req.addr == 0xFF46) && req.write && (MB_DELTA_FG || MB_DELTA_GH || MB_DELTA_HA); 61 | bool DMA_RST = DMA_RUN_TRIG_d4; 62 | 63 | if (MB_DELTA_BC) { 64 | // something wrong here, inverting this until we figure it out. 65 | // bool LUPA = nor4(DMA_WR, DMA_WR_LATCH); 66 | bool LUPA = DMA_WR || DMA_WR_LATCH; 67 | DMA_RUN_TRIG_d0 = LUPA; 68 | DMA_RUN_WRITE = DMA_RUN_READ; 69 | if (DMA_RUN_READ) addr++; 70 | } 71 | 72 | if (MB_DELTA_FG) { 73 | /*_p04.LENE*/ DMA_RUN_TRIG_d4 = DMA_RUN_TRIG_d0; 74 | /*_p04.MYTE*/ DMA_DONE = (addr & 0xFF) == 159; 75 | if (req.write && req.addr == 0xFF46) { 76 | addr = (req.data_lo << 8); 77 | } 78 | } 79 | 80 | if (DMA_WR) DMA_WR_LATCH = 1; 81 | if (DMA_RST) DMA_WR_LATCH = 0; 82 | 83 | if (DMA_RUN_TRIG_d4) DMA_RUN_READ = 1; 84 | if (DMA_DONE) DMA_RUN_READ = 0; 85 | 86 | if (DMA_RST) { 87 | DMA_DONE = 0; 88 | addr = addr & 0xFF00; 89 | } 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | 94 | void MetroBoyDMA::dump(Dumper& d) const { 95 | d("\002--------------DMA2--------------\001\n"); 96 | 97 | d("DMA_WR_LATCH %d\n", (bool)DMA_WR_LATCH); 98 | d("DMA_RUN_TRIG_d0 %d\n", (bool)DMA_RUN_TRIG_d0); 99 | d("DMA_RUN_TRIG_d4 %d\n", (bool)DMA_RUN_TRIG_d4); 100 | d("DMA_RUN_READ %d\n", (bool)DMA_RUN_READ); 101 | d("DMA_RUN_WRITE %d\n", (bool)DMA_RUN_WRITE); 102 | d("DMA_DONE %d\n", (bool)DMA_DONE); 103 | d("addr 0x%04x\n", addr); 104 | } 105 | 106 | //----------------------------------------------------------------------------- 107 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyDMA.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Req; 4 | struct Ack; 5 | struct Dumper; 6 | 7 | //----------------------------------------------------------------------------- 8 | 9 | struct MetroBoyDMA { 10 | 11 | void tick(int phase_total, const Req& req, Ack& ack); 12 | void tock(int phase_total, const Req& req); 13 | void dump(Dumper& dump) const; 14 | 15 | int addr = 0xFFFF; 16 | 17 | /*_p04.LYXE*/ bool DMA_WR_LATCH = 0; 18 | /*_p04.LUVY*/ bool DMA_RUN_TRIG_d0 = 0; 19 | /*_p04.LENE*/ bool DMA_RUN_TRIG_d4 = 0; 20 | /*_p04.LOKY*/ bool DMA_RUN_READ = 0; 21 | /*_p04.MATU*/ bool DMA_RUN_WRITE = 0; 22 | /*_p04.MYTE*/ bool DMA_DONE = 0; 23 | }; 24 | 25 | //----------------------------------------------------------------------------- 26 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyInterrupts.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyInterrupts.h" 2 | 3 | #include "metrolib/gameboy/MetroBoyCPU.h" 4 | #include "metrolib/gameboy/GBConstants.h" 5 | #include "metrolib/core/Constants.h" 6 | 7 | //----------------------------------------------------------------------------- 8 | 9 | #define MB_DELTA_AB ((phase_total & 7) == 0) 10 | #define MB_DELTA_BC ((phase_total & 7) == 1) 11 | #define MB_DELTA_CD ((phase_total & 7) == 2) 12 | #define MB_DELTA_DE ((phase_total & 7) == 3) 13 | #define MB_DELTA_EF ((phase_total & 7) == 4) 14 | #define MB_DELTA_FG ((phase_total & 7) == 5) 15 | #define MB_DELTA_GH ((phase_total & 7) == 6) 16 | #define MB_DELTA_HA ((phase_total & 7) == 7) 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | void MetroBoyInterrupts::tick(int phase_total, const Req& req, Ack& ack) const { 21 | (void)phase_total; 22 | 23 | if (req.read) { 24 | if (req.addr == ADDR_IF) { 25 | ack.addr = req.addr; 26 | ack.data = 0b11100000 | intf; 27 | ack.read++; 28 | } 29 | if (req.addr == ADDR_IE) { 30 | ack.addr = req.addr; 31 | ack.data = imask; 32 | ack.read++; 33 | } 34 | } 35 | } 36 | 37 | //----------------------------------------------------------------------------- 38 | 39 | void MetroBoyInterrupts::tock(int phase_total, const Req& req, 40 | uint8_t cpu_int_ack, 41 | bool vblank_int, 42 | bool stat_int, 43 | bool timer_int, 44 | bool serial_int, 45 | bool joypad_int) { 46 | 47 | if (MB_DELTA_AB) { 48 | intf &= ~cpu_int_ack; 49 | } 50 | 51 | if (MB_DELTA_GH) { 52 | if (req.write) { 53 | if (req.addr == ADDR_IF) intf = (uint8_t)req.data | 0b11100000; 54 | if (req.addr == ADDR_IE) imask = (uint8_t)req.data; 55 | } 56 | if (vblank_int) intf |= INT_VBLANK_MASK; 57 | if (stat_int) intf |= INT_STAT_MASK; 58 | if (timer_int) intf |= INT_TIMER_MASK; 59 | if (serial_int) intf |= INT_JOYPAD_MASK; 60 | if (joypad_int) intf |= INT_JOYPAD_MASK; 61 | } 62 | } 63 | 64 | //----------------------------------------------------------------------------- 65 | 66 | void MetroBoyInterrupts::dump(Dumper& d) const { 67 | d("\002--------------INTERRUPTS----------\001\n"); 68 | 69 | d("imask %s\n", byte_to_bits(imask)); 70 | d("intf %s\n", byte_to_bits(intf)); 71 | } 72 | 73 | //----------------------------------------------------------------------------- 74 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyInterrupts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Req; 6 | struct Ack; 7 | struct Dumper; 8 | 9 | struct MetroBoyInterrupts { 10 | MetroBoyInterrupts() = default; 11 | 12 | void tick(int phase_total, const Req& req, Ack& ack) const; 13 | void tock(int phase_total, const Req& req, 14 | uint8_t cpu_int_ack, 15 | bool vblank_int, 16 | bool stat_int, 17 | bool timer_int, 18 | bool serial_int, 19 | bool joypad_int); 20 | 21 | void dump(Dumper& dump) const; 22 | 23 | uint8_t intf = 0xE1; 24 | uint8_t imask = 0x00; 25 | }; 26 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyJoypad.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyJoypad.h" 2 | #include "metrolib/core/Constants.h" 3 | #include 4 | 5 | #include "metrolib/core/Dumper.h" 6 | #include "metrolib/gameboy/GBConstants.h" 7 | #include "metrolib/gameboy/MetroBoyCPU.h" 8 | 9 | void MetroBoyJoypad::reset() { 10 | val = 0xFF; 11 | p1 = 0xCF; // FF00 12 | } 13 | 14 | void MetroBoyJoypad::tick(int phase_total, const Req& req, Ack& ack) const { 15 | (void)phase_total; 16 | 17 | if (req.read && req.addr == ADDR_P1) { 18 | ack.addr = req.addr; 19 | ack.data_lo = p1; 20 | ack.read++; 21 | } 22 | } 23 | 24 | void MetroBoyJoypad::tock(int phase_total, const Req& req) { 25 | if (MB_DELTA_GH && req.write && req.addr == ADDR_P1) { 26 | p1 = (p1 & 0xCF) | (req.data_lo & 0x30); 27 | switch (p1 & 0x30) { 28 | case 0x00: p1 = (p1 & 0xF0) | 0x0F; break; 29 | case 0x10: p1 = (p1 & 0xF0) | ((val >> 4) & 0xF); break; 30 | case 0x20: p1 = (p1 & 0xF0) | ((val >> 0) & 0xF); break; 31 | } 32 | } 33 | } 34 | 35 | void MetroBoyJoypad::set(uint8_t new_val) { 36 | val = new_val; 37 | switch (p1 & 0x30) { 38 | case 0x00: p1 = (p1 & 0xF0) | 0x0F; break; 39 | case 0x10: p1 = (p1 & 0xF0) | ((val >> 4) & 0xF); break; 40 | case 0x20: p1 = (p1 & 0xF0) | ((val >> 0) & 0xF); break; 41 | } 42 | } 43 | 44 | void MetroBoyJoypad::dump(Dumper& dump) const { 45 | dump("\002--------------JOYPAD----------\001\n"); 46 | dump("%c %c %c %c %c %c %c %c\n", 47 | val & 0x01 ? '-' : 'R', 48 | val & 0x02 ? '-' : 'L', 49 | val & 0x04 ? '-' : 'U', 50 | val & 0x08 ? '-' : 'D', 51 | val & 0x10 ? '-' : 'A', 52 | val & 0x20 ? '-' : 'B', 53 | val & 0x40 ? '-' : 'E', 54 | val & 0x80 ? '-' : 'S'); 55 | } 56 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyJoypad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | struct Req; 5 | struct Ack; 6 | struct Dumper; 7 | 8 | struct MetroBoyJoypad { 9 | MetroBoyJoypad() = default; 10 | 11 | void reset(); 12 | 13 | void tick(int phase_total, const Req& req, Ack& ack) const; 14 | void tock(int phase_total, const Req& req); 15 | 16 | uint8_t get() const { return val; } 17 | void set(uint8_t new_val); 18 | void dump(Dumper& dump) const; 19 | 20 | uint8_t val = 0; 21 | uint8_t p1 = 0; // FF00 22 | }; 23 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyOAM.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyOAM.h" 2 | 3 | #include "metrolib/gameboy/MetroBoyCPU.h" 4 | #include "metrolib/gameboy/GBConstants.h" 5 | 6 | #include 7 | #include 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | void MetroBoyOAM::reset() { 12 | memset(ram, 0, sizeof(ram)); 13 | } 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | void MetroBoyOAM::tick(int phase_total, const Req& req, Ack& ack) const { 18 | (void)phase_total; 19 | 20 | if (req.read && (req.addr >= ADDR_OAM_BEGIN) && (req.addr <= ADDR_OAM_END)) { 21 | ack.addr = req.addr; 22 | if (req.addr & 1) { 23 | ack.data = ram[(req.addr & 0x00FF) >> 1] >> 8; 24 | } 25 | else { 26 | ack.data = ram[(req.addr & 0x00FF) >> 1]; 27 | } 28 | ack.read++; 29 | } 30 | } 31 | 32 | // FIXME this probably writes on phases other than GH during DMA... 33 | 34 | void MetroBoyOAM::tock(int phase_total, const Req& req) { 35 | if (MB_DELTA_GH && req.write && (req.addr >= ADDR_OAM_BEGIN) && (req.addr <= ADDR_OAM_END)) { 36 | uint16_t oam_addr = req.addr & 0x00FF; 37 | uint16_t d = ram[oam_addr >> 1]; 38 | if (oam_addr & 1) d = (d & 0x00FF) | (req.data_lo << 8); 39 | else d = (d & 0xFF00) | (req.data_lo << 0); 40 | ram[oam_addr >> 1] = d; 41 | } 42 | } 43 | 44 | //----------------------------------------------------------------------------- 45 | 46 | void MetroBoyOAM::dump(Dumper& d) const { 47 | d("\002--------------OAM--------------\001\n"); 48 | 49 | uint8_t* flat = (uint8_t*)ram; 50 | for (int y = 0; y < 20; y++) { 51 | d("%04x: ", y * 8 + ADDR_OAM_BEGIN); 52 | for (int x = 0; x < 8; x++) { 53 | uint8_t b = flat[x + y * 8]; 54 | uint8_t l = (b >> 0) & 0x0F; 55 | uint8_t h = (b >> 4) & 0x0F; 56 | 57 | d("%c%c ", h > 9 ? '7' + h : '0' + h, l > 9 ? '7' + l : '0' + l); 58 | } 59 | d("\n"); 60 | } 61 | 62 | d("\n"); 63 | } 64 | 65 | //----------------------------------------------------------------------------- 66 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyOAM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | struct Req; 5 | struct Ack; 6 | struct Dumper; 7 | 8 | //----------------------------------------------------------------------------- 9 | 10 | struct MetroBoyOAM { 11 | 12 | void reset(); 13 | void tock(int phase_total, const Req& req); 14 | void tick(int phase_total, const Req& req, Ack& ack) const; 15 | 16 | void dump(Dumper& dump) const; 17 | 18 | uint16_t ram[128] = {0}; 19 | }; 20 | 21 | //----------------------------------------------------------------------------- 22 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyPPU.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MetroBoyLib/MetroBoyOAM.h" 3 | #include "MetroBoyLib/MetroBoyVRAM.h" 4 | #include "metrolib/core/Constants.h" 5 | 6 | struct LCD; 7 | 8 | //----------------------------------------------------------------------------- 9 | 10 | struct MetroBoyPPU { 11 | void reset(); 12 | void reset_to_bootrom(); 13 | 14 | bool read (uint16_t addr, uint8_t& out); 15 | bool write(uint16_t addr, uint8_t data); 16 | 17 | uint8_t read(uint16_t addr) { 18 | uint8_t temp = 0; 19 | read(addr, temp); 20 | return temp; 21 | } 22 | 23 | void tick(int phase_total, const Req& req, Ack& ibus_ack); 24 | void tock(int phase_total, const Req& ibus_req, const Ack vbus_ack, const Ack obus_ack); 25 | //void tock_lcdoff(); 26 | 27 | void get_vbus_req(Req& r) const; 28 | void get_obus_req(Req& r) const; 29 | 30 | void emit_pixel(); 31 | 32 | void dump(Dumper& dump) const; 33 | 34 | //---------- 35 | // Timers and states 36 | 37 | // 114 * 8 = 912 phases per line 38 | 39 | // CATU_LINE_P000 = 1 @ 0 - scan index reset, pix count reset, mode 2 40 | // BESU_SCANNINGp = 1 @ 0 41 | // NYPE_LINE_P002 = 1 @ 2 42 | // ANEL_LINE_P002 = 1 @ 2 43 | // CENO_SCANNINGp = 1 @ 4 44 | // XYMU_RENDERINGp = 1 @ 160 45 | // BYBA_SCAN_DONE_A = 1 @ 160 46 | // DOBA_SCAN_DONE_B = 1 @ 161 47 | // pix count 1 @ 174 48 | // pix count 167 @ 506 49 | // XYMU_RENDERINGp = 0 @ 507 50 | // RUTU_LINE_P910 = 1 @ 910 51 | 52 | int phase_count = 0; 53 | uint8_t line = 0; // this is the actual line, no glitches 54 | bool lyc_match = 0; 55 | int frame_count = 0; 56 | uint8_t stat_int = 0; 57 | uint8_t vblank_int = 0; 58 | 59 | //---------- 60 | // Registers 61 | 62 | uint8_t reg_lcdc = 0; // FF40 63 | uint8_t stat = 0; // FF41 64 | uint8_t scy = 0; // FF42 65 | uint8_t scx = 0; // FF43 66 | uint8_t ly = 0; // FF44 // this line counter contains glitches 67 | uint8_t reg_lyc = 0; // FF45 68 | uint8_t bgp = 0; // FF47 69 | uint8_t obp0 = 0; // FF48 70 | uint8_t obp1 = 0; // FF49 71 | uint8_t wy = 0; // FF4A 72 | uint8_t wx = 0; // FF4B 73 | 74 | 75 | uint8_t palettes[4] = {0}; 76 | 77 | void update_palettes() { 78 | palettes[0] = bgp; 79 | palettes[1] = bgp; 80 | palettes[2] = obp0; 81 | palettes[3] = obp1; 82 | } 83 | 84 | //---------- 85 | // Sprite store 86 | 87 | int8_t sprite_index = 0; 88 | uint8_t sprite_count = 0; 89 | int sprite_hit = 0; 90 | 91 | uint8_t spriteY = 0; 92 | uint8_t spriteX = 0; 93 | uint8_t spriteP = 0; 94 | uint8_t spriteF = 0; 95 | 96 | uint8_t sprite_x[10] = {0}; // 80 bits 97 | uint8_t sprite_y[10] = {0}; // 80 bits 98 | uint8_t sprite_i[10] = {0}; // 60 bits? 99 | 100 | //---------- 101 | // Vram Fetcher 102 | 103 | uint8_t bg_fetch_state = 0; 104 | uint8_t sp_fetch_state = 0; 105 | 106 | //---------- 107 | // Window stuff 108 | 109 | #if 0 110 | uint8_t win_y_latch = 0; 111 | uint8_t win_y_counter = 0; 112 | 113 | bool in_window_old = 0; 114 | bool in_window_new = 0; 115 | bool in_window_early = 0; 116 | bool in_window_late = 0; 117 | 118 | bool win_retrig_old = 0; 119 | bool win_retrig_new = 0; 120 | #endif 121 | 122 | //---------- 123 | // Pixel pipe 124 | 125 | //uint8_t map_x = 0; 126 | 127 | uint8_t tile_map = 0; 128 | uint8_t tile_lo = 0; 129 | uint8_t tile_hi = 0; 130 | bool tile_latched = 0; 131 | 132 | uint8_t pix_count = 0; 133 | 134 | uint8_t pix_discard_scx = 0; 135 | uint8_t pix_discard_pad = 0; 136 | 137 | uint8_t bg_pipe_lo = 0; 138 | uint8_t bg_pipe_hi = 0; 139 | uint8_t spr_pipe_lo = 0; 140 | uint8_t spr_pipe_hi = 0; 141 | uint8_t pal_pipe = 0; 142 | uint8_t mask_pipe = 0; 143 | 144 | uint8_t pix_out = 0; 145 | bool pix_oe = 0; 146 | }; 147 | 148 | //----------------------------------------------------------------------------- 149 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoySPU2.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | 3 | /* 4 | void tock_ack(uint16_t addr) { 5 | ack_data = 0; 6 | ack_valid = 0; 7 | 8 | if (addr >= 0xFF30 && addr <= 0xFF3F) { 9 | // wavetable 10 | ack_data = s3_wave[addr & 0xF]; 11 | } 12 | else if (addr >= 0xFF10 && addr <= 0xFF26) { 13 | switch (addr) { 14 | case 0xFF10: ack_data = nr10 | 0x80; break; 15 | case 0xFF11: ack_data = nr11 | 0x3F; break; 16 | case 0xFF12: ack_data = nr12 | 0x00; break; 17 | case 0xFF13: ack_data = nr13 | 0xFF; break; 18 | case 0xFF14: ack_data = nr14 | 0xBF; break; 19 | 20 | case 0xFF15: ack_data = nr20 | 0xFF; break; 21 | case 0xFF16: ack_data = nr21 | 0x3F; break; 22 | case 0xFF17: ack_data = nr22 | 0x00; break; 23 | case 0xFF18: ack_data = nr23 | 0xFF; break; 24 | case 0xFF19: ack_data = nr24 | 0xBF; break; 25 | 26 | case 0xFF1A: ack_data = nr30 | 0x7F; break; 27 | case 0xFF1B: ack_data = nr31 | 0xFF; break; 28 | case 0xFF1C: ack_data = nr32 | 0x9F; break; 29 | case 0xFF1D: ack_data = nr33 | 0xFF; break; 30 | case 0xFF1E: ack_data = nr34 | 0xBF; break; 31 | 32 | case 0xFF1F: ack_data = nr40 | 0xFF; break; 33 | case 0xFF20: ack_data = nr41 | 0xFF; break; 34 | case 0xFF21: ack_data = nr42 | 0x00; break; 35 | case 0xFF22: ack_data = nr43 | 0x00; break; 36 | case 0xFF23: ack_data = nr44 | 0xBF; break; 37 | 38 | case 0xFF24: ack_data = nr50 | 0x00; break; 39 | case 0xFF25: ack_data = nr51 | 0x00; break; 40 | 41 | case 0xFF26: { 42 | uint8_t bus_out_ = (nr52 & 0x80) | 0x70; 43 | if (s1_running) bus_out_ |= 0b00000001; 44 | if (s2_running) bus_out_ |= 0b00000010; 45 | if (s3_running) bus_out_ |= 0b00000100; 46 | if (s4_running) bus_out_ |= 0b00001000; 47 | ack_data = bus_out_; 48 | break; 49 | } 50 | } 51 | } 52 | } 53 | */ 54 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoySerial.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoySerial.h" 2 | #include "metrolib/core/Constants.h" 3 | #include 4 | 5 | #include "metrolib/gameboy/GBConstants.h" 6 | #include "metrolib/gameboy/MetroBoyCPU.h" 7 | 8 | //----------------------------------------------------------------------------- 9 | 10 | void MetroBoySerial::reset() { 11 | sb = 0x00; // FF01 12 | sc = 0x7E; // FF02 13 | } 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | void MetroBoySerial::tick(int phase_total, const Req& req, Ack& ack) const { 18 | (void)phase_total; 19 | 20 | if (req.read && ((req.addr == ADDR_SB) || (req.addr == ADDR_SC))) { 21 | ack.addr = req.addr; 22 | ack.data_lo = req.addr == ADDR_SB ? sb : sc; 23 | ack.read++; 24 | } 25 | } 26 | 27 | void MetroBoySerial::tock(int phase_total, const Req& req) { 28 | if (req.write && MB_DELTA_GH) { 29 | if (req.addr == ADDR_SB) sb = (uint8_t)req.data_lo; 30 | if (req.addr == ADDR_SC) sc = (uint8_t)req.data_lo | 0b01111110; 31 | } 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | 36 | void MetroBoySerial::dump(Dumper& d) const { 37 | d("\002--------------SERIAL-----------\001\n"); 38 | d("SB 0x%02x\n", sb); 39 | d("SC 0x%02x\n", sc); 40 | } 41 | 42 | //----------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoySerial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Req; 6 | struct Ack; 7 | struct Dumper; 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | struct MetroBoySerial { 12 | void reset(); 13 | 14 | void tick(int phase_total, const Req& req, Ack& ack) const; 15 | void tock(int phase_total, const Req& req); 16 | 17 | void dump(Dumper& dump) const; 18 | 19 | private: 20 | uint8_t sb = 0; 21 | uint8_t sc = 0; 22 | }; 23 | 24 | //----------------------------------------------------------------------------- 25 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyTimer.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyTimer.h" 2 | #include "metrolib/core/Constants.h" 3 | #include 4 | 5 | #include "metrolib/core/Dumper.h" 6 | #include "metrolib/gameboy/MetroBoyCPU.h" 7 | #include "metrolib/gameboy/GBConstants.h" 8 | 9 | // Passes mooneye timer tests 10 | 11 | static const uint16_t masks[] = { 0x80, 0x02, 0x08, 0x20 }; 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | void MetroBoyTimer::reset() { 16 | div = 0xEAF2; 17 | tima = 0; 18 | tma = 0; 19 | tac = 0; 20 | 21 | tima_clk = 0; 22 | tima_7_sync = 0; 23 | timer_int = 0; 24 | } 25 | 26 | //----------------------------------------------------------------------------- 27 | 28 | void MetroBoyTimer::tick(int phase_total, const Req& req, Ack& ack) { 29 | (void)phase_total; 30 | 31 | if (req.read) switch(req.addr) { 32 | case ADDR_DIV: ack.read++; ack.data_lo += uint8_t(div >> 6); break; 33 | case ADDR_TIMA: ack.read++; ack.data_lo += tima; break; 34 | case ADDR_TMA: ack.read++; ack.data_lo += tma; break; 35 | case ADDR_TAC: ack.read++; ack.data_lo += tac | 0b11111000; break; 36 | }; 37 | } 38 | 39 | //----------------------------------------------------------------------------- 40 | // Timer interrupt fires when the high bit of tima (after sync with phase C) 41 | // goes low. Writing to tima clears the synchronized bit for some reason. 42 | 43 | void MetroBoyTimer::tock(int phase_total, const Req& req) { 44 | 45 | if (MB_DELTA_CD) { 46 | div++; 47 | timer_int = tima_7_sync && !(tima & 0x80); 48 | tima_7_sync = (tima & 0x80); 49 | update_tima(); 50 | } 51 | 52 | if (MB_DELTA_FG && req.write) { 53 | switch(req.addr) { 54 | case ADDR_DIV: div = 0; break; 55 | case ADDR_TIMA: tima = req.data_lo; tima_7_sync = false; break; 56 | case ADDR_TMA: tma = req.data_lo; break; 57 | case ADDR_TAC: tac = req.data_lo; break; 58 | } 59 | update_tima(); 60 | } 61 | 62 | // In the circuit, tima_7_sync is reset whenever timer_int is high, 63 | // presumably to stop the interrupt from retriggering until the cpu acks the 64 | // interrupt. But that doesn't actually make sense, as timer_int is set and 65 | // tima_7_sync is cleared at the same time and the interrupt is only high 66 | // for one mcycle (c to c). 67 | if (timer_int) tima_7_sync = 0; 68 | } 69 | 70 | //----------------------------------------------------------------------------- 71 | // tima is "clocked" off a signal derived from div and tac. 72 | 73 | void MetroBoyTimer::update_tima() { 74 | bool tima_clk_ = (div & masks[tac & 3]) && (tac & TAC_RUN); 75 | if (tima_clk && !tima_clk_) tima++; 76 | tima_clk = tima_clk_; 77 | 78 | if (timer_int) { 79 | tima = tma; 80 | tima_7_sync = 0; 81 | } 82 | } 83 | 84 | //----------------------------------------------------------------------------- 85 | 86 | void MetroBoyTimer::dump(Dumper& d) const { 87 | d("\002--------------TIMER2------------\001\n"); 88 | d("cnt 0x%04x\n", div); 89 | d("div 0x%02x\n", uint8_t(div >> 6)); 90 | d("tima 0x%02x\n", tima); 91 | d("tma 0x%02x\n", tma); 92 | d("tac %s\n", byte_to_bits(tac)); 93 | d("tima_7_sync %d\n", tima_7_sync); 94 | d("timer_int %d\n", timer_int); 95 | d("tima_clk %d\n", tima_clk); 96 | d("\n"); 97 | } 98 | 99 | //----------------------------------------------------------------------------- 100 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyTimer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Req; 6 | struct Ack; 7 | struct Dumper; 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | struct MetroBoyTimer { 12 | void reset(); 13 | 14 | void tick(int phase_total, const Req& req, Ack& ack); 15 | void tock(int phase_total, const Req& req); 16 | void dump(Dumper& dump) const; 17 | void update_tima(); 18 | 19 | uint16_t div = 0; 20 | uint8_t tima = 0; 21 | uint8_t tma = 0; 22 | uint8_t tac = 0; 23 | 24 | bool tima_clk = 0; 25 | bool tima_7_sync = 0; 26 | bool timer_int = 0; 27 | }; 28 | 29 | //----------------------------------------------------------------------------- 30 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyVRAM.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyVRAM.h" 2 | 3 | #include "metrolib/core/Dumper.h" 4 | #include "metrolib/gameboy/MetroBoyCPU.h" 5 | #include "metrolib/gameboy/GBConstants.h" 6 | 7 | #include 8 | #include 9 | 10 | //----------------------------------------------------------------------------- 11 | // the nintendo logo as it appears in vram 12 | 13 | static const uint8_t bootrom_logo[] = { 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 15 | 0xf0, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xf3, 0x00, 0xf3, 0x00, 16 | 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 17 | 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x00, 0xf3, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x00, 0xcf, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x0f, 0x00, 0x0f, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x00, 0xf3, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, 24 | 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 25 | 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc3, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 27 | 0xf3, 0x00, 0xf3, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 28 | 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x3c, 0x00, 0x3c, 0x00, 29 | 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 30 | 0xf3, 0x00, 0xf3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 31 | 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 32 | 0x3c, 0x00, 0x3c, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x0f, 0x00, 0x0f, 0x00, 33 | 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 34 | 0xfc, 0x00, 0xfc, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 35 | 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf3, 0x00, 0xf0, 0x00, 0xf0, 0x00, 36 | 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xc3, 0x00, 0xff, 0x00, 0xff, 0x00, 37 | 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0xc3, 0x00, 0xc3, 0x00, 38 | 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xfc, 0x00, 39 | 0x3c, 0x00, 0x42, 0x00, 0xb9, 0x00, 0xa5, 0x00, 0xb9, 0x00, 0xa5, 0x00, 0x42, 0x00, 0x3c, 0x00, 40 | }; 41 | 42 | //----------------------------------------------------------------------------- 43 | 44 | void MetroBoyVRAM::reset() { 45 | memset(ram, 0, sizeof(ram)); 46 | memcpy(ram, bootrom_logo, 416); 47 | } 48 | 49 | //----------------------------------------------------------------------------- 50 | 51 | void MetroBoyVRAM::tick(int phase_total, const Req& req, Ack& ack) const { 52 | (void)phase_total; 53 | if (req.read && (req.addr >= 0x8000) && (req.addr <= 0x9FFF)) { 54 | ack.addr = req.addr; 55 | ack.data_lo = ram[req.addr & 0x1FFF]; 56 | ack.read++; 57 | } 58 | } 59 | 60 | void MetroBoyVRAM::tock(int phase_total, const Req& req) { 61 | if (MB_DELTA_GH && req.write && (req.addr >= 0x8000) && (req.addr <= 0x9FFF)) { 62 | ram[req.addr & 0x1FFF] = uint8_t(req.data_lo); 63 | } 64 | } 65 | 66 | //----------------------------------------------------------------------------- 67 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyVRAM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Req; 6 | struct Ack; 7 | struct Dumper; 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | struct MetroBoyVRAM { 12 | void reset(); 13 | void tick(int phase_total, const Req& req, Ack& ack) const; 14 | void tock(int phase_total, const Req& req); 15 | 16 | uint8_t ram[8192] = {0}; 17 | }; 18 | 19 | //----------------------------------------------------------------------------- 20 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyZRAM.cpp: -------------------------------------------------------------------------------- 1 | #include "MetroBoyLib/MetroBoyZRAM.h" 2 | 3 | #include "metrolib/core/Dumper.h" 4 | #include "metrolib/gameboy/MetroBoyCPU.h" 5 | #include "metrolib/gameboy/GBConstants.h" 6 | 7 | #include 8 | #include 9 | 10 | //----------------------------------------------------------------------------- 11 | 12 | void MetroBoyZRAM::reset() { 13 | memset(ram, 0, 128); 14 | } 15 | 16 | //----------------------------------------------------------------------------- 17 | 18 | void MetroBoyZRAM::tick(int phase_total, const Req& req, Ack& ack) const { 19 | (void)phase_total; 20 | if (req.read && (req.addr >= 0xFF80) && (req.addr <= 0xFFFE)) { 21 | ack.addr = req.addr; 22 | ack.data_lo = ram[req.addr & 0x007F]; 23 | ack.read++; 24 | } 25 | } 26 | 27 | void MetroBoyZRAM::tock(int phase_total, const Req& req) { 28 | if (MB_DELTA_GH && req.write && (req.addr >= 0xFF80) && (req.addr <= 0xFFFE)) { 29 | ram[req.addr & 0x007F] = (uint8_t)req.data_lo; 30 | } 31 | } 32 | 33 | //----------------------------------------------------------------------------- 34 | 35 | void MetroBoyZRAM::dump(Dumper& d) const { 36 | d("\002---------------ZRAM-------------\001\n"); 37 | for (int y = 0; y < 16; y++) { 38 | d("%04x: ", y * 8 + ADDR_ZEROPAGE_BEGIN); 39 | for (int x = 0; x < 8; x++) { 40 | uint8_t b = ram[x + y * 8]; 41 | uint8_t l = (b >> 0) & 0x0F; 42 | uint8_t h = (b >> 4) & 0x0F; 43 | 44 | d("%c%c ", h > 9 ? '7' + h : '0' + h, l > 9 ? '7' + l : '0' + l); 45 | } 46 | d("\n"); 47 | } 48 | } 49 | 50 | //----------------------------------------------------------------------------- 51 | -------------------------------------------------------------------------------- /src/MetroBoyLib/MetroBoyZRAM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Req; 6 | struct Ack; 7 | struct Dumper; 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | struct MetroBoyZRAM { 12 | void reset(); 13 | 14 | void tock(int phase_total, const Req& req); 15 | void tick(int phase_total, const Req& req, Ack& ack) const; 16 | void dump(Dumper& dump) const; 17 | uint8_t* get() { return ram; } 18 | 19 | uint8_t ram[128] = {0}; 20 | }; 21 | 22 | //----------------------------------------------------------------------------- 23 | -------------------------------------------------------------------------------- /src/MetroBoyTests/test_codegen.cpp: -------------------------------------------------------------------------------- 1 | #include "metrolib/gameboy/Assembler.h" 2 | #include "MetroBoyLib/MetroBoy.h" 3 | 4 | #include 5 | 6 | // no sprites, no scroll - last render is 61 7 | // sprite at 0, no scroll - last render is 63 8 | 9 | // last cycle in render phase 10 | int spritex_vs_scx[17][9] = { 11 | { 63, 64, 64, 64, 64, 65, 65, 65, 63 }, 12 | { 63, 63, 63, 63, 63, 63, 64, 65, 63 }, 13 | { 63, 63, 63, 63, 63, 63, 65, 65, 63 }, 14 | { 63, 63, 63, 63, 63, 65, 65, 65, 63 }, 15 | { 62, 62, 63, 63, 64, 64, 64, 64, 62 }, 16 | { 62, 62, 63, 64, 64, 64, 64, 64, 62 }, 17 | { 62, 62, 64, 64, 64, 64, 64, 64, 62 }, 18 | { 62, 64, 64, 64, 64, 64, 64, 64, 62 }, 19 | 20 | { 63, 63, 63, 63, 63, 63, 64, 64, 63 }, 21 | { 63, 63, 63, 63, 63, 63, 64, 65, 63 }, 22 | { 63, 63, 63, 63, 63, 63, 65, 65, 63 }, 23 | { 63, 63, 63, 63, 63, 65, 65, 65, 63 }, 24 | { 62, 62, 63, 63, 64, 64, 64, 64, 62 }, 25 | { 62, 62, 63, 64, 64, 64, 64, 64, 62 }, 26 | { 62, 62, 64, 64, 64, 64, 64, 64, 62 }, 27 | { 62, 64, 64, 64, 64, 64, 64, 64, 62 }, 28 | 29 | { 63, 63, 63, 63, 63, 63, 64, 64, 63 }, 30 | }; 31 | 32 | void test_preloaded(MetroBoy& gameboy, const std::string& /*name*/) { 33 | uint8_t result = 0xFF; 34 | int phase = 0; 35 | const int timeout = 100000 * 8; // bits_ram_en needs lots of mcycles 36 | for (; phase < timeout; phase++) { 37 | gameboy.next_phase(); 38 | result = gameboy.vram.ram[0]; 39 | if (result) break; 40 | } 41 | 42 | if (phase == timeout) { 43 | printf("?"); 44 | } 45 | else if (result == 0x55) { 46 | printf("."); 47 | } 48 | else { 49 | printf("X"); 50 | } 51 | } 52 | 53 | void emit_test_scx_sprite(Assembler& l, int spriteX, int spriteY, int scroll, int delay, int result) { 54 | l.begin_block(0x150); 55 | l.lcd_off_unsafe(); 56 | l.clear_oam(); 57 | l.load_sprite(0, spriteY, spriteX, 0, 0); 58 | l.scx(scroll); 59 | l.lcd_on_sprites(); 60 | l.nops(112); 61 | l.nops(delay); 62 | l.test_finish_stat(result); 63 | } 64 | 65 | /* 66 | void test_scx_sprite() { 67 | Assembler* l = new Assembler(); 68 | Gameboy* g = new Gameboy(); 69 | 70 | for (int spriteX = 0; spriteX < 17; spriteX++) { 71 | const int spriteY = 16; 72 | for (int scrollX = 0; scrollX < 9; scrollX++) { 73 | const int delay = spritex_vs_scx[spriteX][scrollX]; 74 | const int result = 0x83; 75 | 76 | std::string name; 77 | sprintf(name, "sprite_vs_scx_%02d_%02d_%02d_%02d", spriteX, scrollX, delay, result); 78 | l->begin(name); 79 | 80 | emit_test_scx_sprite(*l, spriteX, spriteY, scrollX, delay, result); 81 | l->link(); 82 | 83 | g->reset(0x100); 84 | test_preloaded(*g, name); 85 | } 86 | printf("\n"); 87 | 88 | for (int scrollX = 0; scrollX < 9; scrollX++) { 89 | const int delay = spritex_vs_scx[spriteX][scrollX] + 1; 90 | const int result = 0x80; 91 | 92 | std::string name; 93 | sprintf(name, "sprite_vs_scx_%02d_%02d_%02d_%02d", spriteX, scrollX, delay, result); 94 | l->begin(name); 95 | 96 | emit_test_scx_sprite(*l, spriteX, spriteY, scrollX, delay, result); 97 | l->link(); 98 | 99 | g->reset(0x100); 100 | test_preloaded(*g, name); 101 | } 102 | printf("\n"); 103 | } 104 | } 105 | 106 | void test_codegen() { 107 | test_scx_sprite(); 108 | } 109 | */ 110 | -------------------------------------------------------------------------------- /src/Metronica/MetronicaApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //#include "metrolib/core/Assembler.h" 4 | //#include "metrolib/core/StateManager2.h" 5 | //#include "metrolib/core/SmoothTimer.h" 6 | 7 | #include "metrolib/appbase/App.h" 8 | #include "metrolib/appbase/GridPainter.h" 9 | #include "metrolib/appbase/TextPainter.h" 10 | #include "metrolib/appbase/DumpPainter.h" 11 | //#include "metrolib/appbase/GBBlitter.h" 12 | #include "metrolib/appbase/Blitter.h" 13 | 14 | //#include "GateBoyLib/GateBoy.h" 15 | //#include "GateBoyLib/GateBoyThread.h" 16 | 17 | class MetronicaApp : public App { 18 | public: 19 | 20 | const char* app_get_title() override { return "Metronica"; } 21 | void app_init(int screen_w, int screen_h) override; 22 | void app_close() override; 23 | bool pause_when_idle() override { return false; } 24 | 25 | void app_update(dvec2 screen_size, double delta) override; 26 | void app_render_frame(dvec2 screen_size, double delta) override; 27 | void app_render_ui(dvec2 /*screen_size*/, double /*delta*/) override {}; 28 | 29 | //---------- 30 | 31 | private: 32 | 33 | ViewController view_control; 34 | 35 | const uint8_t* keyboard_state = nullptr; 36 | 37 | GridPainter grid_painter = {}; 38 | TextPainter text_painter = {}; 39 | Blitter blitter = {}; 40 | 41 | int frame_count = 0; 42 | int replay_cursor = 0; 43 | 44 | double frame_begin = 0; 45 | double frame_end = 0; 46 | double frame_time = 0; 47 | double frame_time_smooth = 0; 48 | 49 | bool app_paused = false; 50 | 51 | uint64_t counter_start = 0; 52 | uint64_t counter_per_second = 0; 53 | uint64_t counter_old = 0; 54 | uint64_t counter_new = 0; 55 | uint64_t time_bucket = 0; 56 | uint64_t phase_old = 0; 57 | uint64_t phase_new = 0; 58 | 59 | int ram_tex = 0; 60 | }; 61 | -------------------------------------------------------------------------------- /src/verilog/.gitignore: -------------------------------------------------------------------------------- 1 | obj_dir 2 | -------------------------------------------------------------------------------- /src/verilog/IRAM.sv: -------------------------------------------------------------------------------- 1 | `ifndef IRAM_SV 2 | `define IRAM_SV 3 | 4 | `include "types.sv" 5 | 6 | import types::*; 7 | 8 | module IRAM(input bool clk, 9 | input bool rst, 10 | input CpuBus in_bus); 11 | 12 | parameter uint16_t ADDR_IRAM_BEGIN = 16'hC000; 13 | parameter uint16_t ADDR_IRAM_END = 16'hDFFF; 14 | parameter uint16_t ADDR_ECHO_BEGIN = 16'hE000; 15 | parameter uint16_t ADDR_ECHO_END = 16'hFFFF; 16 | 17 | typedef logic [12:0] ram_addr_t; 18 | uint8_t ram[8192]; 19 | BusOut out; 20 | 21 | task reset; 22 | for (integer i = 0; i < 8192; i++) ram[i] <= 0; 23 | out.data <= 0; 24 | out.oe <= 0; 25 | endtask; 26 | 27 | function BusOut tick; 28 | return out; 29 | endfunction 30 | 31 | task tock(CpuBus bus); 32 | ram_addr_t ram_addr = ram_addr_t'(bus.addr - ADDR_IRAM_BEGIN); 33 | out.data <= 0; 34 | out.oe <= 0; 35 | if (bus.addr < ADDR_IRAM_BEGIN) return; 36 | if (bus.read) out <= {ram[ram_addr], true}; 37 | if (bus.write) ram[ram_addr] <= bus.data; 38 | endtask; 39 | 40 | always @(posedge clk) begin 41 | if (rst) reset(); 42 | else tock(in_bus); 43 | end 44 | 45 | endmodule; 46 | 47 | `endif -------------------------------------------------------------------------------- /src/verilog/VRAM.sv: -------------------------------------------------------------------------------- 1 | `ifndef VRAM_SV 2 | `define VRAM_SV 3 | 4 | `include "types.sv" 5 | import types::*; 6 | 7 | //----------------------------------------------------------------------------- 8 | 9 | module VRAM ( 10 | input bool clk, 11 | input bool rst, 12 | input CpuBus bus 13 | ); 14 | 15 | parameter uint16_t ADDR_VRAM_BEGIN = 16'h8000; 16 | parameter uint16_t ADDR_VRAM_END = 16'h9FFF; 17 | 18 | typedef logic [12:0] ram_addr_t; 19 | uint8_t ram[8192]; 20 | BusOut out; 21 | 22 | //----------------------------------------------------------------------------- 23 | 24 | task reset; 25 | foreach(ram[i]) ram[i] <= 0; 26 | out.data <= 0; 27 | out.oe <= 0; 28 | endtask; 29 | 30 | //---------- 31 | 32 | function BusOut tick; 33 | return out; 34 | endfunction 35 | 36 | //---------- 37 | 38 | task tock(CpuBus bus); 39 | ram_addr_t ram_addr = ram_addr_t'(bus.addr - ADDR_VRAM_BEGIN); 40 | 41 | out.data <= 0; 42 | out.oe <= 0; 43 | if (bus.addr < ADDR_VRAM_BEGIN || bus.addr> ADDR_VRAM_END) return; 44 | if (bus.read) out <= {ram[ram_addr], true}; 45 | if (bus.write) ram[ram_addr] <= bus.data; 46 | endtask; 47 | 48 | //---------- 49 | 50 | always @(posedge clk) begin 51 | if (rst) reset(); 52 | else tock(bus); 53 | end 54 | 55 | endmodule; 56 | 57 | //----------------------------------------------------------------------------- 58 | 59 | `endif // VRAM_SV -------------------------------------------------------------------------------- /src/verilog/VRAM_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "obj_dir/VVRAM.h" 4 | 5 | double sc_time_stamp() { return 0; } 6 | 7 | /* 8 | int main(int argc, char** argv) { 9 | printf("----------\n"); 10 | 11 | Verilated::commandArgs(argc, argv); 12 | 13 | VVRAM vram; 14 | 15 | vram.rst = 0; 16 | 17 | for (int i = 0; i < 10; i++) { 18 | printf("cycle %d\n", i); 19 | vram.clk = 0; 20 | vram.eval(); 21 | vram.clk = 1; 22 | vram.eval(); 23 | } 24 | 25 | printf("----------\n"); 26 | printf("Done\n"); 27 | } 28 | */ -------------------------------------------------------------------------------- /src/verilog/VRAM_test.sh: -------------------------------------------------------------------------------- 1 | echo Cleaning 2 | rm -rf obj_dir 3 | echo Verilating 4 | verilator -Wall --cc VRAM.sv 5 | echo Compiling 6 | g++ -I/usr/local/share/verilator/include -o VRAM_test VRAM_test.cpp obj_dir/VVRAM.cpp obj_dir/VVRAM__Syms.cpp /usr/local/share/verilator/include/verilated.cpp 7 | echo Running 8 | ./VRAM_test 9 | 10 | -------------------------------------------------------------------------------- /src/verilog/classtest.sv: -------------------------------------------------------------------------------- 1 | class C; 2 | int x; 3 | task set (int i); 4 | x = i; 5 | endtask 6 | function int get; 7 | return x; 8 | endfunction 9 | endclass 10 | -------------------------------------------------------------------------------- /src/verilog/types.sv: -------------------------------------------------------------------------------- 1 | `ifndef TYPES_SV 2 | `define TYPES_SV 3 | 4 | package types; 5 | 6 | //----------------------------------------------------------------------------- 7 | 8 | typedef logic bool; 9 | typedef logic [7:0] uint8_t; 10 | typedef logic [15:0] uint16_t; 11 | typedef logic [31:0] uint32_t; 12 | typedef logic [15:0] sample_t; 13 | 14 | typedef logic signed [7:0] int8_t; 15 | 16 | parameter bool true=1; 17 | parameter bool false=0; 18 | 19 | `define bit_set(a,b) a[b] 20 | 21 | typedef struct packed { 22 | uint16_t addr; 23 | uint8_t data; 24 | bool read; 25 | bool write; 26 | } CpuBus; 27 | 28 | typedef struct packed { 29 | uint8_t data; 30 | bool oe; 31 | } BusOut; 32 | 33 | parameter uint8_t INT_VBLANK = 8'b00000001; 34 | parameter uint8_t INT_STAT = 8'b00000010; 35 | parameter uint8_t INT_TIMER = 8'b00000100; 36 | parameter uint8_t INT_SERIAL = 8'b00001000; 37 | parameter uint8_t INT_JOYPAD = 8'b00010000; 38 | 39 | /* verilator lint_off VARHIDDEN */ 40 | /* verilator lint_off IMPORTSTAR */ 41 | 42 | //----------------------------------------------------------------------------- 43 | 44 | endpackage; 45 | 46 | `endif // TYPES_V -------------------------------------------------------------------------------- /src/verilog/types.v: -------------------------------------------------------------------------------- 1 | typedef logic bool; 2 | typedef logic[7:0] uint8_t; 3 | typedef logic[15:0] uint16_t; 4 | typedef logic[31:0] uint32_t; 5 | -------------------------------------------------------------------------------- /src/verilog/wishbone_device.sv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/src/verilog/wishbone_device.sv -------------------------------------------------------------------------------- /src/verilog/wishbone_host.sv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aappleby/metroboy/36797ad4cf77b3e04ffe45716218a79b5280076a/src/verilog/wishbone_host.sv -------------------------------------------------------------------------------- /symlinks/gbmicrotest: -------------------------------------------------------------------------------- 1 | ../../gbmicrotest/ -------------------------------------------------------------------------------- /symlinks/metrolib: -------------------------------------------------------------------------------- 1 | ../../metrolib -------------------------------------------------------------------------------- /symlinks/metron: -------------------------------------------------------------------------------- 1 | ../../metron -------------------------------------------------------------------------------- /symlinks/third_party: -------------------------------------------------------------------------------- 1 | ../../third_party/ -------------------------------------------------------------------------------- /test.hancho: -------------------------------------------------------------------------------- 1 | """ 2 | ################################################################################ 3 | # GateBoyTests 4 | 5 | build obj/GateBoyTests.o : compile_cpp src/GateBoyTests/GateBoyTests.cpp 6 | 7 | build bin/GateBoyTests : c_binary $ 8 | obj/GateBoyTests.o $ 9 | bin/GateBoyLib.a $ 10 | symlinks/metrolib/bin/metrolib/libaudio.a $ 11 | symlinks/metrolib/bin/metrolib/libcore.a $ 12 | symlinks/metrolib/bin/metrolib/libgameboy.a 13 | libraries=-lSDL2 14 | 15 | 16 | ################################################################################ 17 | # MetroBoyTests 18 | 19 | build obj/MetroBoyTests.o : compile_cpp src/MetroBoyTests/MetroBoyTests.cpp 20 | build obj/test_codegen.o : compile_cpp src/MetroBoyTests/test_codegen.cpp 21 | build obj/test_mooneye.o : compile_cpp src/MetroBoyTests/test_mooneye.cpp 22 | build obj/test_screenshot.o : compile_cpp src/MetroBoyTests/test_screenshot.cpp 23 | 24 | build bin/MetroBoyTests : c_binary $ 25 | obj/MetroBoyTests.o $ 26 | obj/test_codegen.o $ 27 | obj/test_mooneye.o $ 28 | obj/test_screenshot.o $ 29 | bin/MetroBoyLib.a $ 30 | symlinks/metrolib/bin/metrolib/libcore.a $ 31 | symlinks/metrolib/bin/metrolib/libgameboy.a 32 | 33 | """ -------------------------------------------------------------------------------- /tests/cpu_instrs/source/01-special.s: -------------------------------------------------------------------------------- 1 | ; Tests instructions that don't fit template 2 | 3 | .include "shell.inc" 4 | 5 | main: 6 | set_test 2,"JR negative" 7 | ld a,0 8 | jp jr_neg 9 | inc a 10 | - inc a 11 | inc a 12 | cp 2 13 | jp nz,test_failed 14 | jp + 15 | jr_neg: 16 | jr - 17 | + 18 | 19 | set_test 3,"JR positive" 20 | ld a,0 21 | jr + 22 | inc a 23 | + inc a 24 | inc a 25 | cp 2 26 | jp nz,test_failed 27 | 28 | 29 | set_test 4,"LD PC,HL" 30 | ld hl,+ 31 | ld a,0 32 | ld pc,hl 33 | inc a 34 | + inc a 35 | inc a 36 | cp 2 37 | jp nz,test_failed 38 | 39 | 40 | set_test 5,"POP AF" 41 | ld bc,$1200 42 | - push bc 43 | pop af 44 | push af 45 | pop de 46 | ld a,c 47 | and $F0 48 | cp e 49 | jp nz,test_failed 50 | inc b 51 | inc c 52 | jr nz,- 53 | 54 | 55 | set_test 6,"DAA" 56 | ; Test all combinations of A and flags (256*16 total) 57 | ld de,0 58 | - push de 59 | pop af 60 | daa 61 | 62 | push af 63 | call update_crc 64 | pop hl 65 | ld a,l 66 | call update_crc 67 | 68 | inc d 69 | jr nz,- 70 | 71 | ld a,e 72 | add $10 73 | ld e,a 74 | jr nz,- 75 | 76 | check_crc $6A9F8D8A 77 | 78 | jp tests_passed 79 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/02-interrupts.s: -------------------------------------------------------------------------------- 1 | ; Tests DI, EI, and HALT (STOP proved untestable) 2 | 3 | .include "shell.inc" 4 | 5 | main: 6 | wreg IE,$04 7 | 8 | set_test 2,"EI" 9 | ei 10 | ld bc,0 11 | push bc 12 | pop bc 13 | inc b 14 | wreg IF,$04 15 | interrupt_addr: 16 | dec b 17 | jp nz,test_failed 18 | ld hl,sp-2 19 | ldi a,(hl) 20 | cp interrupt_addr 24 | jp nz,test_failed 25 | lda IF 26 | and $04 27 | jp nz,test_failed 28 | 29 | set_test 3,"DI" 30 | di 31 | ld bc,0 32 | push bc 33 | pop bc 34 | wreg IF,$04 35 | ld hl,sp-2 36 | ldi a,(hl) 37 | or (hl) 38 | jp nz,test_failed 39 | lda IF 40 | and $04 41 | jp z,test_failed 42 | 43 | set_test 4,"Timer doesn't work" 44 | wreg TAC,$05 45 | wreg TIMA,0 46 | wreg IF,0 47 | delay 500 48 | lda IF 49 | delay 500 50 | and $04 51 | jp nz,test_failed 52 | delay 500 53 | lda IF 54 | and $04 55 | jp z,test_failed 56 | pop af 57 | 58 | set_test 5,"HALT" 59 | wreg TAC,$05 60 | wreg TIMA,0 61 | wreg IF,0 62 | halt ; timer interrupt will exit halt 63 | nop ; avoids DMG bug 64 | lda IF 65 | and $04 66 | jp z,test_failed 67 | 68 | jp tests_passed 69 | 70 | .bank 0 slot 0 71 | .org $50 72 | inc a 73 | ret 74 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/03-op sp,hl.s: -------------------------------------------------------------------------------- 1 | ; Tests SP/HL instructions 2 | 3 | ;.define PRINT_CHECKSUMS 1 4 | .include "shell.inc" 5 | .include "instr_test.s" 6 | 7 | instrs: 8 | .byte $33,0,0 ; INC SP 9 | .byte $3B,0,0 ; DEC SP 10 | .byte $39,0,0 ; ADD HL,SP 11 | .byte $F9,0,0 ; LD SP,HL 12 | .byte $E8,$01,0 ; ADD SP,1 13 | .byte $E8,$FF,0 ; ADD SP,-1 14 | .byte $F8,$01,0 ; LD HL,SP+1 15 | .byte $F8,$FF,0 ; LD HL,SP-1 16 | instrs_end: 17 | 18 | test_instr: 19 | ; C = flags register 20 | ld c,$00 21 | call test 22 | ld c,$F0 23 | call test 24 | ret 25 | 26 | test: 27 | ; Go through each value for HL 28 | ld hl,values 29 | hl_loop: 30 | ld e,(hl) 31 | inc hl 32 | ld d,(hl) 33 | inc hl 34 | push hl 35 | 36 | ; Go through each value for SP 37 | ld hl,values 38 | values_loop: 39 | push bc 40 | push de 41 | push hl 42 | 43 | push bc 44 | pop af 45 | 46 | ; Switch stack 47 | ld (temp),sp 48 | ld a,(hl+) 49 | ld h,(hl) 50 | ld l,a 51 | ; call print_regs 52 | ld sp,hl 53 | 54 | ; Set registers 55 | ld h,d 56 | ld l,e 57 | ld a,$12 58 | ld bc,$5691 59 | ld de,$9ABC 60 | 61 | jp instr 62 | instr_done: 63 | ; Save new SP and switch to yet another stack 64 | ld (temp+2),sp 65 | ld sp,$DF70 66 | 67 | call checksum_af_bc_de_hl 68 | 69 | ; Checksum SP 70 | ld a,(temp+2) 71 | call update_crc_fast 72 | ld a,(temp+3) 73 | call update_crc_fast 74 | 75 | ldsp temp 76 | 77 | pop hl 78 | pop de 79 | pop bc 80 | inc hl 81 | inc hl 82 | ld a,l 83 | cp taken ; JP NZ,taken 18 | .byte $C3,taken ; JP taken 19 | .byte $CA,taken ; JP Z,taken 20 | .byte $D2,taken ; JP NC,taken 21 | .byte $DA,taken ; JP C,taken 22 | 23 | .byte $C4,taken ; CALL NZ,taken 24 | .byte $CC,taken ; CALL Z,taken 25 | .byte $CD,taken ; CALL taken 26 | .byte $D4,taken ; CALL NC,taken 27 | .byte $DC,taken ; CALL C,taken 28 | 29 | ; RET cond 30 | ; INC A 31 | .byte $C0,$3C,0 ; RET NZ 32 | .byte $C8,$3C,0 ; RET Z 33 | .byte $C9,$3C,0 ; RET 34 | .byte $D0,$3C,0 ; RET NC 35 | .byte $D8,$3C,0 ; RET C 36 | .byte $D9,$3C,0 ; RETI 37 | 38 | ; RST 39 | ; can only easily test this one on devcart 40 | .byte $C7,0,0 ; RST $00 41 | .ifndef BUILD_DEVCART 42 | .byte $CF,0,0 ; RST $08 43 | .byte $D7,0,0 ; RST $10 44 | .byte $DF,0,0 ; RST $18 45 | .byte $E7,0,0 ; RST $20 46 | .byte $EF,0,0 ; RST $28 47 | .byte $F7,0,0 ; RST $30 48 | .byte $FF,0,0 ; RST $38 49 | .endif 50 | 51 | instrs_end: 52 | 53 | test_instr: 54 | wreg IE,0 ; disable interrupts, since RETI does EI 55 | 56 | ; Go through all 16 combinations of flags 57 | ld bc,$1200 58 | - 59 | ; Fill 4 bytes of new stack 60 | ld a,$12 61 | ld ($DF80-2),a 62 | ld a,$34 63 | ld ($DF80-3),a 64 | ld a,$56 65 | ld ($DF80-4),a 66 | ld a,$78 67 | ld ($DF80-5),a 68 | 69 | ; Set AF 70 | push bc 71 | pop af 72 | 73 | ; Switch to new stack 74 | ld (temp),sp 75 | ld sp,$DF80 76 | 77 | ; Set return address 78 | ld de,instr+3 79 | push de 80 | 81 | jp instr 82 | instr_done: 83 | inc a 84 | taken: 85 | di ; RETI enables interrupts 86 | 87 | ; Save new SP and switch to yet another stack 88 | ld (temp+2),sp 89 | ld sp,$DF70 90 | 91 | ; Checksum A and SP 92 | call update_crc_fast 93 | ld a,(temp+2) 94 | call update_crc_fast 95 | ld a,(temp+3) 96 | call update_crc_fast 97 | 98 | ; Checksum 4 bytes of stack 99 | ld a,($DF80-2) 100 | call update_crc_fast 101 | ld a,($DF80-3) 102 | call update_crc_fast 103 | ld a,($DF80-4) 104 | call update_crc_fast 105 | ld a,($DF80-5) 106 | call update_crc_fast 107 | 108 | ldsp temp 109 | 110 | ld a,c 111 | add $10 112 | ld c,a 113 | jr nz,- 114 | 115 | ret 116 | 117 | checksums: 118 | .byte $EC,$A4,$94,$79,$C4,$00,$96,$2C,$C4,$64,$90,$33,$77,$C7,$0A,$D4 119 | .byte $77,$A3,$0C,$CB,$79,$E7,$7E,$AE,$DA,$DC,$03,$F7,$4F,$9F,$E9,$20 120 | .byte $72,$12,$DA,$01,$44,$6A,$4D,$8F,$D1,$79,$30,$4C,$AA,$37,$F2,$6A 121 | .byte $97,$EA,$56,$5F,$32,$28,$C7,$D1,$49,$66,$05,$F7,$80,$0F,$BA,$8E 122 | .byte $41,$E2,$A4,$9A,$2D,$2D,$8C,$72,$A5,$13,$76,$A8,$64,$FE,$68,$BC 123 | .byte $2D,$2D,$8C,$72,$50,$96,$24,$27,$50,$96,$24,$27,$50,$96,$24,$27 124 | .byte $50,$96,$24,$27,$50,$96,$24,$27,$50,$96,$24,$27,$50,$96,$24,$27 125 | .byte $50,$96,$24,$27 126 | 127 | .include "multi_custom.s" 128 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/08-misc instrs.s: -------------------------------------------------------------------------------- 1 | ; Tests miscellaneous instructions 2 | 3 | ;.define PRINT_CHECKSUMS 1 4 | .include "shell.inc" 5 | .include "instr_test.s" 6 | 7 | instrs: 8 | .byte $F0,$91,0 ; LDH A,($91) 9 | .byte $E0,$91,0 ; LDH ($91),A 10 | .byte $F2,$00,0 ; LDH A,(C) 11 | .byte $E2,$00,0 ; LDH (C),A 12 | .byte $FA,$91,$FF ; LD A,($FF91) 13 | .byte $EA,$91,$FF ; LD ($FF91),A 14 | .byte $08,$91,$FF ; LD ($FF91),SP 15 | .byte $01,$23,$01 ; LD BC,$0123 16 | .byte $11,$23,$01 ; LD DE,$0123 17 | .byte $21,$23,$01 ; LD HL,$0123 18 | .byte $31,$23,$01 ; LD SP,$0123 19 | .byte $F5,0,0 ; PUSH AF 20 | .byte $C5,0,0 ; PUSH BC 21 | .byte $D5,0,0 ; PUSH DE 22 | .byte $E5,0,0 ; PUSH HL 23 | .byte $F1,0,0 ; POP AF 24 | .byte $C1,0,0 ; POP BC 25 | .byte $D1,0,0 ; POP DE 26 | .byte $E1,0,0 ; POP HL 27 | instrs_end: 28 | 29 | test_instr: 30 | ; C = flags register 31 | ld c,$00 32 | call test 33 | ld c,$10 34 | call test 35 | ld c,$E0 36 | call test 37 | ld c,$F0 38 | call test 39 | ret 40 | 41 | test: 42 | ; Fill RAM 43 | ld a,$FE 44 | ld ($FF90),a 45 | ld a,$DC 46 | ld ($FF91),a 47 | ld a,$BA 48 | ld ($FF92),a 49 | 50 | ; Fill stack 51 | ld a,$13 52 | ld ($DF80),a 53 | ld a,$57 54 | ld ($DF80-1),a 55 | ld a,$9B 56 | ld ($DF80-2),a 57 | ld a,$DF 58 | ld ($DF80-3),a 59 | 60 | ; Set registers 61 | ld b,$12 62 | push bc 63 | ld bc,$5691 64 | ld de,$9ABC 65 | ld hl,$DEF0 66 | pop af 67 | 68 | ; Switch stack 69 | ld (temp),sp 70 | ld sp,$DF80-2 71 | 72 | jp instr 73 | instr_done: 74 | ; Save new SP and switch to another stack 75 | ld (temp+2),sp 76 | ld sp,$DF70 77 | 78 | call checksum_af_bc_de_hl 79 | 80 | ; Checksum SP 81 | ld a,(temp+2) 82 | call update_crc_fast 83 | ld a,(temp+3) 84 | call update_crc_fast 85 | 86 | ; Checksum RAM 87 | ld a,($FF90) 88 | call update_crc_fast 89 | ld a,($FF91) 90 | call update_crc_fast 91 | ld a,($FF92) 92 | call update_crc_fast 93 | 94 | ; Checksum stack 95 | ld a,($DF80) 96 | call update_crc_fast 97 | ld a,($DF80-1) 98 | call update_crc_fast 99 | ld a,($DF80-2) 100 | call update_crc_fast 101 | ld a,($DF80-3) 102 | call update_crc_fast 103 | 104 | ; Restore SP 105 | ldsp temp 106 | 107 | ret 108 | 109 | checksums: 110 | .byte $4D,$FF,$15,$97,$6D,$A7,$35,$65,$4D,$FF,$15,$97,$6D,$A7,$35,$65,$4D,$FF,$15,$97,$6D,$A7,$35,$65,$AD,$FA,$5E,$41,$D0,$78,$79,$C1,$AF,$66,$99,$34,$0D,$E1,$97,$99,$6F,$D0,$6F,$5D,$C3,$1F,$A3,$8A,$C2,$F1,$9C,$F3,$C1,$C3,$DC,$78,$C0,$2D,$E3,$01,$8F,$C4,$0F,$44,$95,$22,$6A,$39,$61,$C5,$AB,$55,$FB,$DF,$2C,$52, 111 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/common/checksums.s: -------------------------------------------------------------------------------- 1 | ; Multiple checksum table handling 2 | 3 | .define next_checksum bss+0 4 | .redefine bss bss+2 5 | 6 | ; If PRINT_CHECKSUMS is defined, checksums are printed 7 | ; rather than compared. 8 | 9 | ; Initializes multiple checksum handler to use checksums 10 | ; table (defined by user). 11 | ; Preserved: BC, DE, HL 12 | checksums_init: 13 | ld a,checksums 16 | ld (next_checksum+1),a 17 | ret 18 | 19 | ; Compares current checksum with next checksum in 20 | ; list. Z if they match, NZ if not. 21 | ; Preserved: BC, DE, HL 22 | checksums_compare: 23 | .ifdef PRINT_CHECKSUMS 24 | lda checksum+3 25 | push af 26 | lda checksum+2 27 | push af 28 | lda checksum+1 29 | push af 30 | lda checksum+0 31 | push af 32 | 33 | ld a,(next_checksum) 34 | inc a 35 | ld (next_checksum),a 36 | sub 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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/common/crc_fast.s: -------------------------------------------------------------------------------- 1 | ; Fast table-based CRC-32 2 | 3 | .define crc_tables (bss+$FF)&$FF00 ; 256-byte aligned 4 | .redefine bss crc_tables+$400 5 | 6 | 7 | ; Initializes fast CRC tables and resets checksum. 8 | ; Time: 47 msec 9 | init_crc_fast: 10 | ld l,0 11 | @next: 12 | xor a 13 | ld c,a 14 | ld d,a 15 | ld e,l 16 | 17 | ld h,8 18 | - rra 19 | rr c 20 | rr d 21 | rr e 22 | jr nc,+ 23 | xor $ED 24 | ld b,a 25 | ld a,c 26 | xor $B8 27 | ld c,a 28 | ld a,d 29 | xor $83 30 | ld d,a 31 | ld a,e 32 | xor $20 33 | ld e,a 34 | ld a,b 35 | 36 | + dec h 37 | jr nz,- 38 | 39 | ld h,>crc_tables 40 | ld (hl),e 41 | inc h 42 | ld (hl),d 43 | inc h 44 | ld (hl),c 45 | inc h 46 | ld (hl),a 47 | 48 | inc l 49 | jr nz,@next 50 | 51 | jp init_crc 52 | 53 | 54 | ; Faster version of update_crc 55 | ; Preserved: BC, DE 56 | ; Time: 50 cycles (including CALL) 57 | update_crc_fast: 58 | 59 | ; Fastest inline macro version of update_crc_fast 60 | ; Time: 40 cycles 61 | ; Size: 28 bytes 62 | .macro update_crc_fast 63 | ld l,a ; 1 64 | lda checksum ; 3 65 | xor l ; 1 66 | ld l,a ; 1 67 | ld h,>crc_tables ; 2 68 | 69 | lda checksum+1 ; 3 70 | xor (hl) ; 2 71 | inc h ; 1 72 | sta checksum ; 3 73 | 74 | lda checksum+2 ; 3 75 | xor (hl) ; 2 76 | inc h ; 1 77 | sta checksum+1 ; 3 78 | 79 | lda checksum+3 ; 3 80 | xor (hl) ; 2 81 | inc h ; 1 82 | sta checksum+2 ; 3 83 | 84 | ld a,(hl) ; 2 85 | sta checksum+3 ; 3 86 | .endm 87 | update_crc_fast 88 | ret 89 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/common/instr_test.s: -------------------------------------------------------------------------------- 1 | ; Framework for CPU instruction tests 2 | 3 | ; Calls test_instr with each instruction copied 4 | ; to instr, with a JP instr_done after it. 5 | ; Verifies checksum after testing instruction and 6 | ; prints opcode if it's wrong. 7 | 8 | .include "checksums.s" 9 | .include "cpu_speed.s" 10 | .include "apu.s" 11 | .include "crc_fast.s" 12 | 13 | .define instr $DEF8 14 | .define rp_temp (instr-4) 15 | 16 | .define temp bss 17 | 18 | ; Sets SP to word at addr 19 | ; Preserved: BC, DE 20 | .macro ldsp ; addr 21 | ld a,(\1) 22 | ld l,a 23 | ld a,((\1)+1) 24 | ld h,a 25 | ld sp,hl 26 | .endm 27 | 28 | main: 29 | call cpu_fast 30 | call init_crc_fast 31 | call checksums_init 32 | set_test 0 33 | 34 | ld hl,instrs 35 | - ; Copy instruction 36 | ld a,(hl+) 37 | ld (instr),a 38 | ld a,(hl+) 39 | ld (instr+1),a 40 | ld a,(hl+) 41 | ld (instr+2),a 42 | push hl 43 | 44 | ; Put JP instr_done after it 45 | ld a,$C3 46 | ld (instr+3),a 47 | ld a,instr_done 50 | ld (instr+5),a 51 | 52 | call reset_crc 53 | call test_instr 54 | 55 | call checksums_compare 56 | jr z,passed 57 | 58 | set_test 1 59 | ld a,(instr) 60 | call print_a 61 | cp $CB 62 | jr nz,+ 63 | ld a,(instr+1) 64 | call print_a 65 | + 66 | 67 | passed: 68 | ; Next instruction 69 | pop hl 70 | ld a,l 71 | cp instrs_end 75 | jr nz,- 76 | 77 | jp tests_done 78 | 79 | 80 | ; Updates checksum with AF, BC, DE, and HL 81 | checksum_af_bc_de_hl: 82 | push hl 83 | 84 | push af 85 | update_crc_fast 86 | pop hl 87 | ld a,l 88 | update_crc_fast 89 | 90 | ld a,b 91 | update_crc_fast 92 | ld a,c 93 | update_crc_fast 94 | 95 | ld a,d 96 | update_crc_fast 97 | ld a,e 98 | update_crc_fast 99 | 100 | pop de 101 | ld a,d 102 | update_crc_fast 103 | ld a,e 104 | update_crc_fast 105 | ret 106 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/common/multi_custom.s: -------------------------------------------------------------------------------- 1 | ; RST handlers 2 | .bank 0 slot 0 3 | .org 0 4 | inc a 5 | ret 6 | .ds 6,0 7 | inc a 8 | ret 9 | .ds 6,0 10 | inc a 11 | ret 12 | .ds 6,0 13 | inc a 14 | ret 15 | .ds 6,0 16 | inc a 17 | ret 18 | .ds 6,0 19 | inc a 20 | ret 21 | .ds 6,0 22 | inc a 23 | ret 24 | .ds 6,0 25 | inc a 26 | ret 27 | .ds 6,0 28 | inc a 29 | ret 30 | .ds 6,0 31 | inc a 32 | ret 33 | .ds 6,0 34 | inc a 35 | ret 36 | .ds 6,0 37 | inc a 38 | ret 39 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/cpu_instrs/source/linkfile: -------------------------------------------------------------------------------- 1 | [objects] 2 | test.o 3 | -------------------------------------------------------------------------------- /tests/cpu_instrs/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 | -------------------------------------------------------------------------------- /tests/micro_audio/audio1.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | 4 | ; Square 1 5 | ; NR10 FF10 -PPPNSSS Sweep period, negate, shift 6 | ; NR11 FF11 DDLLLLLL Duty, Length load (64-L) 7 | ; NR12 FF12 VVVVAPPP Starting volume, Envelope add mode, period 8 | ; NR13 FF13 FFFFFFFF Frequency LSB 9 | ; NR14 FF14 TL---FFF Trigger, Length enable, Frequency MSB 10 | 11 | ; starting with sweep off then turning it on after trigger does something weird 12 | 13 | ; starting with %01110000 and changing to %01110111 after trigger is super weird 14 | ; starting with %01111000 and changing to %01111111 after trigger also super weird, sounds like above 15 | 16 | main: 17 | 18 | ; turn audio on 19 | ld a, %11111111 20 | ldh ($25), a 21 | ldh ($26), a 22 | 23 | ; FF10 -PPPNSSS Sweep period, negate, shift 24 | ld a, %01111000 25 | ldh ($10), a 26 | 27 | ; FF11 DDLLLLLL Duty, Length load (64-L) 28 | ld a, %10000000 29 | ldh ($11), a 30 | 31 | ; FF12 VVVVAPPP Starting volume, Envelope add mode, period 32 | ld a, %11110000 33 | ldh ($12), a 34 | 35 | ; FF13 FFFFFFFF Frequency LSB 36 | ld a, %01111111 37 | ldh ($13), a 38 | 39 | ; FF14 TL---FFF Trigger, Length enable, Frequency MSB 40 | ld a, %11000111 41 | ldh ($14), a 42 | 43 | ; FF10 -PPPNSSS Sweep period, negate, shift 44 | ld a, %01111111 45 | ldh ($10), a 46 | 47 | end: 48 | jr end 49 | -------------------------------------------------------------------------------- /tests/micro_audio/audio2.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | 4 | ; Square 2 5 | ; NR21 FF16 DDLLLLLL Duty, Length load (64-L) 6 | ; NR22 FF17 VVVVAPPP Starting volume, Envelope add mode, period 7 | ; NR23 FF18 FFFFFFFF Frequency LSB 8 | ; NR24 FF19 TL---FFF Trigger, Length enable, Frequency MSB 9 | 10 | main: 11 | 12 | ; turn audio on 13 | ld a, %11111111 14 | ldh ($25), a 15 | ldh ($26), a 16 | 17 | ; FF16 DDLLLLLL Duty, Length load (64-L) 18 | ld a, %10000000 19 | ldh ($16), a 20 | 21 | ; FF17 VVVVAPPP Starting volume, Envelope add mode, period 22 | ld a, %11110000 23 | ldh ($17), a 24 | 25 | ; FF18 FFFFFFFF Frequency LSB 26 | ld a, %01111111 27 | ldh ($18), a 28 | 29 | ; FF19 TL---FFF Trigger, Length enable, Frequency MSB 30 | ld a, %10000111 31 | ldh ($19), a 32 | 33 | end: 34 | jr end 35 | -------------------------------------------------------------------------------- /tests/micro_audio/audio3.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | 4 | ; Wave 5 | ; NR30 FF1A E--- ---- DAC power 6 | ; NR31 FF1B LLLL LLLL Length load (256-L) 7 | ; NR32 FF1C -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%) 8 | ; NR33 FF1D FFFF FFFF Frequency LSB 9 | ; NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB 10 | 11 | ; DAC power 12 | ; Length 13 | ; Volume 14 | ; Frequency LSB 15 | ; Frequency MSB - can change 16 | ; Length enable 17 | ; Trigger 18 | 19 | main: 20 | 21 | ; turn audio on 22 | ld a, %11111111 23 | ldh ($25), a 24 | ldh ($26), a 25 | 26 | ; load waveform 27 | ld a, $FF 28 | ldh ($30), a 29 | ldh ($31), a 30 | ldh ($32), a 31 | ldh ($33), a 32 | ldh ($34), a 33 | ldh ($35), a 34 | ldh ($36), a 35 | ldh ($37), a 36 | ld a, $00 37 | ldh ($38), a 38 | ldh ($39), a 39 | ldh ($3A), a 40 | ldh ($3B), a 41 | ldh ($3C), a 42 | ldh ($3D), a 43 | ldh ($3E), a 44 | ldh ($3F), a 45 | 46 | ; FF1A E------- DAC power 47 | ld a, %10000000 48 | ldh ($1A), a 49 | 50 | ; FF1B LLLLLLLL Length load (256-L) 51 | ld a, %11111111 52 | ldh ($1B), a 53 | 54 | ; FF1C -VV----- Volume code (00=0%, 01=100%, 10=50%, 11=25%) 55 | ld a, %00100000 56 | ldh ($1C), a 57 | 58 | ; FF1D FFFFFFFF Frequency LSB 59 | ld a, %11111111 60 | ldh ($1D), a 61 | 62 | ; FF1E TL---FFF Trigger, Length enable, Frequency MSB 63 | ld a, %11000011 64 | ldh ($1E), a 65 | 66 | ; FF1A E------- DAC power 67 | ld a, %00000000 68 | ldh ($1A), a 69 | 70 | end: 71 | jr end 72 | -------------------------------------------------------------------------------- /tests/micro_audio/audio4.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | 4 | ; Noise 5 | ; FF1F ---- ---- Not used 6 | ; NR41 FF20 --LL LLLL Length load (64-L) 7 | ; NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period 8 | ; NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code 9 | ; NR44 FF23 TL-- ---- Trigger, Length enable 10 | 11 | 12 | ; Noise 13 | ; FF1F ---- ---- Not used 14 | ; NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period 15 | ; NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code 16 | ; NR44 FF23 TL-- ---- Trigger, Length enable 17 | 18 | 19 | 20 | ; noise - 21 | ; reloading length register does _not_ extend sound 22 | ; continuously retriggering stops the sound 23 | ; continuously setting length enable bit stops the sound 24 | ; can change shift while noise is playing 25 | 26 | ; if length enabled 27 | ; continuous 28 | ; length - writable (63-L-1) 29 | ; volume - is weird 30 | ; env direction 31 | ; env period 32 | ; shift 33 | ; width 34 | ; divisor 35 | ; once 36 | ; length - writable (63-L-1) 37 | ; volume - is weird 38 | ; env direction - can't change 39 | ; env period 40 | ; shift 41 | ; width 42 | ; divisor 43 | 44 | 45 | ; if length not enabled 46 | ; length 47 | ; volume 48 | ; env direction 49 | ; env period 50 | ; shift 51 | ; width 52 | ; divisor 53 | 54 | ; cannot change volume during sound at all? 55 | ; cannot increase volume during playback? 56 | ; can change env period during sound 57 | ; cannot change env dir during sound 58 | 59 | ; length enabled, once - 60 | ; 1: 61 | ; 62: 62 | ; 63: ........ ........ ........ ........ s....... ........ ........ ....xxxx 63 | ; seems like writing once when env is 63 clears bits (xor) 64 | 65 | ; length is always writable, but actual sound length is _63_-_L_-_1_, wrapping around to 63 if L = 63 66 | 67 | ; can change dir/period during sound, but still acts weird 68 | ; if volume = 0 and env = down, sound never starts 69 | 70 | 71 | 72 | main: 73 | 74 | ; turn audio on 75 | ld a, %11111111 76 | ldh ($25), a 77 | ldh ($26), a 78 | 79 | ; FF20 --LLLLLL Length load (63-L-1) 80 | ld a, %00000000 81 | ldh ($20), a 82 | 83 | ; FF21 VVVVAPPP Starting volume, Envelope add mode, period 84 | ld a, %11110000 85 | ldh ($21), a 86 | 87 | ; FF22 SSSSWDDD Clock shift, Width mode of LFSR, Divisor code 88 | ld a, %00100111 89 | ldh ($22), a 90 | 91 | ; FF23 TL------ Trigger, Length enable 92 | ld a, %10000000 93 | ldh ($23), a 94 | 95 | ; FF23 TL------ Trigger, Length enable 96 | ld a, %11000000 97 | ldh ($23), a 98 | 99 | end: 100 | jr end 101 | -------------------------------------------------------------------------------- /tests/micro_audio/makefile: -------------------------------------------------------------------------------- 1 | ALL_GB_SOURCE = $(wildcard *.s) 2 | ALL_GB_OBJ = $(patsubst %.s,%.o, $(ALL_GB_SOURCE)) 3 | ALL_GB_BINARY = $(patsubst %.s,%.gb, $(ALL_GB_SOURCE)) 4 | ALL_GB_LINK = $(patsubst %.s,%.link, $(ALL_GB_SOURCE)) 5 | 6 | DMG_GB_LINK = $(addprefix build/dmg/, $(ALL_GB_LINK)) 7 | 8 | DMG_GB_BINARY = $(addprefix build/dmg/, $(ALL_GB_BINARY)) 9 | AGS_GB_BINARY = $(addprefix build/ags/, $(ALL_GB_BINARY)) 10 | 11 | all: $(DMG_GB_BINARY) 12 | 13 | #all: $(AGS_GB_BINARY) $(DMG_GB_BINARY) 14 | 15 | #all: $(AGS_GB_BINARY) 16 | 17 | dmg_link: $(DMG_GB_LINK) 18 | 19 | build/dmg/%.link: %.s 20 | @echo $@ 21 | @echo [objects] > $@ 22 | @echo $(patsubst %.link,%.o, $(notdir $@)) >> $@ 23 | 24 | build/dmg/%.gb: %.s 25 | @echo $@ 26 | @wla-gb -DDMG -o build/temp.o $< 27 | @wlalink -S temp.link $@ 28 | 29 | build/ags/%.gb: %.s 30 | @echo $@ 31 | @wla-gb -DAGS -o build/temp.o $< 32 | @wlalink -S temp.link $@ 33 | 34 | clean: 35 | @rm -f build/ags/* 36 | @rm -f build/dmg/* 37 | @rm -f build/temp.o 38 | 39 | .PHONY: all clean 40 | -------------------------------------------------------------------------------- /tests/micro_audio/spu_env_change.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | 4 | ; Square 1 5 | ; NR10 FF10 -PPPNSSS Sweep period, negate, shift 6 | ; NR11 FF11 DDLLLLLL Duty, Length load (64-L) 7 | ; NR12 FF12 VVVVAPPP Starting volume, Envelope add mode, period 8 | ; NR13 FF13 FFFFFFFF Frequency LSB 9 | ; NR14 FF14 TL---FFF Trigger, Length enable, Frequency MSB 10 | 11 | ; FF76 - pcm12 12 | ; FF77 - pcm34 13 | 14 | ; envelope 15 | 16 | .macro nr10 ARGS p n s 17 | ld a, (p << 4) | (n << 3) | s 18 | ldh ($10), a 19 | .endm 20 | 21 | .macro nr11 ARGS d l 22 | ld a, (d << 6) | l 23 | ldh ($11), a 24 | .endm 25 | 26 | .macro nr12 ARGS v a p 27 | ld a, (v << 4) | (a << 3) | p 28 | ldh ($12), a 29 | .endm 30 | 31 | .macro trigger ARGS freq l 32 | ld a, freq & $FF 33 | ldh ($13), a 34 | ld a, $80 | (l << 6) | (freq >> 8) 35 | ldh ($14), a 36 | .endm 37 | 38 | ;------------------------------------------------------------------------------- 39 | 40 | main: 41 | ld a, $FF 42 | ldh ($25), a 43 | ldh ($26), a 44 | 45 | nr10 0 0 0 46 | nr11 2 0 47 | 48 | nr12 15 0 0 49 | 50 | ; 18 51 | trigger (2047 - 47) 0 52 | 53 | ;nr12 10 1 0 54 | ;nr12 10 0 0 55 | 56 | end: 57 | ldh a, ($76) 58 | and $0F 59 | ld ($8000), a 60 | ldh a, ($26) 61 | and $0F 62 | ld ($8002), a 63 | jr end 64 | -------------------------------------------------------------------------------- /tests/micro_cpu/.gitignore: -------------------------------------------------------------------------------- 1 | *.sym 2 | -------------------------------------------------------------------------------- /tests/micro_cpu/cpu_mov.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | ld b, 10 5 | ld a, b 6 | jp main -------------------------------------------------------------------------------- /tests/micro_cpu/cpu_nop.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | nop 5 | jp main -------------------------------------------------------------------------------- /tests/micro_cpu/cpu_zeropage.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | ld a, 10 5 | ldh ($80), a 6 | ld a, 0 7 | ldh a,($80) 8 | jp main -------------------------------------------------------------------------------- /tests/micro_cpu/makefile: -------------------------------------------------------------------------------- 1 | ALL_GB_SOURCE = $(wildcard *.s) 2 | ALL_GB_OBJ = $(patsubst %.s,%.o, $(ALL_GB_SOURCE)) 3 | ALL_GB_BINARY = $(patsubst %.s,%.gb, $(ALL_GB_SOURCE)) 4 | ALL_GB_LINK = $(patsubst %.s,%.link, $(ALL_GB_SOURCE)) 5 | 6 | DMG_GB_LINK = $(addprefix build/dmg/, $(ALL_GB_LINK)) 7 | 8 | DMG_GB_BINARY = $(addprefix build/dmg/, $(ALL_GB_BINARY)) 9 | AGS_GB_BINARY = $(addprefix build/ags/, $(ALL_GB_BINARY)) 10 | 11 | all: $(DMG_GB_BINARY) 12 | 13 | #all: $(AGS_GB_BINARY) $(DMG_GB_BINARY) 14 | 15 | #all: $(AGS_GB_BINARY) 16 | 17 | dmg_link: $(DMG_GB_LINK) 18 | 19 | build/dmg/%.link: %.s 20 | @echo $@ 21 | @echo [objects] > $@ 22 | @echo $(patsubst %.link,%.o, $(notdir $@)) >> $@ 23 | 24 | build/dmg/%.gb: %.s 25 | @echo $@ 26 | @wla-gb -DDMG -o build/temp.o $< 27 | @wlalink -S temp.link $@ 28 | 29 | build/ags/%.gb: %.s 30 | @echo $@ 31 | @wla-gb -DAGS -o build/temp.o $< 32 | @wlalink -S temp.link $@ 33 | 34 | clean: 35 | @rm -f build/ags/* 36 | @rm -f build/dmg/* 37 | @rm -f build/temp.o 38 | 39 | .PHONY: all clean 40 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x00.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $00 5 | 6 | .org $00 7 | ld a, $55 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x08.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $08 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $55 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x10.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $10 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $55 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x18.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $18 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $55 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x20.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $20 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $55 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x28.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $28 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $55 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x30.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $30 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $55 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $FF 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/rst_0x38.s: -------------------------------------------------------------------------------- 1 | .include "header.inc" 2 | 3 | main: 4 | rst $38 5 | 6 | .org $00 7 | ld a, $FF 8 | ld ($8000), a 9 | - jr - 10 | 11 | .org $08 12 | ld a, $FF 13 | ld ($8000), a 14 | - jr - 15 | 16 | .org $10 17 | ld a, $FF 18 | ld ($8000), a 19 | - jr - 20 | 21 | .org $18 22 | ld a, $FF 23 | ld ($8000), a 24 | - jr - 25 | 26 | .org $20 27 | ld a, $FF 28 | ld ($8000), a 29 | - jr - 30 | 31 | .org $28 32 | ld a, $FF 33 | ld ($8000), a 34 | - jr - 35 | 36 | .org $30 37 | ld a, $FF 38 | ld ($8000), a 39 | - jr - 40 | 41 | .org $38 42 | ld a, $55 43 | ld ($8000), a 44 | - jr - 45 | 46 | -------------------------------------------------------------------------------- /tests/micro_cpu/temp.link: -------------------------------------------------------------------------------- 1 | [objects] 2 | build/temp.o 3 | -------------------------------------------------------------------------------- /tests/scanner/batch.py: -------------------------------------------------------------------------------- 1 | import os; 2 | 3 | #============= 4 | 5 | def test_sample(addr, x, y): 6 | print(F"test_sample 0x{addr:04x} at ({x},{y})") 7 | 8 | source = F""" 9 | .include "header.inc" 10 | 11 | main: 12 | lcd_off_unsafe 13 | 14 | reset_lcd_for_test 15 | long_delay ({x} + (114 * {y}) - 7) 16 | ld a, $55 17 | ld a, (${addr:04X}) 18 | 19 | test_display_a 20 | """ 21 | return source 22 | 23 | #============= 24 | 25 | def test_read(addr, x, y): 26 | print(F"test_read 0x{addr:04x} at ({x},{y})") 27 | 28 | source = F""" 29 | .include "header.inc" 30 | 31 | main: 32 | lcd_off_unsafe 33 | ld a, $55 34 | ld (${addr:04X}), a 35 | 36 | reset_lcd_for_test 37 | long_delay ({x} + (114 * {y}) - 7) 38 | ld a, $55 39 | ld a, (${addr:04X}) 40 | 41 | test_display_a 42 | """ 43 | return source 44 | 45 | #============= 46 | 47 | def test_write(addr, x, y): 48 | print(F"test_write 0x{addr:04x} at ({x},{y})") 49 | 50 | source = F""" 51 | .include "header.inc" 52 | 53 | main: 54 | reset_lcd_for_test 55 | long_delay ({x} + (114 * {y}) - 7) 56 | ld a, $55 57 | ld (${addr:04X}), a 58 | 59 | lcd_off_unsafe 60 | ld a, (${addr:04X}) 61 | ld b, a 62 | lcd_on 63 | ld a, b 64 | test_display_a 65 | """ 66 | return source 67 | 68 | # y 10 x 110 = + 69 | # y 10 x 111 = + 70 | # y 10 x 112 = + 71 | # y 10 x 113 = + 72 | # y 10 x 114 = + 73 | # y 10 x 115 = + 74 | # y 10 x 116 = + 75 | # y 10 x 117 = + 76 | 77 | # 0x8000 write 78 | # y 10 x 0 = + 79 | # y 10 x 20 = + 80 | # y 10 x 21 = X 81 | # y 10 x 63 = X 82 | # y 10 x 64 = + 83 | # y 10 x 113 = + 84 | 85 | # 0x8000 write 86 | # y 0 x 0 = 87 | # y 0 x 20 = + 88 | # y 0 x 21 = x 89 | # y 0 x 63 = 90 | # y 0 x 64 = 91 | # y 0 x 113 = 92 | 93 | #============= 94 | 95 | def compile_and_upload(source): 96 | file = open("sample.s", "w") 97 | file.write(source) 98 | file.close() 99 | 100 | os.system("wla-gb -o sample.o sample.s") 101 | os.system("wlalink sample.link sample.gb") 102 | os.system("./gb-live32.exe -u sample.gb > /dev/null") 103 | input("Press Enter to continue...") 104 | 105 | #============= 106 | 107 | y = 154 108 | for x in [0, 1, 2, 3, 18, 19, 20, 21, 63, 64, 113, 114]: 109 | compile_and_upload(test_write(0x9000, x, y)) 110 | 111 | 112 | """ 113 | oam read : y 20 -| 0:X | 1: | 19:X | 20:X | 21:X | 63:X | 64:O |113:O |114:X | 114 | oam write : y 20 -| 0:O | 1: | 19:O | 20:O | 21:O | 63:O | 64:O |113:O |114:O | // wtf is oam write lock just broken on dmg? 115 | vram read : y 20 -| 0:O | 1: | 19:O | 20:X | 21:X | 63:X | 64:O |113:O |114:O | 116 | vram write: y 20 -| 0:O | 1: | 19:O | 20:O | 21:X | 63:X | 64:O |113:O |114:O | 117 | ly : y 20 -| 0:20 | 1:20 | 19:20 | 20:20 | 21:20 | 63:20 | 64:20 |113:20 |114:21 | 118 | stat : y 20 -| 0:x80 | 1:x82 | 19:x82 | 20:x82 | 21:x83 | 63:x83 | 64:x80 |113:x80 |114:x80 | 119 | oam read : y 153 -| 0: | 1: | 19: | 20: | 21: | 63: | 64: |113: |114: | 120 | oam write : y 153 -| 0:O | 1: | 19:O | 20:O | 21:O | 63:O | 64:O |113:O |114:O | 121 | vram read : y 153 -| 0: | 1: | 19: | 20: | 21: | 63: | 64: |113: |114: | 122 | vram write: y 153 -| 0:O | 1: | 19:O | 20:O | 21:O | 63:O | 64:O |113:O |114:O | 123 | stat : y 153 -| 0:x81 | 1:x81 | 19:x85 | 20: | 21: | 63: | 64: |113: |114: | 124 | 125 | 126 | vram write: y 154 -| 0:+ | 1: | 19:+ | 20:+ | 21:X | 63:X | 64:+ |113:+ |114:+ | 127 | 128 | 129 | ags: 130 | oam write : y 20 - 0:X 19:X 20:X 21:X 63:X 64:+ 113:+ 114:X // more reasonable on ags 131 | oam write : y 153 - 0:+ 19:+ 20:+ 21:+ 63:+ 64:+ 113:+ 114:X 132 | oam write : y 154 - 0:X 19:X 20:X 21:X 63:X 64:+ 113:+ 114:X 133 | 134 | """ 135 | -------------------------------------------------------------------------------- /tests/scanner/header.inc: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | 3 | .gbheader 4 | name "microtest" 5 | cartridgetype $03 6 | nintendologo 7 | romdmg 8 | ramsize 3 9 | .endgb 10 | 11 | .memorymap 12 | slot 0 start $0000 size $4000 13 | defaultslot 0 14 | .endme 15 | 16 | .rombanksize $4000 17 | .rombanks 2 18 | 19 | //------------------------------------------------------------------------------ 20 | 21 | .define VRAM $8000 22 | 23 | .define DIV $04 24 | .define TIMA $05 25 | .define TMA $06 26 | .define TAC $07 27 | 28 | .define LCDC $40 29 | .define STAT $41 30 | .define SCY $42 31 | .define SCX $43 32 | .define LY $44 33 | .define LYC $45 34 | .define DMA $46 35 | .define BGP $47 36 | .define OBP0 $48 37 | .define OBP1 $49 38 | .define WY $4A 39 | .define WX $4B 40 | 41 | .define IF $0F 42 | .define IE $FF 43 | 44 | .define VBLANK_INT_VECTOR $40 45 | .define STAT_INT_VECTOR $48 46 | .define TIMER_INT_VECTOR $50 47 | .define SERIAL_INT_VECTOR $58 48 | .define JOYPAD_INT_VECTOR $60 49 | 50 | .include "macros.inc" 51 | 52 | .bank 0 53 | .org $100 54 | nop 55 | jp $150 56 | .org $150 -------------------------------------------------------------------------------- /tests/scanner/sample.link: -------------------------------------------------------------------------------- 1 | [objects] 2 | sample.o 3 | -------------------------------------------------------------------------------- /tests/scanner/sample.s: -------------------------------------------------------------------------------- 1 | 2 | .include "header.inc" 3 | 4 | main: 5 | reset_lcd_for_test 6 | long_delay (114 + (114 * 154) - 7) 7 | ld a, $55 8 | ld ($9000), a 9 | 10 | lcd_off_unsafe 11 | ld a, ($9000) 12 | ld b, a 13 | lcd_on 14 | ld a, b 15 | test_display_a 16 | --------------------------------------------------------------------------------