romdevtools 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1110 -0
- package/CHANGELOG.md +525 -0
- package/LICENSE +45 -0
- package/NOTICE +102 -0
- package/README.md +84 -0
- package/examples/README.md +36 -0
- package/examples/art-first-workflow/README.md +209 -0
- package/examples/atari2600/main.asm +143 -0
- package/examples/atari2600/templates/default.asm +183 -0
- package/examples/atari2600/templates/mini_invaders.asm +381 -0
- package/examples/atari2600/templates/music_demo.asm +322 -0
- package/examples/atari2600/templates/paddle.asm +345 -0
- package/examples/atari2600/templates/single_screen.asm +242 -0
- package/examples/atari7800/main.c +88 -0
- package/examples/atari7800/templates/default.c +183 -0
- package/examples/atari7800/templates/hello_sprite.c +141 -0
- package/examples/atari7800/templates/music_demo.c +123 -0
- package/examples/atari7800/templates/platformer.c +159 -0
- package/examples/atari7800/templates/puzzle.c +164 -0
- package/examples/atari7800/templates/racing.c +139 -0
- package/examples/atari7800/templates/shmup.c +148 -0
- package/examples/atari7800/templates/sports.c +207 -0
- package/examples/c64/main.c +63 -0
- package/examples/c64/templates/hello_sprite.c +117 -0
- package/examples/c64/templates/music_demo.c +131 -0
- package/examples/c64/templates/platformer.c +194 -0
- package/examples/c64/templates/puzzle.c +178 -0
- package/examples/c64/templates/racing.c +140 -0
- package/examples/c64/templates/shmup.c +201 -0
- package/examples/c64/templates/sports.c +109 -0
- package/examples/c64/templates/tile_engine.c +174 -0
- package/examples/gb/main.asm +106 -0
- package/examples/gb/main.c +49 -0
- package/examples/gb/templates/default.c +58 -0
- package/examples/gb/templates/hello_sprite.c +130 -0
- package/examples/gb/templates/music_demo.c +52 -0
- package/examples/gb/templates/platformer.c +178 -0
- package/examples/gb/templates/puzzle.c +217 -0
- package/examples/gb/templates/racing.c +158 -0
- package/examples/gb/templates/shmup.c +172 -0
- package/examples/gb/templates/sports.c +136 -0
- package/examples/gb/templates/tile_engine.c +280 -0
- package/examples/gba/templates/gba_hello.c +64 -0
- package/examples/gba/templates/maxmod_demo.c +105 -0
- package/examples/gba/templates/platformer.c +215 -0
- package/examples/gba/templates/puzzle.c +237 -0
- package/examples/gba/templates/racing.c +175 -0
- package/examples/gba/templates/shmup.c +197 -0
- package/examples/gba/templates/sports.c +177 -0
- package/examples/gba/templates/tonc_hello.c +72 -0
- package/examples/gba/templates/tonc_hello_sprite.c +109 -0
- package/examples/gbc/main.asm +123 -0
- package/examples/gbc/templates/default.c +61 -0
- package/examples/gbc/templates/hello_sprite.c +140 -0
- package/examples/gbc/templates/music_demo.c +63 -0
- package/examples/gbc/templates/platformer.c +181 -0
- package/examples/gbc/templates/puzzle.c +217 -0
- package/examples/gbc/templates/racing.c +164 -0
- package/examples/gbc/templates/shmup.c +188 -0
- package/examples/gbc/templates/sports.c +142 -0
- package/examples/gbc/templates/tile_engine.c +280 -0
- package/examples/genesis/main.s +161 -0
- package/examples/genesis/templates/hello_sprite.c +75 -0
- package/examples/genesis/templates/platformer.c +166 -0
- package/examples/genesis/templates/puzzle.c +240 -0
- package/examples/genesis/templates/racing.c +170 -0
- package/examples/genesis/templates/sgdk_hello.c +41 -0
- package/examples/genesis/templates/shmup.c +188 -0
- package/examples/genesis/templates/shmup_2p.c +244 -0
- package/examples/genesis/templates/sports.c +172 -0
- package/examples/genesis/templates/tile_engine.c +135 -0
- package/examples/genesis/templates/xgm2_demo.c +53 -0
- package/examples/genesis/templates/xgm2_demo_data.s +27 -0
- package/examples/gg/main.c +10 -0
- package/examples/gg/templates/default.c +156 -0
- package/examples/gg/templates/hello_sprite.c +90 -0
- package/examples/gg/templates/music_demo.c +156 -0
- package/examples/gg/templates/platformer.c +220 -0
- package/examples/gg/templates/puzzle.c +204 -0
- package/examples/gg/templates/racing.c +161 -0
- package/examples/gg/templates/shmup.c +188 -0
- package/examples/gg/templates/sports.c +137 -0
- package/examples/gg/templates/tile_engine.c +137 -0
- package/examples/lynx/main.c +4 -0
- package/examples/lynx/templates/default.c +41 -0
- package/examples/lynx/templates/hello_sprite.c +39 -0
- package/examples/lynx/templates/music_demo.c +37 -0
- package/examples/lynx/templates/platformer.c +72 -0
- package/examples/lynx/templates/puzzle.c +142 -0
- package/examples/lynx/templates/racing.c +94 -0
- package/examples/lynx/templates/shmup.c +132 -0
- package/examples/lynx/templates/sports.c +59 -0
- package/examples/msx/catch_game/README.md +75 -0
- package/examples/msx/catch_game/_verify.mjs +93 -0
- package/examples/msx/catch_game/main.c +249 -0
- package/examples/msx/catch_game/shot_after_left.png +0 -0
- package/examples/msx/catch_game/shot_after_right.png +0 -0
- package/examples/msx/catch_game/shot_before.png +0 -0
- package/examples/msx/catch_game/shot_scored.png +0 -0
- package/examples/msx/music_sfx/README.md +38 -0
- package/examples/msx/music_sfx/main.c +164 -0
- package/examples/msx/sprite_move/README.md +44 -0
- package/examples/msx/sprite_move/main.c +100 -0
- package/examples/nes/main.c +143 -0
- package/examples/nes/space-shooter/README.md +69 -0
- package/examples/nes/space-shooter/main.c +441 -0
- package/examples/nes/space-shooter/nes_runtime.c +347 -0
- package/examples/nes/space-shooter/nes_runtime.h +141 -0
- package/examples/nes/templates/default.c +41 -0
- package/examples/nes/templates/hello_sprite.c +104 -0
- package/examples/nes/templates/music_demo.c +80 -0
- package/examples/nes/templates/platformer.c +171 -0
- package/examples/nes/templates/puzzle.c +232 -0
- package/examples/nes/templates/racing.c +203 -0
- package/examples/nes/templates/shmup.c +228 -0
- package/examples/nes/templates/sports.c +205 -0
- package/examples/nes/templates/tile_engine.c +224 -0
- package/examples/pce/catch_game/README.md +8 -0
- package/examples/pce/catch_game/_verify.mjs +75 -0
- package/examples/pce/catch_game/main.c +327 -0
- package/examples/pce/catch_game/shot_after.png +0 -0
- package/examples/pce/catch_game/shot_before.png +0 -0
- package/examples/pce/main.c +37 -0
- package/examples/pce/music_sfx/README.md +8 -0
- package/examples/pce/music_sfx/main.c +154 -0
- package/examples/pce/sprite_move/README.md +19 -0
- package/examples/pce/sprite_move/main.c +155 -0
- package/examples/porting-across-platforms/README.md +59 -0
- package/examples/sms/main.c +129 -0
- package/examples/sms/templates/default.c +110 -0
- package/examples/sms/templates/hello_sprite.c +90 -0
- package/examples/sms/templates/music_demo.c +151 -0
- package/examples/sms/templates/platformer.c +215 -0
- package/examples/sms/templates/puzzle.c +204 -0
- package/examples/sms/templates/racing.c +161 -0
- package/examples/sms/templates/shmup.c +167 -0
- package/examples/sms/templates/shmup_2p.c +219 -0
- package/examples/sms/templates/sports.c +147 -0
- package/examples/sms/templates/tile_engine.c +137 -0
- package/examples/snes/main.asm +70 -0
- package/examples/snes/templates/c-hello-data.asm +28 -0
- package/examples/snes/templates/c-hello.c +71 -0
- package/examples/snes/templates/default-data.asm +36 -0
- package/examples/snes/templates/default.c +74 -0
- package/examples/snes/templates/hello_sprite-data.asm +45 -0
- package/examples/snes/templates/hello_sprite.c +76 -0
- package/examples/snes/templates/music_demo-data.asm +20 -0
- package/examples/snes/templates/music_demo.c +83 -0
- package/examples/snes/templates/platformer-data.asm +27 -0
- package/examples/snes/templates/platformer.c +142 -0
- package/examples/snes/templates/puzzle-data.asm +14 -0
- package/examples/snes/templates/puzzle.c +199 -0
- package/examples/snes/templates/racing-data.asm +33 -0
- package/examples/snes/templates/racing.c +146 -0
- package/examples/snes/templates/shmup-data.asm +61 -0
- package/examples/snes/templates/shmup.c +199 -0
- package/examples/snes/templates/sports-data.asm +28 -0
- package/examples/snes/templates/sports.c +144 -0
- package/package.json +97 -0
- package/src/cheats/gamegenie.js +506 -0
- package/src/cheats/lookup.js +262 -0
- package/src/cheats/parse-cht.js +92 -0
- package/src/cli/smoke.js +283 -0
- package/src/cores/registry.js +99 -0
- package/src/cores/wasm/bluemsx_libretro.js +2 -0
- package/src/cores/wasm/bluemsx_libretro.wasm +0 -0
- package/src/cores/wasm/fceumm_libretro.js +2 -0
- package/src/cores/wasm/fceumm_libretro.wasm +0 -0
- package/src/cores/wasm/gambatte_libretro.js +2 -0
- package/src/cores/wasm/gambatte_libretro.wasm +0 -0
- package/src/cores/wasm/geargrafx_libretro.js +2 -0
- package/src/cores/wasm/geargrafx_libretro.wasm +0 -0
- package/src/cores/wasm/genesis_plus_gx_libretro.js +2 -0
- package/src/cores/wasm/genesis_plus_gx_libretro.wasm +0 -0
- package/src/cores/wasm/handy_libretro.js +2 -0
- package/src/cores/wasm/handy_libretro.wasm +0 -0
- package/src/cores/wasm/mgba_libretro.js +2 -0
- package/src/cores/wasm/mgba_libretro.wasm +0 -0
- package/src/cores/wasm/prosystem_libretro.js +2 -0
- package/src/cores/wasm/prosystem_libretro.wasm +0 -0
- package/src/cores/wasm/snes9x_libretro.js +2 -0
- package/src/cores/wasm/snes9x_libretro.wasm +0 -0
- package/src/cores/wasm/stella2014_libretro.js +2 -0
- package/src/cores/wasm/stella2014_libretro.wasm +0 -0
- package/src/cores/wasm/vice_x64_libretro.js +2 -0
- package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
- package/src/host/LibretroHost.js +1469 -0
- package/src/host/c64-sid-state.js +161 -0
- package/src/host/callbacks.js +444 -0
- package/src/host/chafa-render.js +170 -0
- package/src/host/coreLoader.js +57 -0
- package/src/host/cpu-state.js +543 -0
- package/src/host/dsp-state.js +188 -0
- package/src/host/framebuffer.js +115 -0
- package/src/host/gb-apu-state.js +321 -0
- package/src/host/gba-video-state.js +215 -0
- package/src/host/gpgx-state.js +320 -0
- package/src/host/index.js +4 -0
- package/src/host/lynx-mikey-state.js +226 -0
- package/src/host/msx-ay-state.js +78 -0
- package/src/host/nes-apu-state.js +194 -0
- package/src/host/pce-psg-state.js +82 -0
- package/src/host/retroConstants.js +72 -0
- package/src/host/snes9x-state.js +35 -0
- package/src/host/types.js +332 -0
- package/src/http/routes.js +182 -0
- package/src/http/skill-doc.js +176 -0
- package/src/http/swagger.js +84 -0
- package/src/http/tool-registry.js +142 -0
- package/src/install/postinstall.mjs +81 -0
- package/src/mcp/disclosure.js +250 -0
- package/src/mcp/log.js +87 -0
- package/src/mcp/server.js +498 -0
- package/src/mcp/state.js +88 -0
- package/src/mcp/tool-manifest.js +92 -0
- package/src/mcp/tools/address-to-symbol.js +145 -0
- package/src/mcp/tools/art-loaders.js +743 -0
- package/src/mcp/tools/assets.js +87 -0
- package/src/mcp/tools/audio.js +562 -0
- package/src/mcp/tools/cart-parts.js +651 -0
- package/src/mcp/tools/cheats.js +343 -0
- package/src/mcp/tools/classify-region.js +107 -0
- package/src/mcp/tools/diff-cluster.js +32 -0
- package/src/mcp/tools/diff-roms.js +394 -0
- package/src/mcp/tools/disasm.js +1410 -0
- package/src/mcp/tools/find-references.js +375 -0
- package/src/mcp/tools/font-map.js +563 -0
- package/src/mcp/tools/frame.js +293 -0
- package/src/mcp/tools/free-space.js +71 -0
- package/src/mcp/tools/index.js +339 -0
- package/src/mcp/tools/input-layout.js +231 -0
- package/src/mcp/tools/input.js +227 -0
- package/src/mcp/tools/lifecycle.js +132 -0
- package/src/mcp/tools/lospec.js +187 -0
- package/src/mcp/tools/memory.js +472 -0
- package/src/mcp/tools/metasprite-tools.js +256 -0
- package/src/mcp/tools/platform-docs.js +113 -0
- package/src/mcp/tools/platform-tools.js +1300 -0
- package/src/mcp/tools/platforms.js +139 -0
- package/src/mcp/tools/playtest.js +360 -0
- package/src/mcp/tools/preview-tile.js +478 -0
- package/src/mcp/tools/project.js +1939 -0
- package/src/mcp/tools/record.js +180 -0
- package/src/mcp/tools/reinject.js +732 -0
- package/src/mcp/tools/rendering-context.js +838 -0
- package/src/mcp/tools/rom-id.js +308 -0
- package/src/mcp/tools/run-until.js +176 -0
- package/src/mcp/tools/snippets.js +209 -0
- package/src/mcp/tools/splice-chr.js +190 -0
- package/src/mcp/tools/sprite-pipeline.js +626 -0
- package/src/mcp/tools/state.js +198 -0
- package/src/mcp/tools/symbols.js +400 -0
- package/src/mcp/tools/tile-inspect.js +293 -0
- package/src/mcp/tools/toolchain.js +720 -0
- package/src/mcp/tools/trace-vram-source.js +81 -0
- package/src/mcp/tools/watch-memory.js +1036 -0
- package/src/mcp/tools/which-tiles.js +180 -0
- package/src/mcp/util.js +355 -0
- package/src/observer/bus.js +127 -0
- package/src/observer/livestream.html +594 -0
- package/src/observer/server.js +78 -0
- package/src/observer/tool-wrap.js +142 -0
- package/src/platforms/_guides/MUSIC_SOURCING.md +104 -0
- package/src/platforms/_guides/ROMHACKING_PLAYBOOK.md +245 -0
- package/src/platforms/atari2600/MENTAL_MODEL.md +194 -0
- package/src/platforms/atari2600/TROUBLESHOOTING.md +155 -0
- package/src/platforms/atari2600/UPSTREAM_SOURCES.md +49 -0
- package/src/platforms/atari2600/lib/README.md +67 -0
- package/src/platforms/atari2600/lib/cc65-src/crt0.s +49 -0
- package/src/platforms/atari2600/lib/cc65-src/ctype.s +5 -0
- package/src/platforms/atari2600/lib/kernel_skeleton.asm +85 -0
- package/src/platforms/atari2600/lib/player_kernel.asm +87 -0
- package/src/platforms/atari2600/lib/playfield_kernel.asm +63 -0
- package/src/platforms/atari2600/lib/read_joystick.asm +76 -0
- package/src/platforms/atari2600/lib/score_kernel.asm +141 -0
- package/src/platforms/atari2600/lib/vcs_constants.h +97 -0
- package/src/platforms/atari2600/lib/vectors.asm +16 -0
- package/src/platforms/atari2600/tia.js +355 -0
- package/src/platforms/atari7800/MENTAL_MODEL.md +324 -0
- package/src/platforms/atari7800/TROUBLESHOOTING.md +195 -0
- package/src/platforms/atari7800/UPSTREAM_SOURCES.md +43 -0
- package/src/platforms/atari7800/lib/README.md +45 -0
- package/src/platforms/atari7800/lib/c/atari7800_music.c +215 -0
- package/src/platforms/atari7800/lib/c/atari7800_music.h +33 -0
- package/src/platforms/atari7800/lib/c/atari7800_sfx.c +74 -0
- package/src/platforms/atari7800/lib/c/atari7800_sfx.h +45 -0
- package/src/platforms/atari7800/lib/cc65-src/clock.s +69 -0
- package/src/platforms/atari7800/lib/cc65-src/clocks_per_sec.s +34 -0
- package/src/platforms/atari7800/lib/cc65-src/clrscr.s +27 -0
- package/src/platforms/atari7800/lib/cc65-src/conio.s +226 -0
- package/src/platforms/atari7800/lib/cc65-src/conio_font.s +278 -0
- package/src/platforms/atari7800/lib/cc65-src/cputc.s +139 -0
- package/src/platforms/atari7800/lib/cc65-src/crt0.s +71 -0
- package/src/platforms/atari7800/lib/cc65-src/ctype.s +5 -0
- package/src/platforms/atari7800/lib/cc65-src/exehdr.s +46 -0
- package/src/platforms/atari7800/lib/cc65-src/extra/mono.s +28 -0
- package/src/platforms/atari7800/lib/cc65-src/extzp.inc +15 -0
- package/src/platforms/atari7800/lib/cc65-src/extzp.s +15 -0
- package/src/platforms/atari7800/lib/cc65-src/get_tv.s +65 -0
- package/src/platforms/atari7800/lib/cc65-src/irq.s +36 -0
- package/src/platforms/atari7800/lib/cc65-src/joy/atari7800-stdjoy.s +197 -0
- package/src/platforms/atari7800/lib/cc65-src/joy_stat_stddrv.s +14 -0
- package/src/platforms/atari7800/lib/cc65-src/libref.s +8 -0
- package/src/platforms/atari7800/lib/cc65-src/mono_clrscr.s +27 -0
- package/src/platforms/atari7800/lib/cc65-src/mono_conio.s +231 -0
- package/src/platforms/atari7800/lib/cc65-src/mono_cputc.s +102 -0
- package/src/platforms/atari7800/lib/cc65-src/mono_font.s +2065 -0
- package/src/platforms/atari7800/lib/cc65-src/mono_setcursor.s +214 -0
- package/src/platforms/atari7800/lib/cc65-src/setcursor.s +214 -0
- package/src/platforms/atari7800/lib/cc65-src/textcolor.s +53 -0
- package/src/platforms/atari7800/lib/cc65-src/wherex.s +19 -0
- package/src/platforms/atari7800/lib/cc65-src/wherey.s +19 -0
- package/src/platforms/atari7800/lib/display_list.asm +53 -0
- package/src/platforms/atari7800/lib/maria_init.asm +48 -0
- package/src/platforms/atari7800/lib/maria_registers.h +61 -0
- package/src/platforms/atari7800/lib/palette_load.asm +53 -0
- package/src/platforms/atari7800/lib/read_pad.asm +57 -0
- package/src/platforms/atari7800/lib/vblank_wait.asm +16 -0
- package/src/platforms/atari7800/maria.js +220 -0
- package/src/platforms/atari7800/song.js +232 -0
- package/src/platforms/c64/MENTAL_MODEL.md +188 -0
- package/src/platforms/c64/TROUBLESHOOTING.md +130 -0
- package/src/platforms/c64/UPSTREAM_SOURCES.md +35 -0
- package/src/platforms/c64/image-to-tilemap.js +190 -0
- package/src/platforms/c64/lib/README.md +52 -0
- package/src/platforms/c64/lib/basic_stub.s +25 -0
- package/src/platforms/c64/lib/c/c64_music.c +248 -0
- package/src/platforms/c64/lib/c/c64_music.h +36 -0
- package/src/platforms/c64/lib/c/c64_sfx.c +94 -0
- package/src/platforms/c64/lib/c/c64_sfx.h +37 -0
- package/src/platforms/c64/lib/c64_registers.h +108 -0
- package/src/platforms/c64/lib/cc65-src/_scrsize.s +12 -0
- package/src/platforms/c64/lib/cc65-src/acc_c128_speed.s +5 -0
- package/src/platforms/c64/lib/cc65-src/acc_c64dtv_speed.s +64 -0
- package/src/platforms/c64/lib/cc65-src/acc_c65_speed.s +69 -0
- package/src/platforms/c64/lib/cc65-src/acc_chameleon_speed.s +100 -0
- package/src/platforms/c64/lib/cc65-src/acc_detect_c128.s +33 -0
- package/src/platforms/c64/lib/cc65-src/acc_detect_c64dtv.s +44 -0
- package/src/platforms/c64/lib/cc65-src/acc_detect_c65.s +55 -0
- package/src/platforms/c64/lib/cc65-src/acc_detect_chameleon.s +39 -0
- package/src/platforms/c64/lib/cc65-src/acc_detect_scpu.s +34 -0
- package/src/platforms/c64/lib/cc65-src/acc_detect_turbomaster.s +45 -0
- package/src/platforms/c64/lib/cc65-src/acc_scpu_speed.s +59 -0
- package/src/platforms/c64/lib/cc65-src/acc_turbomaster_speed.s +56 -0
- package/src/platforms/c64/lib/cc65-src/bordercolor.s +17 -0
- package/src/platforms/c64/lib/cc65-src/break.s +108 -0
- package/src/platforms/c64/lib/cc65-src/cgetc.s +62 -0
- package/src/platforms/c64/lib/cc65-src/clrscr.s +11 -0
- package/src/platforms/c64/lib/cc65-src/color.s +24 -0
- package/src/platforms/c64/lib/cc65-src/conio.s +10 -0
- package/src/platforms/c64/lib/cc65-src/cputc.s +105 -0
- package/src/platforms/c64/lib/cc65-src/crt0.s +117 -0
- package/src/platforms/c64/lib/cc65-src/devnum.s +7 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-65816.s +377 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-c256k.s +508 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-dqbb.s +447 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-georam.s +355 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-isepic.s +276 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-kerberos.s +281 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-ram.s +270 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-ramcart.s +297 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-reu.s +286 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-rrr.s +363 -0
- package/src/platforms/c64/lib/cc65-src/emd/c64-vdc.s +405 -0
- package/src/platforms/c64/lib/cc65-src/emd/dtv-himem.s +251 -0
- package/src/platforms/c64/lib/cc65-src/extra/soft80.s +71 -0
- package/src/platforms/c64/lib/cc65-src/extra/soft80mono.s +74 -0
- package/src/platforms/c64/lib/cc65-src/extra/tgimousedata.s +21 -0
- package/src/platforms/c64/lib/cc65-src/get_ostype.s +43 -0
- package/src/platforms/c64/lib/cc65-src/get_tv.s +20 -0
- package/src/platforms/c64/lib/cc65-src/gettime.s +83 -0
- package/src/platforms/c64/lib/cc65-src/irq.s +49 -0
- package/src/platforms/c64/lib/cc65-src/joy/c64-hitjoy.s +218 -0
- package/src/platforms/c64/lib/cc65-src/joy/c64-numpad.s +155 -0
- package/src/platforms/c64/lib/cc65-src/joy/c64-ptvjoy.s +146 -0
- package/src/platforms/c64/lib/cc65-src/joy/c64-stdjoy.s +111 -0
- package/src/platforms/c64/lib/cc65-src/joy_stat_stddrv.s +14 -0
- package/src/platforms/c64/lib/cc65-src/joy_stddrv.s +14 -0
- package/src/platforms/c64/lib/cc65-src/kbhit.s +23 -0
- package/src/platforms/c64/lib/cc65-src/kbrepeat.s +14 -0
- package/src/platforms/c64/lib/cc65-src/kernal.s +53 -0
- package/src/platforms/c64/lib/cc65-src/kplot.s +22 -0
- package/src/platforms/c64/lib/cc65-src/libref.s +16 -0
- package/src/platforms/c64/lib/cc65-src/mainargs.s +137 -0
- package/src/platforms/c64/lib/cc65-src/mcbdefault.s +146 -0
- package/src/platforms/c64/lib/cc65-src/mcbspritedata.s +4 -0
- package/src/platforms/c64/lib/cc65-src/mou/c64-1351.s +467 -0
- package/src/platforms/c64/lib/cc65-src/mou/c64-inkwell.s +446 -0
- package/src/platforms/c64/lib/cc65-src/mou/c64-joy.s +451 -0
- package/src/platforms/c64/lib/cc65-src/mou/c64-pot.s +409 -0
- package/src/platforms/c64/lib/cc65-src/mouse_stat_stddrv.s +14 -0
- package/src/platforms/c64/lib/cc65-src/mouse_stddrv.s +14 -0
- package/src/platforms/c64/lib/cc65-src/mouseref.s +23 -0
- package/src/platforms/c64/lib/cc65-src/pencalib.c +94 -0
- package/src/platforms/c64/lib/cc65-src/randomize.s +18 -0
- package/src/platforms/c64/lib/cc65-src/revers.s +27 -0
- package/src/platforms/c64/lib/cc65-src/ser/c64-swlink.s +484 -0
- package/src/platforms/c64/lib/cc65-src/ser_stat_stddrv.s +14 -0
- package/src/platforms/c64/lib/cc65-src/ser_stddrv.s +13 -0
- package/src/platforms/c64/lib/cc65-src/settime.s +84 -0
- package/src/platforms/c64/lib/cc65-src/soft80.inc +46 -0
- package/src/platforms/c64/lib/cc65-src/soft80_cgetc.s +85 -0
- package/src/platforms/c64/lib/cc65-src/soft80_charset.s +182 -0
- package/src/platforms/c64/lib/cc65-src/soft80_color.s +159 -0
- package/src/platforms/c64/lib/cc65-src/soft80_conio.s +157 -0
- package/src/platforms/c64/lib/cc65-src/soft80_cpeekc.s +148 -0
- package/src/platforms/c64/lib/cc65-src/soft80_cpeekcolor.s +19 -0
- package/src/platforms/c64/lib/cc65-src/soft80_cpeekrevers.s +15 -0
- package/src/platforms/c64/lib/cc65-src/soft80_cpeeks.s +71 -0
- package/src/platforms/c64/lib/cc65-src/soft80_cputc.s +521 -0
- package/src/platforms/c64/lib/cc65-src/soft80_kclrscr.s +76 -0
- package/src/platforms/c64/lib/cc65-src/soft80_kplot.s +63 -0
- package/src/platforms/c64/lib/cc65-src/soft80_scrsize.s +14 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_cgetc.s +67 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_color.s +65 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_conio.s +168 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_cpeekcolor.s +17 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_cputc.s +204 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_kclrscr.s +63 -0
- package/src/platforms/c64/lib/cc65-src/soft80mono_kplot.s +52 -0
- package/src/platforms/c64/lib/cc65-src/status.s +15 -0
- package/src/platforms/c64/lib/cc65-src/sysuname.s +37 -0
- package/src/platforms/c64/lib/cc65-src/tgi/c64-hi.s +881 -0
- package/src/platforms/c64/lib/cc65-src/tgi_stat_stddrv.s +14 -0
- package/src/platforms/c64/lib/cc65-src/tgi_stddrv.s +13 -0
- package/src/platforms/c64/lib/cc65-src/tmcommon.s +67 -0
- package/src/platforms/c64/lib/cc65-src/waitvsync.s +18 -0
- package/src/platforms/c64/lib/read_joystick.s +28 -0
- package/src/platforms/c64/lib/sid_play.s +48 -0
- package/src/platforms/c64/lib/sprite_table.s +73 -0
- package/src/platforms/c64/lib/vic_init.s +47 -0
- package/src/platforms/c64/sid.js +128 -0
- package/src/platforms/c64/song.js +262 -0
- package/src/platforms/c64/vic.js +273 -0
- package/src/platforms/common/default-palette.js +139 -0
- package/src/platforms/common/image-to-tiles.js +418 -0
- package/src/platforms/common/intent.js +111 -0
- package/src/platforms/common/metasprite-adapters.js +279 -0
- package/src/platforms/common/metasprite-codegen.js +192 -0
- package/src/platforms/common/metasprite-core.js +201 -0
- package/src/platforms/common/metasprite.js +89 -0
- package/src/platforms/common/platform-palette.js +137 -0
- package/src/platforms/common/registers.js +329 -0
- package/src/platforms/common/render-tiles.js +86 -0
- package/src/platforms/common/screenshot-sprite.js +153 -0
- package/src/platforms/common/tile-decode.js +149 -0
- package/src/platforms/gb/MENTAL_MODEL.md +262 -0
- package/src/platforms/gb/TROUBLESHOOTING.md +218 -0
- package/src/platforms/gb/UPSTREAM_SOURCES.md +43 -0
- package/src/platforms/gb/image-to-tilemap.js +173 -0
- package/src/platforms/gb/lib/README.md +115 -0
- package/src/platforms/gb/lib/c/LICENSE-HUGEDRIVER +25 -0
- package/src/platforms/gb/lib/c/README.md +122 -0
- package/src/platforms/gb/lib/c/SDCC_GOTCHAS.md +190 -0
- package/src/platforms/gb/lib/c/gb_crt0.s +163 -0
- package/src/platforms/gb/lib/c/gb_hardware.h +113 -0
- package/src/platforms/gb/lib/c/gb_runtime.c +284 -0
- package/src/platforms/gb/lib/c/gb_runtime.h +145 -0
- package/src/platforms/gb/lib/c/hUGEDriver.c +191 -0
- package/src/platforms/gb/lib/c/hUGEDriver.h +95 -0
- package/src/platforms/gb/lib/c/hUGEDriver.upstream.asm +1908 -0
- package/src/platforms/gb/lib/c/lcd_init.c +19 -0
- package/src/platforms/gb/lib/c/patch-header.js +154 -0
- package/src/platforms/gb/lib/c/song_data.c +88 -0
- package/src/platforms/gb/lib/c/unroll.h +52 -0
- package/src/platforms/gb/lib/c/wait_vblank.c +14 -0
- package/src/platforms/gb/lib/dma_oam.asm +51 -0
- package/src/platforms/gb/lib/header.asm +56 -0
- package/src/platforms/gb/lib/joypad_read.asm +47 -0
- package/src/platforms/gb/lib/lcd_init.asm +44 -0
- package/src/platforms/gb/lib/load_palette.asm +25 -0
- package/src/platforms/gb/lib/load_tiles.asm +29 -0
- package/src/platforms/gb/lib/vblank_wait.asm +31 -0
- package/src/platforms/gb/ppu.js +574 -0
- package/src/platforms/gb/song.js +140 -0
- package/src/platforms/gba/MENTAL_MODEL.md +216 -0
- package/src/platforms/gba/TROUBLESHOOTING.md +250 -0
- package/src/platforms/gba/UPSTREAM_SOURCES.md +41 -0
- package/src/platforms/gba/lib/arm-archives/libc.a +0 -0
- package/src/platforms/gba/lib/arm-archives/libgcc.a +0 -0
- package/src/platforms/gba/lib/arm-archives/libnosys.a +0 -0
- package/src/platforms/gba/lib/c/gba_sfx.c +81 -0
- package/src/platforms/gba/lib/c/gba_sfx.h +48 -0
- package/src/platforms/gba/lib/libgba/crtbegin.o +0 -0
- package/src/platforms/gba/lib/libgba/crtend.o +0 -0
- package/src/platforms/gba/lib/libgba/crti.o +0 -0
- package/src/platforms/gba/lib/libgba/crtn.o +0 -0
- package/src/platforms/gba/lib/libgba/gba_cart.ld +319 -0
- package/src/platforms/gba/lib/libgba/gba_crt0.s +258 -0
- package/src/platforms/gba/lib/libgba/include/BoyScout.h +128 -0
- package/src/platforms/gba/lib/libgba/include/disc.h +36 -0
- package/src/platforms/gba/lib/libgba/include/disc_io.h +64 -0
- package/src/platforms/gba/lib/libgba/include/dldi.h +112 -0
- package/src/platforms/gba/lib/libgba/include/erapi.h +195 -0
- package/src/platforms/gba/lib/libgba/include/fade.h +76 -0
- package/src/platforms/gba/lib/libgba/include/gba.h +47 -0
- package/src/platforms/gba/lib/libgba/include/gba_affine.h +83 -0
- package/src/platforms/gba/lib/libgba/include/gba_base.h +122 -0
- package/src/platforms/gba/lib/libgba/include/gba_compression.h +66 -0
- package/src/platforms/gba/lib/libgba/include/gba_console.h +55 -0
- package/src/platforms/gba/lib/libgba/include/gba_dma.h +100 -0
- package/src/platforms/gba/lib/libgba/include/gba_input.h +127 -0
- package/src/platforms/gba/lib/libgba/include/gba_interrupt.h +146 -0
- package/src/platforms/gba/lib/libgba/include/gba_multiboot.h +72 -0
- package/src/platforms/gba/lib/libgba/include/gba_sio.h +124 -0
- package/src/platforms/gba/lib/libgba/include/gba_sound.h +356 -0
- package/src/platforms/gba/lib/libgba/include/gba_sprites.h +195 -0
- package/src/platforms/gba/lib/libgba/include/gba_systemcalls.h +194 -0
- package/src/platforms/gba/lib/libgba/include/gba_timers.h +63 -0
- package/src/platforms/gba/lib/libgba/include/gba_types.h +60 -0
- package/src/platforms/gba/lib/libgba/include/gba_video.h +307 -0
- package/src/platforms/gba/lib/libgba/include/mappy.h +72 -0
- package/src/platforms/gba/lib/libgba/include/mbv2.h +90 -0
- package/src/platforms/gba/lib/libgba/include/xcomms.h +93 -0
- package/src/platforms/gba/lib/libgba/include/xcomms_cmd.h +53 -0
- package/src/platforms/gba/lib/libgba/libgba.seed.a +0 -0
- package/src/platforms/gba/lib/libgba/libgba.seed.hash +1 -0
- package/src/platforms/gba/lib/libgba/src/AffineSet.c +42 -0
- package/src/platforms/gba/lib/libgba/src/ArcTan.s +41 -0
- package/src/platforms/gba/lib/libgba/src/BoyScout/BoyScout.c +1242 -0
- package/src/platforms/gba/lib/libgba/src/BoyScout/GBASoundRegs.h +200 -0
- package/src/platforms/gba/lib/libgba/src/Compression.c +84 -0
- package/src/platforms/gba/lib/libgba/src/CpuSet.c +41 -0
- package/src/platforms/gba/lib/libgba/src/Div.s +58 -0
- package/src/platforms/gba/lib/libgba/src/DivArm.s +50 -0
- package/src/platforms/gba/lib/libgba/src/InterruptDispatcher.s +113 -0
- package/src/platforms/gba/lib/libgba/src/IntrWait.c +35 -0
- package/src/platforms/gba/lib/libgba/src/MultiBoot.s +33 -0
- package/src/platforms/gba/lib/libgba/src/Reset.s +49 -0
- package/src/platforms/gba/lib/libgba/src/Sound.s +48 -0
- package/src/platforms/gba/lib/libgba/src/Sqrt.s +34 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/disc.c +58 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/dldi.c +189 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/dldi_stub.s +78 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_cf_common.c +260 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_cf_common.h +72 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_m3_common.c +60 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_m3_common.h +48 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_m3cf.c +83 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_m3cf.h +45 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_m3sd.c +508 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_m3sd.h +45 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_mpcf.c +87 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_mpcf.h +42 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_sc_common.c +47 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_sc_common.h +43 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_sccf.c +83 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_sccf.h +45 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_scsd.c +385 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_scsd.h +48 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_scsd_s.s +139 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_sd_common.c +203 -0
- package/src/platforms/gba/lib/libgba/src/disc_io/io_sd_common.h +108 -0
- package/src/platforms/gba/lib/libgba/src/fade.c +185 -0
- package/src/platforms/gba/lib/libgba/src/input.c +127 -0
- package/src/platforms/gba/lib/libgba/src/interrupt.c +116 -0
- package/src/platforms/gba/lib/libgba/src/mappy_print.c +74 -0
- package/src/platforms/gba/lib/libgba/src/mb2print.c +69 -0
- package/src/platforms/gba/lib/libgba/src/mbv2.c +230 -0
- package/src/platforms/gba/lib/libgba/src/mbv2.txt +119 -0
- package/src/platforms/gba/lib/libgba/src/xcomms.c +203 -0
- package/src/platforms/gba/lib/libgba/src/xcomms_print.c +56 -0
- package/src/platforms/gba/lib/libgba/sysinclude/_ansi.h +82 -0
- package/src/platforms/gba/lib/libgba/sysinclude/_newlib_version.h +11 -0
- package/src/platforms/gba/lib/libgba/sysinclude/_syslist.h +41 -0
- package/src/platforms/gba/lib/libgba/sysinclude/alloca.h +21 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ar.h +65 -0
- package/src/platforms/gba/lib/libgba/sysinclude/argz.h +33 -0
- package/src/platforms/gba/lib/libgba/sysinclude/assert.h +50 -0
- package/src/platforms/gba/lib/libgba/sysinclude/complex.h +150 -0
- package/src/platforms/gba/lib/libgba/sysinclude/cpio.h +30 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ctype.h +183 -0
- package/src/platforms/gba/lib/libgba/sysinclude/devctl.h +78 -0
- package/src/platforms/gba/lib/libgba/sysinclude/dirent.h +85 -0
- package/src/platforms/gba/lib/libgba/sysinclude/elf.h +3147 -0
- package/src/platforms/gba/lib/libgba/sysinclude/envlock.h +15 -0
- package/src/platforms/gba/lib/libgba/sysinclude/envz.h +16 -0
- package/src/platforms/gba/lib/libgba/sysinclude/errno.h +11 -0
- package/src/platforms/gba/lib/libgba/sysinclude/fastmath.h +13 -0
- package/src/platforms/gba/lib/libgba/sysinclude/fcntl.h +1 -0
- package/src/platforms/gba/lib/libgba/sysinclude/fenv.h +42 -0
- package/src/platforms/gba/lib/libgba/sysinclude/float.h +631 -0
- package/src/platforms/gba/lib/libgba/sysinclude/fnmatch.h +55 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ftw.h +66 -0
- package/src/platforms/gba/lib/libgba/sysinclude/getopt.h +185 -0
- package/src/platforms/gba/lib/libgba/sysinclude/glob.h +90 -0
- package/src/platforms/gba/lib/libgba/sysinclude/grp.h +86 -0
- package/src/platforms/gba/lib/libgba/sysinclude/iconv.h +63 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ieeefp.h +295 -0
- package/src/platforms/gba/lib/libgba/sysinclude/inttypes.h +345 -0
- package/src/platforms/gba/lib/libgba/sysinclude/iso646.h +45 -0
- package/src/platforms/gba/lib/libgba/sysinclude/langinfo.h +332 -0
- package/src/platforms/gba/lib/libgba/sysinclude/libgen.h +37 -0
- package/src/platforms/gba/lib/libgba/sysinclude/limits.h +168 -0
- package/src/platforms/gba/lib/libgba/sysinclude/locale.h +96 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/_arc4random.h +1 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/_default_types.h +250 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/_endian.h +39 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/_time.h +3 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/_types.h +8 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/ansi.h +1 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/endian.h +69 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/fastmath.h +98 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/ieee.h +127 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/ieeefp.h +533 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/malloc.h +8 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/param.h +8 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/setjmp-dj.h +43 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/setjmp.h +524 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/stdlib.h +8 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/termios.h +1 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/time.h +15 -0
- package/src/platforms/gba/lib/libgba/sysinclude/machine/types.h +13 -0
- package/src/platforms/gba/lib/libgba/sysinclude/malloc.h +173 -0
- package/src/platforms/gba/lib/libgba/sysinclude/math.h +645 -0
- package/src/platforms/gba/lib/libgba/sysinclude/memory.h +4 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ndbm.h +91 -0
- package/src/platforms/gba/lib/libgba/sysinclude/newlib.h +427 -0
- package/src/platforms/gba/lib/libgba/sysinclude/paths.h +9 -0
- package/src/platforms/gba/lib/libgba/sysinclude/pthread.h +456 -0
- package/src/platforms/gba/lib/libgba/sysinclude/pwd.h +83 -0
- package/src/platforms/gba/lib/libgba/sysinclude/reent.h +190 -0
- package/src/platforms/gba/lib/libgba/sysinclude/regdef.h +7 -0
- package/src/platforms/gba/lib/libgba/sysinclude/regex.h +103 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sched.h +112 -0
- package/src/platforms/gba/lib/libgba/sysinclude/search.h +64 -0
- package/src/platforms/gba/lib/libgba/sysinclude/setjmp.h +25 -0
- package/src/platforms/gba/lib/libgba/sysinclude/signal.h +35 -0
- package/src/platforms/gba/lib/libgba/sysinclude/spawn.h +111 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/ssp.h +76 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/stdio.h +101 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/stdlib.h +30 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/string.h +115 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/strings.h +55 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/unistd.h +93 -0
- package/src/platforms/gba/lib/libgba/sysinclude/ssp/wchar.h +97 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdarg.h +135 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdatomic.h +409 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdbool.h +51 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stddef.h +463 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdint.h +466 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdio.h +807 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdio_ext.h +79 -0
- package/src/platforms/gba/lib/libgba/sysinclude/stdlib.h +345 -0
- package/src/platforms/gba/lib/libgba/sysinclude/string.h +183 -0
- package/src/platforms/gba/lib/libgba/sysinclude/strings.h +80 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_default_fcntl.h +241 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_intsup.h +199 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_locale.h +12 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_pthreadtypes.h +233 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_sigset.h +43 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_stdint.h +90 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_timespec.h +52 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_timeval.h +60 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_types.h +228 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/_tz_structs.h +24 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/cdefs.h +754 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/config.h +314 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/custom_file.h +2 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/dir.h +10 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/dirent.h +13 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/endian.h +207 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/errno.h +198 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/fcntl.h +12 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/features.h +551 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/fenv.h +90 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/file.h +2 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/iconvnls.h +77 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/iosupport.h +143 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/lock.h +75 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/param.h +35 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/queue.h +919 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/reent.h +913 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/resource.h +24 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/sched.h +69 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/select.h +94 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/signal.h +388 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/stat.h +179 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/statvfs.h +41 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/stdio.h +27 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/string.h +2 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/syslimits.h +61 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/time.h +448 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/timeb.h +40 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/times.h +32 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/timespec.h +63 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/tree.h +864 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/types.h +228 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/unistd.h +591 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/utime.h +22 -0
- package/src/platforms/gba/lib/libgba/sysinclude/sys/wait.h +44 -0
- package/src/platforms/gba/lib/libgba/sysinclude/tar.h +43 -0
- package/src/platforms/gba/lib/libgba/sysinclude/termios.h +7 -0
- package/src/platforms/gba/lib/libgba/sysinclude/tgmath.h +127 -0
- package/src/platforms/gba/lib/libgba/sysinclude/threads.h +93 -0
- package/src/platforms/gba/lib/libgba/sysinclude/time.h +313 -0
- package/src/platforms/gba/lib/libgba/sysinclude/unctrl.h +42 -0
- package/src/platforms/gba/lib/libgba/sysinclude/unistd.h +6 -0
- package/src/platforms/gba/lib/libgba/sysinclude/utime.h +12 -0
- package/src/platforms/gba/lib/libgba/sysinclude/utmp.h +8 -0
- package/src/platforms/gba/lib/libgba/sysinclude/varargs.h +7 -0
- package/src/platforms/gba/lib/libgba/sysinclude/wchar.h +339 -0
- package/src/platforms/gba/lib/libgba/sysinclude/wctype.h +74 -0
- package/src/platforms/gba/lib/libgba/sysinclude/wordexp.h +53 -0
- package/src/platforms/gba/lib/libtonc/crtbegin.o +0 -0
- package/src/platforms/gba/lib/libtonc/crtend.o +0 -0
- package/src/platforms/gba/lib/libtonc/crti.o +0 -0
- package/src/platforms/gba/lib/libtonc/crtn.o +0 -0
- package/src/platforms/gba/lib/libtonc/gba_cart.ld +319 -0
- package/src/platforms/gba/lib/libtonc/gba_crt0.s +258 -0
- package/src/platforms/gba/lib/libtonc/include/tonc.h +72 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_asminc.h +132 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_bios.h +555 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_core.h +573 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_input.h +184 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_irq.h +121 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_legacy.h +481 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_libgba.h +537 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_math.h +692 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_memdef.h +962 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_memmap.h +583 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_nocash.h +51 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_oam.h +186 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_surface.h +461 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_text.h +270 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_tte.h +748 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_types.h +376 -0
- package/src/platforms/gba/lib/libtonc/include/tonc_video.h +615 -0
- package/src/platforms/gba/lib/libtonc/libtonc.seed.a +0 -0
- package/src/platforms/gba/lib/libtonc/libtonc.seed.hash +1 -0
- package/src/platforms/gba/lib/libtonc/src/asm/clr_blend_fast.s +72 -0
- package/src/platforms/gba/lib/libtonc/src/asm/clr_fade_fast.s +74 -0
- package/src/platforms/gba/lib/libtonc/src/asm/div_lut.s +54 -0
- package/src/platforms/gba/lib/libtonc/src/asm/sin_lut.s +90 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_bios.s +289 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_bios_ex.s +97 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_isr_master.s +92 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_isr_nest.s +98 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_memcpy.s +126 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_memset.s +123 -0
- package/src/platforms/gba/lib/libtonc/src/asm/tonc_nocash.s +73 -0
- package/src/platforms/gba/lib/libtonc/src/font/sys8.png +0 -0
- package/src/platforms/gba/lib/libtonc/src/font/sys8.s +46 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana10.png +0 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana10.s +293 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9.png +0 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9.s +167 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9_b4.png +0 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9_b4.s +545 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9b.png +0 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9b.s +167 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9i.png +0 -0
- package/src/platforms/gba/lib/libtonc/src/font/verdana9i.s +167 -0
- package/src/platforms/gba/lib/libtonc/src/pre1.3/tonc_bitmap.c +289 -0
- package/src/platforms/gba/lib/libtonc/src/pre1.3/tonc_text.c +81 -0
- package/src/platforms/gba/lib/libtonc/src/pre1.3/tonc_text_bm.c +244 -0
- package/src/platforms/gba/lib/libtonc/src/pre1.3/tonc_text_map.c +122 -0
- package/src/platforms/gba/lib/libtonc/src/pre1.3/tonc_text_oam.c +113 -0
- package/src/platforms/gba/lib/libtonc/src/pre1.3/toncfont.s +43 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_bg.c +61 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_bg_affine.c +112 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_bmp16.c +240 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_bmp8.c +314 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_color.c +368 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_core.c +237 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_input.c +118 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_irq.c +271 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_math.c +54 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_oam.c +76 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_obj_affine.c +146 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_sbmp16.c +369 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_sbmp8.c +405 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_schr4c.c +588 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_schr4r.c +462 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_surface.c +105 -0
- package/src/platforms/gba/lib/libtonc/src/tonc_video.c +33 -0
- package/src/platforms/gba/lib/libtonc/src/tte/ase_drawg.c +83 -0
- package/src/platforms/gba/lib/libtonc/src/tte/bmp16_drawg.c +72 -0
- package/src/platforms/gba/lib/libtonc/src/tte/bmp16_drawg_b1cs.c +92 -0
- package/src/platforms/gba/lib/libtonc/src/tte/bmp8_drawg.c +71 -0
- package/src/platforms/gba/lib/libtonc/src/tte/bmp8_drawg_b1cs.c +104 -0
- package/src/platforms/gba/lib/libtonc/src/tte/bmp8_drawg_b1cts_fast.s +115 -0
- package/src/platforms/gba/lib/libtonc/src/tte/chr4c_drawg_b1cts.c +76 -0
- package/src/platforms/gba/lib/libtonc/src/tte/chr4c_drawg_b1cts_fast.s +128 -0
- package/src/platforms/gba/lib/libtonc/src/tte/chr4c_drawg_b4cts.c +67 -0
- package/src/platforms/gba/lib/libtonc/src/tte/chr4c_drawg_b4cts_fast.s +127 -0
- package/src/platforms/gba/lib/libtonc/src/tte/chr4r_drawg_b1cts.c +80 -0
- package/src/platforms/gba/lib/libtonc/src/tte/chr4r_drawg_b1cts_fast.s +145 -0
- package/src/platforms/gba/lib/libtonc/src/tte/obj_drawg.c +61 -0
- package/src/platforms/gba/lib/libtonc/src/tte/se_drawg.c +110 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_init_ase.c +87 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_init_bmp.c +118 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_init_chr4c.c +86 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_init_chr4r.c +87 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_init_obj.c +84 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_init_se.c +96 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_iohook.c +264 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_main.c +756 -0
- package/src/platforms/gba/lib/libtonc/src/tte/tte_types.s +119 -0
- package/src/platforms/gba/lib/maxmod/LICENSE-MAXMOD +23 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_defs.inc +117 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_format_mas.inc +108 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_macros.inc +195 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_mas.inc +40 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_mas_structs.inc +189 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_mixer_ds.inc +46 -0
- package/src/platforms/gba/lib/maxmod/asm_include/mp_mixer_gba.inc +42 -0
- package/src/platforms/gba/lib/maxmod/asm_include/swi_gba.inc +23 -0
- package/src/platforms/gba/lib/maxmod/include/maxmod.h +412 -0
- package/src/platforms/gba/lib/maxmod/include/mm_types.h +333 -0
- package/src/platforms/gba/lib/maxmod/maxmod.seed.a +0 -0
- package/src/platforms/gba/lib/maxmod/maxmod.seed.hash +1 -0
- package/src/platforms/gba/lib/maxmod/music/chiptune.xm +0 -0
- package/src/platforms/gba/lib/maxmod/music/chiptune_soundbank.bin +0 -0
- package/src/platforms/gba/lib/maxmod/music/chiptune_soundbank.h +4 -0
- package/src/platforms/gba/lib/maxmod/music/make_chiptune_xm.js +203 -0
- package/src/platforms/gba/lib/maxmod/source/mm_effect.s +767 -0
- package/src/platforms/gba/lib/maxmod/source/mm_main.s +115 -0
- package/src/platforms/gba/lib/maxmod/source/mm_mas.s +4990 -0
- package/src/platforms/gba/lib/maxmod/source/mm_mas_arm.s +612 -0
- package/src/platforms/gba/lib/maxmod/source_gba/mm_init_default.s +98 -0
- package/src/platforms/gba/lib/maxmod/source_gba/mm_main_gba.s +292 -0
- package/src/platforms/gba/lib/maxmod/source_gba/mm_mixer_gba.s +1367 -0
- package/src/platforms/gba/lib/sysbase/gba_iosupport.c +138 -0
- package/src/platforms/gbc/MENTAL_MODEL.md +178 -0
- package/src/platforms/gbc/TROUBLESHOOTING.md +142 -0
- package/src/platforms/gbc/UPSTREAM_SOURCES.md +60 -0
- package/src/platforms/gbc/lib/c/LICENSE-HUGEDRIVER +25 -0
- package/src/platforms/gbc/lib/c/README.md +122 -0
- package/src/platforms/gbc/lib/c/SDCC_GOTCHAS.md +190 -0
- package/src/platforms/gbc/lib/c/gb_crt0.s +163 -0
- package/src/platforms/gbc/lib/c/gb_hardware.h +113 -0
- package/src/platforms/gbc/lib/c/gb_runtime.c +284 -0
- package/src/platforms/gbc/lib/c/gb_runtime.h +145 -0
- package/src/platforms/gbc/lib/c/hUGEDriver.c +191 -0
- package/src/platforms/gbc/lib/c/hUGEDriver.h +95 -0
- package/src/platforms/gbc/lib/c/hUGEDriver.upstream.asm +1908 -0
- package/src/platforms/gbc/lib/c/lcd_init.c +19 -0
- package/src/platforms/gbc/lib/c/patch-header.js +154 -0
- package/src/platforms/gbc/lib/c/song_data.c +88 -0
- package/src/platforms/gbc/lib/c/unroll.h +52 -0
- package/src/platforms/gbc/lib/c/wait_vblank.c +14 -0
- package/src/platforms/gbc/song.js +3 -0
- package/src/platforms/genesis/MENTAL_MODEL.md +306 -0
- package/src/platforms/genesis/TROUBLESHOOTING.md +226 -0
- package/src/platforms/genesis/UPSTREAM_SOURCES.md +53 -0
- package/src/platforms/genesis/image-to-tilemap.js +351 -0
- package/src/platforms/genesis/lib/README.md +160 -0
- package/src/platforms/genesis/lib/c/crtbegin.o +0 -0
- package/src/platforms/genesis/lib/c/crtend.o +0 -0
- package/src/platforms/genesis/lib/c/genesis.ld +44 -0
- package/src/platforms/genesis/lib/c/genesis_sfx.c +51 -0
- package/src/platforms/genesis/lib/c/genesis_sfx.h +49 -0
- package/src/platforms/genesis/lib/c/libc.a +0 -0
- package/src/platforms/genesis/lib/c/libgcc.a +0 -0
- package/src/platforms/genesis/lib/c/libm.a +0 -0
- package/src/platforms/genesis/lib/c/sega.s +71 -0
- package/src/platforms/genesis/lib/header.s +96 -0
- package/src/platforms/genesis/lib/nmi_safe.s +79 -0
- package/src/platforms/genesis/lib/pad_read.s +104 -0
- package/src/platforms/genesis/lib/sgdk/COPYING.RUNTIME +73 -0
- package/src/platforms/genesis/lib/sgdk/LICENSE +12 -0
- package/src/platforms/genesis/lib/sgdk/include/asm.h +52 -0
- package/src/platforms/genesis/lib/sgdk/include/asm_mac.i +9 -0
- package/src/platforms/genesis/lib/sgdk/include/bmp.h +690 -0
- package/src/platforms/genesis/lib/sgdk/include/config.h +208 -0
- package/src/platforms/genesis/lib/sgdk/include/dma.h +542 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/console.h +387 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/everdrive.h +116 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/fat16.h +84 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/flash-save/flash.h +164 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/flash-save/saveman.h +177 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/link_cable.h +316 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/minimusic/minimus.h +29 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/16c550.h +221 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/comm.h +93 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/gamejolt.h +541 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/jsmn.h +471 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/json.h +122 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/lsd.h +172 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/megawifi.h +984 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/mw-msg.h +392 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/ssf_ed_pro.h +38 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/mw/ssf_ed_x7.h +73 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/serial/buffer.h +8 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/serial/serial.h +133 -0
- package/src/platforms/genesis/lib/sgdk/include/ext/stb/stb_sprintf.h +1864 -0
- package/src/platforms/genesis/lib/sgdk/include/genesis.h +92 -0
- package/src/platforms/genesis/lib/sgdk/include/joy.h +482 -0
- package/src/platforms/genesis/lib/sgdk/include/kdebug.h +21 -0
- package/src/platforms/genesis/lib/sgdk/include/map.h +409 -0
- package/src/platforms/genesis/lib/sgdk/include/mapper.h +186 -0
- package/src/platforms/genesis/lib/sgdk/include/maths.h +1071 -0
- package/src/platforms/genesis/lib/sgdk/include/maths3D.h +480 -0
- package/src/platforms/genesis/lib/sgdk/include/memory.h +346 -0
- package/src/platforms/genesis/lib/sgdk/include/memory_base.h +37 -0
- package/src/platforms/genesis/lib/sgdk/include/object.h +171 -0
- package/src/platforms/genesis/lib/sgdk/include/pal.h +500 -0
- package/src/platforms/genesis/lib/sgdk/include/pool.h +171 -0
- package/src/platforms/genesis/lib/sgdk/include/psg.h +153 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/pcm/snd_dpcm2.h +79 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/pcm/snd_pcm.h +98 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/pcm/snd_pcm4.h +125 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/pcm/tab_vol.h +6 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/smp_null.h +6 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/smp_null_dpcm.h +6 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/sound.h +70 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/xgm.h +399 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/xgm2.h +389 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/z80_def.i80 +41 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/z80_fct.i80 +83 -0
- package/src/platforms/genesis/lib/sgdk/include/snd/z80_mac.i80 +1476 -0
- package/src/platforms/genesis/lib/sgdk/include/sprite_eng.h +1095 -0
- package/src/platforms/genesis/lib/sgdk/include/sprite_eng_legacy.h +1030 -0
- package/src/platforms/genesis/lib/sgdk/include/sram.h +110 -0
- package/src/platforms/genesis/lib/sgdk/include/string.h +349 -0
- package/src/platforms/genesis/lib/sgdk/include/sys.h +511 -0
- package/src/platforms/genesis/lib/sgdk/include/tab_cnv.h +19 -0
- package/src/platforms/genesis/lib/sgdk/include/task.h +77 -0
- package/src/platforms/genesis/lib/sgdk/include/task_cst.h +33 -0
- package/src/platforms/genesis/lib/sgdk/include/timer.h +132 -0
- package/src/platforms/genesis/lib/sgdk/include/tools.h +450 -0
- package/src/platforms/genesis/lib/sgdk/include/types.h +320 -0
- package/src/platforms/genesis/lib/sgdk/include/vdp.h +1150 -0
- package/src/platforms/genesis/lib/sgdk/include/vdp_bg.h +723 -0
- package/src/platforms/genesis/lib/sgdk/include/vdp_pal.h +101 -0
- package/src/platforms/genesis/lib/sgdk/include/vdp_spr.h +448 -0
- package/src/platforms/genesis/lib/sgdk/include/vdp_tile.h +1136 -0
- package/src/platforms/genesis/lib/sgdk/include/vram.h +270 -0
- package/src/platforms/genesis/lib/sgdk/include/ym2612.h +87 -0
- package/src/platforms/genesis/lib/sgdk/include/z80_ctrl.h +420 -0
- package/src/platforms/genesis/lib/sgdk/libmd.seed.a +0 -0
- package/src/platforms/genesis/lib/sgdk/libmd.seed.hash +1 -0
- package/src/platforms/genesis/lib/sgdk/md.ld +120 -0
- package/src/platforms/genesis/lib/sgdk/music/demo.vgm +0 -0
- package/src/platforms/genesis/lib/sgdk/music/demo.xgc +0 -0
- package/src/platforms/genesis/lib/sgdk/music/demo.xgm +0 -0
- package/src/platforms/genesis/lib/sgdk/res/image/font_default.png +0 -0
- package/src/platforms/genesis/lib/sgdk/res/image/sgdk_logo.png +0 -0
- package/src/platforms/genesis/lib/sgdk/res/libres.h +10 -0
- package/src/platforms/genesis/lib/sgdk/res/libres.res +5 -0
- package/src/platforms/genesis/lib/sgdk/res/libres.s +166 -0
- package/src/platforms/genesis/lib/sgdk/res/sound/stop_xgm.bin +0 -0
- package/src/platforms/genesis/lib/sgdk/rom_header.c +33 -0
- package/src/platforms/genesis/lib/sgdk/sega.preprocessed.s +364 -0
- package/src/platforms/genesis/lib/sgdk/sega.s +365 -0
- package/src/platforms/genesis/lib/sgdk/src/bmp.c +1539 -0
- package/src/platforms/genesis/lib/sgdk/src/bmp_a.s +3477 -0
- package/src/platforms/genesis/lib/sgdk/src/boot/rom_header.c +33 -0
- package/src/platforms/genesis/lib/sgdk/src/boot/sega.s +365 -0
- package/src/platforms/genesis/lib/sgdk/src/dma.c +782 -0
- package/src/platforms/genesis/lib/sgdk/src/dma_a.s +23 -0
- package/src/platforms/genesis/lib/sgdk/src/error_a.s +376 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/console.c +490 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/everdrive.c +285 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/fat16.c +610 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/flash-save/README.md +112 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/flash-save/flash.c +300 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/flash-save/saveman.c +641 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/link_cable.c +1758 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/CHANGELOG.md +31 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/LICENSE.txt +17 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/README.md +18 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/data.z80 +148 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/define.z80 +173 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/doc/api-c.md +80 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/doc/format.md +132 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/doc/teradrive.md +33 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/fm.z80 +363 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/main.z80 +433 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/minimus.c +129 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/minimus_drv.s80 +17 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/psg.z80 +231 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/track.z80 +398 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/minimusic/util.z80 +98 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/16c550.c +84 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/README.md +849 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/comm.c +141 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/gamejolt.c +758 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/json.c +153 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/lsd.c +364 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/megawifi.c +1501 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/ssf_ed_pro.c +98 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/mw/ssf_ed_x7.c +67 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/serial/buffer.c +69 -0
- package/src/platforms/genesis/lib/sgdk/src/ext/serial/serial.c +158 -0
- package/src/platforms/genesis/lib/sgdk/src/joy.c +1361 -0
- package/src/platforms/genesis/lib/sgdk/src/kdebug.s +109 -0
- package/src/platforms/genesis/lib/sgdk/src/map.c +1915 -0
- package/src/platforms/genesis/lib/sgdk/src/mapper.c +280 -0
- package/src/platforms/genesis/lib/sgdk/src/maths.c +878 -0
- package/src/platforms/genesis/lib/sgdk/src/maths3D.c +480 -0
- package/src/platforms/genesis/lib/sgdk/src/maths3D_a.s +205 -0
- package/src/platforms/genesis/lib/sgdk/src/memory.c +760 -0
- package/src/platforms/genesis/lib/sgdk/src/memory_a.s +306 -0
- package/src/platforms/genesis/lib/sgdk/src/object.c +111 -0
- package/src/platforms/genesis/lib/sgdk/src/pal.c +484 -0
- package/src/platforms/genesis/lib/sgdk/src/pool.c +234 -0
- package/src/platforms/genesis/lib/sgdk/src/psg.c +82 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/drv_null.s80 +27 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/drv_xgm.s80 +3037 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/drv_dpcm2.s80 +984 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/drv_pcm.s80 +592 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/drv_pcm4.s80 +699 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/snd_dpcm2.c +172 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/snd_pcm.c +152 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/snd_pcm4.c +213 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/pcm/tab_vol.c +277 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/smp_null.s +22 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/smp_null_dpcm.s +15 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/sound.c +33 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm.c +683 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2.s80 +993 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_fct.i80 +611 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_mac.i80 +133 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_pcm_fct.i80 +114 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_pcm_mac.i80 +1444 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_psg_fct.i80 +491 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_psg_mac.i80 +43 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_ym_fct.i80 +1664 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2/drv_xgm2_ym_mac.i80 +295 -0
- package/src/platforms/genesis/lib/sgdk/src/snd/xgm2.c +1083 -0
- package/src/platforms/genesis/lib/sgdk/src/sprite_eng.c +2256 -0
- package/src/platforms/genesis/lib/sgdk/src/sprite_eng_legacy.c +2309 -0
- package/src/platforms/genesis/lib/sgdk/src/sram.c +30 -0
- package/src/platforms/genesis/lib/sgdk/src/sram_a.s +41 -0
- package/src/platforms/genesis/lib/sgdk/src/string.c +720 -0
- package/src/platforms/genesis/lib/sgdk/src/sys.c +1053 -0
- package/src/platforms/genesis/lib/sgdk/src/sys_a.s +74 -0
- package/src/platforms/genesis/lib/sgdk/src/tab_cnv.c +129 -0
- package/src/platforms/genesis/lib/sgdk/src/tab_log10.c +8201 -0
- package/src/platforms/genesis/lib/sgdk/src/tab_log2.c +8201 -0
- package/src/platforms/genesis/lib/sgdk/src/tab_sin.c +2058 -0
- package/src/platforms/genesis/lib/sgdk/src/tab_sqrt.c +8201 -0
- package/src/platforms/genesis/lib/sgdk/src/task.s +148 -0
- package/src/platforms/genesis/lib/sgdk/src/timer.c +201 -0
- package/src/platforms/genesis/lib/sgdk/src/tools.c +1299 -0
- package/src/platforms/genesis/lib/sgdk/src/tools_a.s +979 -0
- package/src/platforms/genesis/lib/sgdk/src/types.c +18 -0
- package/src/platforms/genesis/lib/sgdk/src/vdp.c +1060 -0
- package/src/platforms/genesis/lib/sgdk/src/vdp_bg.c +511 -0
- package/src/platforms/genesis/lib/sgdk/src/vdp_spr.c +322 -0
- package/src/platforms/genesis/lib/sgdk/src/vdp_tile.c +1011 -0
- package/src/platforms/genesis/lib/sgdk/src/vdp_tile_a.s +68 -0
- package/src/platforms/genesis/lib/sgdk/src/vram.c +273 -0
- package/src/platforms/genesis/lib/sgdk/src/ym2612.c +175 -0
- package/src/platforms/genesis/lib/sgdk/src/z80_ctrl.c +334 -0
- package/src/platforms/genesis/lib/sprite_table.s +129 -0
- package/src/platforms/genesis/lib/vblank_wait.s +73 -0
- package/src/platforms/genesis/lib/vdp_init.s +85 -0
- package/src/platforms/genesis/lib/wram.s +88 -0
- package/src/platforms/genesis/lib/z80_bootstrap.s +102 -0
- package/src/platforms/genesis/vdp.js +548 -0
- package/src/platforms/genesis/xgm2-pcm.js +165 -0
- package/src/platforms/gg/MENTAL_MODEL.md +174 -0
- package/src/platforms/gg/TROUBLESHOOTING.md +105 -0
- package/src/platforms/gg/UPSTREAM_SOURCES.md +36 -0
- package/src/platforms/gg/lib/c/gg_crt0.s +112 -0
- package/src/platforms/gg/lib/c/gg_hw.h +62 -0
- package/src/platforms/gg/lib/c/gg_music.c +155 -0
- package/src/platforms/gg/lib/c/gg_music.h +89 -0
- package/src/platforms/gg/lib/c/gg_sfx.c +82 -0
- package/src/platforms/gg/lib/c/gg_sfx.h +40 -0
- package/src/platforms/gg/lib/c/joypad_read.c +22 -0
- package/src/platforms/gg/lib/c/load_palette.c +20 -0
- package/src/platforms/gg/lib/c/load_tiles.c +32 -0
- package/src/platforms/gg/lib/c/sprite_table.c +60 -0
- package/src/platforms/gg/lib/c/vblank_wait.c +10 -0
- package/src/platforms/gg/lib/c/vdp_init.c +58 -0
- package/src/platforms/gg/song.js +189 -0
- package/src/platforms/index.js +7 -0
- package/src/platforms/lynx/MENTAL_MODEL.md +248 -0
- package/src/platforms/lynx/TROUBLESHOOTING.md +105 -0
- package/src/platforms/lynx/UPSTREAM_SOURCES.md +66 -0
- package/src/platforms/lynx/lib/c/lynx_music.c +63 -0
- package/src/platforms/lynx/lib/c/lynx_music.h +16 -0
- package/src/platforms/lynx/lib/c/lynx_sfx.c +147 -0
- package/src/platforms/lynx/lib/c/lynx_sfx.h +59 -0
- package/src/platforms/lynx/lib/cc65-src/bllhdr.s +18 -0
- package/src/platforms/lynx/lib/cc65-src/bootldr.s +195 -0
- package/src/platforms/lynx/lib/cc65-src/cgetc.s +69 -0
- package/src/platforms/lynx/lib/cc65-src/clock.s +87 -0
- package/src/platforms/lynx/lib/cc65-src/crt0.s +135 -0
- package/src/platforms/lynx/lib/cc65-src/defdir.s +29 -0
- package/src/platforms/lynx/lib/cc65-src/eeprom.s +255 -0
- package/src/platforms/lynx/lib/cc65-src/eeprom46.s +204 -0
- package/src/platforms/lynx/lib/cc65-src/eeprom66.s +227 -0
- package/src/platforms/lynx/lib/cc65-src/eeprom86.s +236 -0
- package/src/platforms/lynx/lib/cc65-src/exec.s +34 -0
- package/src/platforms/lynx/lib/cc65-src/exehdr.s +27 -0
- package/src/platforms/lynx/lib/cc65-src/extzp.inc +23 -0
- package/src/platforms/lynx/lib/cc65-src/extzp.s +30 -0
- package/src/platforms/lynx/lib/cc65-src/irq.s +45 -0
- package/src/platforms/lynx/lib/cc65-src/joy/lynx-stdjoy.s +93 -0
- package/src/platforms/lynx/lib/cc65-src/joy_stat_stddrv.s +14 -0
- package/src/platforms/lynx/lib/cc65-src/kbhit.s +56 -0
- package/src/platforms/lynx/lib/cc65-src/libref.s +9 -0
- package/src/platforms/lynx/lib/cc65-src/load.s +41 -0
- package/src/platforms/lynx/lib/cc65-src/lseek.s +58 -0
- package/src/platforms/lynx/lib/cc65-src/lynx-cart.s +98 -0
- package/src/platforms/lynx/lib/cc65-src/lynx-snd.s +1270 -0
- package/src/platforms/lynx/lib/cc65-src/mainargs.s +24 -0
- package/src/platforms/lynx/lib/cc65-src/open.s +136 -0
- package/src/platforms/lynx/lib/cc65-src/oserror.s +14 -0
- package/src/platforms/lynx/lib/cc65-src/read.s +44 -0
- package/src/platforms/lynx/lib/cc65-src/ser/lynx-comlynx.s +404 -0
- package/src/platforms/lynx/lib/cc65-src/ser_stat_stddrv.s +14 -0
- package/src/platforms/lynx/lib/cc65-src/sysuname.s +39 -0
- package/src/platforms/lynx/lib/cc65-src/tgi/lynx-160-102-16.s +1075 -0
- package/src/platforms/lynx/lib/cc65-src/tgi_colors.s +9 -0
- package/src/platforms/lynx/lib/cc65-src/tgi_irq.s +11 -0
- package/src/platforms/lynx/lib/cc65-src/tgi_stat_stddrv.s +14 -0
- package/src/platforms/lynx/lib/cc65-src/tgi_stddrv.s +13 -0
- package/src/platforms/lynx/lib/cc65-src/uploader.s +81 -0
- package/src/platforms/lynx/song.js +291 -0
- package/src/platforms/msx/MENTAL_MODEL.md +115 -0
- package/src/platforms/msx/TROUBLESHOOTING.md +62 -0
- package/src/platforms/msx/UPSTREAM_SOURCES.md +46 -0
- package/src/platforms/msx/image-to-tilemap.js +103 -0
- package/src/platforms/msx/lib/c/hello_msx.c +51 -0
- package/src/platforms/msx/lib/c/msx_crt0.s +74 -0
- package/src/platforms/msx/lib/c/msx_hw.h +92 -0
- package/src/platforms/msx/lib/c/msx_vdp.c +150 -0
- package/src/platforms/msx/tiles.js +99 -0
- package/src/platforms/msx/vdp.js +160 -0
- package/src/platforms/nes/MENTAL_MODEL.md +323 -0
- package/src/platforms/nes/TROUBLESHOOTING.md +319 -0
- package/src/platforms/nes/UPSTREAM_SOURCES.md +31 -0
- package/src/platforms/nes/image-to-chr.js +195 -0
- package/src/platforms/nes/image-to-tilemap.js +415 -0
- package/src/platforms/nes/lib/README.md +40 -0
- package/src/platforms/nes/lib/asm/LICENSE-FAMITONE +40 -0
- package/src/platforms/nes/lib/asm/chr_ram_header.s +32 -0
- package/src/platforms/nes/lib/asm/famitone2.s +1258 -0
- package/src/platforms/nes/lib/asm/famitone_bridge.s +50 -0
- package/src/platforms/nes/lib/asm/music_data.s +238 -0
- package/src/platforms/nes/lib/c/chr_ram_runtime_hello_sprite.c +81 -0
- package/src/platforms/nes/lib/c/chr_ram_runtime_hud_row.c +101 -0
- package/src/platforms/nes/lib/c/nes_runtime.c +347 -0
- package/src/platforms/nes/lib/c/nes_runtime.h +153 -0
- package/src/platforms/nes/lib/c/nmi_handler.c +51 -0
- package/src/platforms/nes/lib/c/nmi_trampoline.s +25 -0
- package/src/platforms/nes/lib/cc65-src/Makefile.inc +9 -0
- package/src/platforms/nes/lib/cc65-src/_scrsize.s +24 -0
- package/src/platforms/nes/lib/cc65-src/cclear.s +29 -0
- package/src/platforms/nes/lib/cc65-src/chline.s +31 -0
- package/src/platforms/nes/lib/cc65-src/clock.s +31 -0
- package/src/platforms/nes/lib/cc65-src/clrscr.s +72 -0
- package/src/platforms/nes/lib/cc65-src/color.s +68 -0
- package/src/platforms/nes/lib/cc65-src/cpeekc.s +37 -0
- package/src/platforms/nes/lib/cc65-src/cpeekcolor.s +8 -0
- package/src/platforms/nes/lib/cc65-src/cpeekrevers.s +37 -0
- package/src/platforms/nes/lib/cc65-src/cputc.s +95 -0
- package/src/platforms/nes/lib/cc65-src/crt0.s +180 -0
- package/src/platforms/nes/lib/cc65-src/cvline.s +31 -0
- package/src/platforms/nes/lib/cc65-src/get_tv.s +37 -0
- package/src/platforms/nes/lib/cc65-src/gotox.s +21 -0
- package/src/platforms/nes/lib/cc65-src/gotoxy.s +22 -0
- package/src/platforms/nes/lib/cc65-src/gotoy.s +22 -0
- package/src/platforms/nes/lib/cc65-src/irq.s +19 -0
- package/src/platforms/nes/lib/cc65-src/joy/nes-stdjoy.s +102 -0
- package/src/platforms/nes/lib/cc65-src/joy_stat_stddrv.s +14 -0
- package/src/platforms/nes/lib/cc65-src/libref.s +9 -0
- package/src/platforms/nes/lib/cc65-src/mainargs.s +24 -0
- package/src/platforms/nes/lib/cc65-src/neschar.s +4616 -0
- package/src/platforms/nes/lib/cc65-src/ppu.s +158 -0
- package/src/platforms/nes/lib/cc65-src/ppubuf.s +117 -0
- package/src/platforms/nes/lib/cc65-src/randomize.s +18 -0
- package/src/platforms/nes/lib/cc65-src/revers.s +27 -0
- package/src/platforms/nes/lib/cc65-src/setcursor.s +37 -0
- package/src/platforms/nes/lib/cc65-src/sysuname.s +39 -0
- package/src/platforms/nes/lib/cc65-src/tgi/nes-64-56-2.s +480 -0
- package/src/platforms/nes/lib/cc65-src/tgi_stat_stddrv.s +14 -0
- package/src/platforms/nes/lib/cc65-src/tgi_stddrv.s +13 -0
- package/src/platforms/nes/lib/cc65-src/waitvsync.s +18 -0
- package/src/platforms/nes/lib/cc65-src/wherex.s +19 -0
- package/src/platforms/nes/lib/cc65-src/wherey.s +19 -0
- package/src/platforms/nes/lib/clear_nametable.s +38 -0
- package/src/platforms/nes/lib/clear_oam.s +18 -0
- package/src/platforms/nes/lib/load_palette.s +31 -0
- package/src/platforms/nes/lib/nmi_safe.s +65 -0
- package/src/platforms/nes/lib/oam_dma.s +16 -0
- package/src/platforms/nes/lib/read_pad.s +46 -0
- package/src/platforms/nes/lib/reset.s +46 -0
- package/src/platforms/nes/lib/sprite_table_populate.s +75 -0
- package/src/platforms/nes/lib/wait_vblank.s +12 -0
- package/src/platforms/nes/palette.js +39 -0
- package/src/platforms/nes/ppu.js +448 -0
- package/src/platforms/pce/MENTAL_MODEL.md +98 -0
- package/src/platforms/pce/TROUBLESHOOTING.md +54 -0
- package/src/platforms/pce/UPSTREAM_SOURCES.md +38 -0
- package/src/platforms/pce/image-to-tilemap.js +123 -0
- package/src/platforms/pce/lib/c/hello_pce.c +37 -0
- package/src/platforms/pce/lib/c/pce_hw.h +130 -0
- package/src/platforms/pce/lib/c/pce_input.c +36 -0
- package/src/platforms/pce/lib/c/pce_sound.c +53 -0
- package/src/platforms/pce/lib/c/pce_video.c +113 -0
- package/src/platforms/pce/tiles.js +92 -0
- package/src/platforms/pce/vce.js +59 -0
- package/src/platforms/pce/vdc.js +52 -0
- package/src/platforms/sms/MENTAL_MODEL.md +286 -0
- package/src/platforms/sms/TROUBLESHOOTING.md +140 -0
- package/src/platforms/sms/UPSTREAM_SOURCES.md +29 -0
- package/src/platforms/sms/image-to-tilemap.js +260 -0
- package/src/platforms/sms/lib/README.md +117 -0
- package/src/platforms/sms/lib/c/joypad_read.c +33 -0
- package/src/platforms/sms/lib/c/load_palette.c +24 -0
- package/src/platforms/sms/lib/c/load_tiles.c +32 -0
- package/src/platforms/sms/lib/c/sms_crt0.s +101 -0
- package/src/platforms/sms/lib/c/sms_hw.h +53 -0
- package/src/platforms/sms/lib/c/sms_music.c +178 -0
- package/src/platforms/sms/lib/c/sms_music.h +50 -0
- package/src/platforms/sms/lib/c/sms_sfx.c +82 -0
- package/src/platforms/sms/lib/c/sms_sfx.h +40 -0
- package/src/platforms/sms/lib/c/sprite_table.c +60 -0
- package/src/platforms/sms/lib/c/vblank_wait.c +10 -0
- package/src/platforms/sms/lib/c/vdp_init.c +58 -0
- package/src/platforms/sms/lib/header.s +33 -0
- package/src/platforms/sms/lib/joypad_read.s +40 -0
- package/src/platforms/sms/lib/load_palette.s +30 -0
- package/src/platforms/sms/lib/load_tiles.s +50 -0
- package/src/platforms/sms/lib/sprite_table.s +44 -0
- package/src/platforms/sms/lib/vblank_wait.s +36 -0
- package/src/platforms/sms/lib/vdp_init.s +47 -0
- package/src/platforms/sms/song.js +217 -0
- package/src/platforms/sms/vdp.js +530 -0
- package/src/platforms/snes/MENTAL_MODEL.md +289 -0
- package/src/platforms/snes/TROUBLESHOOTING.md +208 -0
- package/src/platforms/snes/UPSTREAM_SOURCES.md +50 -0
- package/src/platforms/snes/brr.js +208 -0
- package/src/platforms/snes/image-to-tilemap.js +227 -0
- package/src/platforms/snes/lib/audio/apu_blob.asm +228 -0
- package/src/platforms/snes/lib/audio/apu_blob.bin +0 -0
- package/src/platforms/snes/lib/audio/explosion.brr +0 -0
- package/src/platforms/snes/lib/audio/sample_bank.bin +0 -0
- package/src/platforms/snes/lib/audio/shoot.brr +0 -0
- package/src/platforms/snes/lib/audio/spc_driver.asm +241 -0
- package/src/platforms/snes/lib/audio_pipeline.asm +62 -0
- package/src/platforms/snes/lib/c/crt0.asm +125 -0
- package/src/platforms/snes/lib/c/hdr.asm +50 -0
- package/src/platforms/snes/lib/c/snes_sfx.c +116 -0
- package/src/platforms/snes/lib/c/snes_sfx.h +96 -0
- package/src/platforms/snes/lib/c/snes_sfx_data.asm +25 -0
- package/src/platforms/snes/lib/cgram_upload.asm +43 -0
- package/src/platforms/snes/lib/lorom_header.asm +47 -0
- package/src/platforms/snes/lib/lorom_multibank.asm +66 -0
- package/src/platforms/snes/lib/nmi_safe.asm +77 -0
- package/src/platforms/snes/lib/oam_upload.asm +45 -0
- package/src/platforms/snes/lib/pad_read.asm +64 -0
- package/src/platforms/snes/lib/pvsneslib/LICENSE +21 -0
- package/src/platforms/snes/lib/pvsneslib/include/ctype.h +6 -0
- package/src/platforms/snes/lib/pvsneslib/include/float.h +4 -0
- package/src/platforms/snes/lib/pvsneslib/include/hdr.asm +47 -0
- package/src/platforms/snes/lib/pvsneslib/include/limits.h +13 -0
- package/src/platforms/snes/lib/pvsneslib/include/math.h +10 -0
- package/src/platforms/snes/lib/pvsneslib/include/setjmp.h +4 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/background.h +396 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/console.h +188 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/dma.h +381 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/input.h +289 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/interrupt.h +264 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/libversion.h +10 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/lzss.h +43 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/map.h +113 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/object.h +234 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/pixel.h +53 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/scores.h +66 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/snestypes.h +65 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/sound.h +245 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/sprite.h +456 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes/video.h +465 -0
- package/src/platforms/snes/lib/pvsneslib/include/snes.h +190 -0
- package/src/platforms/snes/lib/pvsneslib/include/stdarg.h +28 -0
- package/src/platforms/snes/lib/pvsneslib/include/stdbool.h +53 -0
- package/src/platforms/snes/lib/pvsneslib/include/stddef.h +27 -0
- package/src/platforms/snes/lib/pvsneslib/include/stdint.h +7 -0
- package/src/platforms/snes/lib/pvsneslib/include/stdio.h +9 -0
- package/src/platforms/snes/lib/pvsneslib/include/stdlib.h +13 -0
- package/src/platforms/snes/lib/pvsneslib/include/string.h +19 -0
- package/src/platforms/snes/lib/pvsneslib/include/strings.h +5 -0
- package/src/platforms/snes/lib/pvsneslib/source/Makefile +87 -0
- package/src/platforms/snes/lib/pvsneslib/source/backgrounds.asm +834 -0
- package/src/platforms/snes/lib/pvsneslib/source/consoles.asm +1028 -0
- package/src/platforms/snes/lib/pvsneslib/source/crt0_snes.asm +329 -0
- package/src/platforms/snes/lib/pvsneslib/source/dmas.asm +1275 -0
- package/src/platforms/snes/lib/pvsneslib/source/hdr.asm +56 -0
- package/src/platforms/snes/lib/pvsneslib/source/input.asm +445 -0
- package/src/platforms/snes/lib/pvsneslib/source/libc.asm +485 -0
- package/src/platforms/snes/lib/pvsneslib/source/libc_c.c +1214 -0
- package/src/platforms/snes/lib/pvsneslib/source/libm.asm +510 -0
- package/src/platforms/snes/lib/pvsneslib/source/libtcc.asm +374 -0
- package/src/platforms/snes/lib/pvsneslib/source/lzsss.asm +256 -0
- package/src/platforms/snes/lib/pvsneslib/source/maps.asm +1078 -0
- package/src/platforms/snes/lib/pvsneslib/source/objects.asm +2881 -0
- package/src/platforms/snes/lib/pvsneslib/source/scores.asm +226 -0
- package/src/platforms/snes/lib/pvsneslib/source/sm_spc.asm +187 -0
- package/src/platforms/snes/lib/pvsneslib/source/snesmodwla.asm +1186 -0
- package/src/platforms/snes/lib/pvsneslib/source/sounds.asm +273 -0
- package/src/platforms/snes/lib/pvsneslib/source/sprites.asm +3946 -0
- package/src/platforms/snes/lib/pvsneslib/source/vblank.asm +917 -0
- package/src/platforms/snes/lib/pvsneslib/source/videos.asm +1137 -0
- package/src/platforms/snes/lib/reset_init.asm +31 -0
- package/src/platforms/snes/lib/sprite_table_populate.asm +122 -0
- package/src/platforms/snes/lib/vram_dma_upload.asm +42 -0
- package/src/platforms/snes/ppu.js +606 -0
- package/src/platforms/snes/song.js +128 -0
- package/src/playtest/playtest.js +841 -0
- package/src/rom-id/identifier.js +421 -0
- package/src/rom-id/patch.js +217 -0
- package/src/toolchains/_worker/pool.js +253 -0
- package/src/toolchains/_worker/run.js +78 -0
- package/src/toolchains/_worker/wasm-worker.js +233 -0
- package/src/toolchains/arm-none-eabi-gcc/gcc.js +216 -0
- package/src/toolchains/asar/asar.js +542 -0
- package/src/toolchains/assemble-snippet.js +256 -0
- package/src/toolchains/cc65/cc65.js +395 -0
- package/src/toolchains/cc65/da65.js +119 -0
- package/src/toolchains/cc65/dbgparse.js +274 -0
- package/src/toolchains/cc65/preset-resolver.js +59 -0
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +79 -0
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.crt0.s +178 -0
- package/src/toolchains/cc65/presets/nes/chr-ram.cfg +76 -0
- package/src/toolchains/cc65/presets/nes/chr-ram.crt0.s +106 -0
- package/src/toolchains/common/ar.js +121 -0
- package/src/toolchains/common/reassemble.js +353 -0
- package/src/toolchains/common/sdk-cache.js +116 -0
- package/src/toolchains/common/symbols.js +139 -0
- package/src/toolchains/dasm/dasm.js +96 -0
- package/src/toolchains/gba-c/gba-c.js +838 -0
- package/src/toolchains/genesis-c/README.md +61 -0
- package/src/toolchains/genesis-c/genesis-c.js +600 -0
- package/src/toolchains/gnu-ld-map.js +86 -0
- package/src/toolchains/index.js +862 -0
- package/src/toolchains/m68k-elf-gcc/gcc.js +230 -0
- package/src/toolchains/objdump.js +199 -0
- package/src/toolchains/parse-errors.js +338 -0
- package/src/toolchains/registry.js +67 -0
- package/src/toolchains/rgbds/rgbds.js +144 -0
- package/src/toolchains/sdcc/preflight-lint.js +295 -0
- package/src/toolchains/sdcc/sdcc.js +540 -0
- package/src/toolchains/sjasm/sjasm.js +85 -0
- package/src/toolchains/snes-c/snes-c.js +409 -0
- package/src/toolchains/tcc816/tcc816.js +79 -0
- package/src/toolchains/vasm68k/vasm68k.js +138 -0
- package/src/toolchains/wladx/wladx.js +120 -0
- package/src/toolchains/z80/binutils.js +82 -0
|
@@ -0,0 +1,1410 @@
|
|
|
1
|
+
// Disassembly MCP tools — exposed as `disassemble` and `disassembleRom`.
|
|
2
|
+
|
|
3
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
4
|
+
import nodePath from "node:path";
|
|
5
|
+
import { jsonContent, safeTool, writeOutput } from "../util.js";
|
|
6
|
+
import { parseSymbols, buildSymbolMap } from "../../toolchains/common/symbols.js";
|
|
7
|
+
import { registersForPlatform } from "../../platforms/common/registers.js";
|
|
8
|
+
import { findReferencesCore } from "./find-references.js";
|
|
9
|
+
|
|
10
|
+
// ── Per-platform CPU-address → file-offset mappers ────────────────
|
|
11
|
+
// Each returns { bytes, fileOffset, cpu, notes } given the full ROM
|
|
12
|
+
// bytes and a desired CPU address. Throws with a clear message if the
|
|
13
|
+
// address can't be mapped (e.g. outside ROM, bank not present).
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* NES iNES file. PRG-ROM starts at file offset 16; CPU $8000-$FFFF
|
|
17
|
+
* maps into the last 32 KB of PRG (16KB carts mirror $8000 ↔ $C000).
|
|
18
|
+
* Returns the slice starting at the requested CPU address.
|
|
19
|
+
*/
|
|
20
|
+
export function mapNesAddress(data, cpuAddr, length, bank) {
|
|
21
|
+
if (data[0] !== 0x4e || data[1] !== 0x45 || data[2] !== 0x53 || data[3] !== 0x1a) {
|
|
22
|
+
throw new Error("not a valid iNES file (missing NES\\x1a magic at offset 0)");
|
|
23
|
+
}
|
|
24
|
+
const prgBanks = data[4];
|
|
25
|
+
const prgSize = prgBanks * 16384;
|
|
26
|
+
const prgFileStart = 16;
|
|
27
|
+
const num16kBanks = prgSize >> 14; // 16KB banks
|
|
28
|
+
// Mapper number = high nibble of flags6 + high nibble of flags7.
|
|
29
|
+
const mapperNum = ((data[6] >> 4) & 0xF) | (data[7] & 0xF0);
|
|
30
|
+
// For NROM (mapper 0): 16KB → mirrored at $8000 + $C000; 32KB → linear $8000-$FFFF.
|
|
31
|
+
// For mapper>0 the topmost bank is fixed at $C000-$FFFF (UxROM, MMC1 final
|
|
32
|
+
// bank, MMC3 last two banks all do this); other banks are switchable.
|
|
33
|
+
//
|
|
34
|
+
// `bank` (when given) explicitly selects which 16KB PRG bank is mapped into
|
|
35
|
+
// the SWITCHABLE slot ($8000-$BFFF). This is the fix for "disassemble UxROM
|
|
36
|
+
// bank N at $8000" — previously impossible without slicing the ROM by hand.
|
|
37
|
+
// A $C000+ address still resolves to the fixed top bank regardless of `bank`.
|
|
38
|
+
let offsetInPrg;
|
|
39
|
+
let mapperLabel;
|
|
40
|
+
if (bank != null && mapperNum !== 0 && prgSize > 16384) {
|
|
41
|
+
if (bank < 0 || bank >= num16kBanks) {
|
|
42
|
+
throw new Error(`NES bank ${bank} out of range (ROM has ${num16kBanks} × 16KB PRG banks, 0-${num16kBanks - 1})`);
|
|
43
|
+
}
|
|
44
|
+
if (cpuAddr >= 0xC000) {
|
|
45
|
+
// Fixed top bank — `bank` doesn't apply here.
|
|
46
|
+
offsetInPrg = (prgSize - 0x4000) + (cpuAddr - 0xC000);
|
|
47
|
+
mapperLabel = `mapper ${mapperNum} (fixed top bank at $C000; bank arg ignored above $C000)`;
|
|
48
|
+
} else if (cpuAddr >= 0x8000) {
|
|
49
|
+
offsetInPrg = bank * 0x4000 + (cpuAddr - 0x8000);
|
|
50
|
+
mapperLabel = `mapper ${mapperNum} (PRG bank ${bank} mapped at $8000)`;
|
|
51
|
+
} else {
|
|
52
|
+
offsetInPrg = -1;
|
|
53
|
+
mapperLabel = `mapper ${mapperNum}`;
|
|
54
|
+
}
|
|
55
|
+
} else if (prgSize === 16384) {
|
|
56
|
+
// NROM-128 — mirror.
|
|
57
|
+
offsetInPrg = cpuAddr & 0x3FFF;
|
|
58
|
+
mapperLabel = `mapper ${mapperNum} (NROM-128 mirror)`;
|
|
59
|
+
} else if (mapperNum === 0) {
|
|
60
|
+
offsetInPrg = cpuAddr - 0x8000;
|
|
61
|
+
mapperLabel = `mapper 0 (NROM-256)`;
|
|
62
|
+
} else {
|
|
63
|
+
// Banked mapper: top 16KB fixed at $C000, bank 0 at $8000 by default.
|
|
64
|
+
if (cpuAddr >= 0xC000) {
|
|
65
|
+
offsetInPrg = (prgSize - 0x4000) + (cpuAddr - 0xC000);
|
|
66
|
+
} else if (cpuAddr >= 0x8000) {
|
|
67
|
+
offsetInPrg = cpuAddr - 0x8000;
|
|
68
|
+
} else {
|
|
69
|
+
offsetInPrg = -1;
|
|
70
|
+
}
|
|
71
|
+
mapperLabel = `mapper ${mapperNum} (top bank fixed at $C000, bank 0 at $8000 — pass bank:N for a different switchable bank)`;
|
|
72
|
+
}
|
|
73
|
+
if (offsetInPrg < 0 || offsetInPrg >= prgSize) {
|
|
74
|
+
throw new Error(`CPU address $${cpuAddr.toString(16)} outside PRG ROM (${prgSize} bytes, ${mapperLabel})`);
|
|
75
|
+
}
|
|
76
|
+
const fileOffset = prgFileStart + offsetInPrg;
|
|
77
|
+
const slice = data.slice(fileOffset, fileOffset + length);
|
|
78
|
+
return {
|
|
79
|
+
bytes: slice,
|
|
80
|
+
fileOffset,
|
|
81
|
+
cpu: "6502",
|
|
82
|
+
note: `${mapperLabel}, PRG ${prgSize >> 10} KB`,
|
|
83
|
+
mapperNum,
|
|
84
|
+
prgSize,
|
|
85
|
+
prgFileStart,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* SNES LoROM / HiROM file. Handles optional 512-byte SMC copier header,
|
|
91
|
+
* picks LoROM vs HiROM by checking the internal header at $7FC0 or $FFC0.
|
|
92
|
+
*
|
|
93
|
+
* Mapping (no SA-1 / ExHiROM support yet — those are rare for the kind
|
|
94
|
+
* of homebrew agents we care about):
|
|
95
|
+
* LoROM: bank $80-$FF mirrors $00-$7F. Address $XX:8000-$XX:FFFF →
|
|
96
|
+
* file offset = (bank & 0x7F) * 0x8000 + (addr & 0x7FFF) + copier_off
|
|
97
|
+
* HiROM: address $XX:0000-$XX:FFFF (banks $C0-$FF and $40-$7F) →
|
|
98
|
+
* file offset = (bank & 0x3F) * 0x10000 + (addr & 0xFFFF) + copier_off
|
|
99
|
+
*/
|
|
100
|
+
export function mapSnesAddress(data, cpuAddr, length, mapperHint) {
|
|
101
|
+
// Strip optional 512B copier header (SMC files have it; SFC files don't).
|
|
102
|
+
const copierOff = (data.length % 0x8000 === 0x200) ? 0x200 : 0;
|
|
103
|
+
|
|
104
|
+
let isLo, mapper;
|
|
105
|
+
if (mapperHint === "lorom") {
|
|
106
|
+
isLo = true;
|
|
107
|
+
mapper = "LoROM (user-specified)";
|
|
108
|
+
} else if (mapperHint === "hirom") {
|
|
109
|
+
isLo = false;
|
|
110
|
+
mapper = "HiROM (user-specified)";
|
|
111
|
+
} else {
|
|
112
|
+
// Detect mapper: check internal header at LoROM ($7FC0) vs HiROM ($FFC0)
|
|
113
|
+
// by looking at the mapper byte at offset $15 in the header. Valid
|
|
114
|
+
// mapper bytes: $20 (LoROM), $21 (HiROM), $30 (FastROM LoROM), $31 (FastROM HiROM).
|
|
115
|
+
const loMapperByte = data[copierOff + 0x7FC0 + 0x15];
|
|
116
|
+
const hiMapperByte = data[copierOff + 0xFFC0 + 0x15];
|
|
117
|
+
const detectedLo = loMapperByte === 0x20 || loMapperByte === 0x30 || loMapperByte === 0x32;
|
|
118
|
+
const detectedHi = hiMapperByte === 0x21 || hiMapperByte === 0x31;
|
|
119
|
+
if (!detectedLo && !detectedHi) {
|
|
120
|
+
// No valid header — small homebrew often doesn't ship one. Default
|
|
121
|
+
// to LoROM (most common for small carts; HiROM is unusual at <256KB).
|
|
122
|
+
isLo = true;
|
|
123
|
+
mapper = "LoROM (assumed — no valid header found)";
|
|
124
|
+
} else {
|
|
125
|
+
isLo = detectedLo;
|
|
126
|
+
mapper = isLo ? "LoROM" : "HiROM";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Decompose CPU address into bank + offset-within-bank.
|
|
131
|
+
const bank = (cpuAddr >>> 16) & 0xFF;
|
|
132
|
+
const bankAddr = cpuAddr & 0xFFFF;
|
|
133
|
+
|
|
134
|
+
let fileOffset;
|
|
135
|
+
if (isLo) {
|
|
136
|
+
// LoROM: only $8000-$FFFF inside a bank holds ROM data; bank $80-$FF mirrors $00-$7F.
|
|
137
|
+
if (bankAddr < 0x8000) {
|
|
138
|
+
throw new Error(`LoROM: CPU offset $${bankAddr.toString(16)} inside bank is RAM/IO; ROM data is at $8000-$FFFF`);
|
|
139
|
+
}
|
|
140
|
+
fileOffset = copierOff + ((bank & 0x7F) * 0x8000) + (bankAddr - 0x8000);
|
|
141
|
+
} else {
|
|
142
|
+
// HiROM: full 64KB banks; bank $C0+ is mirrors of $40+.
|
|
143
|
+
fileOffset = copierOff + ((bank & 0x3F) * 0x10000) + bankAddr;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (fileOffset < 0 || fileOffset >= data.length) {
|
|
147
|
+
throw new Error(`${mapper}: CPU address $${cpuAddr.toString(16).padStart(6,"0")} → file offset 0x${fileOffset.toString(16)} outside ROM (${data.length} bytes)`);
|
|
148
|
+
}
|
|
149
|
+
const slice = data.slice(fileOffset, fileOffset + length);
|
|
150
|
+
return {
|
|
151
|
+
bytes: slice,
|
|
152
|
+
fileOffset,
|
|
153
|
+
cpu: "65816",
|
|
154
|
+
note: `${mapper}${copierOff ? " (512B copier header stripped)" : ""}, ${data.length >> 10} KB`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Walk a da65-format asm string and prepend `label:` to lines whose
|
|
160
|
+
* leading "$XXXX" address matches a symbol. Loose pattern match — works
|
|
161
|
+
* with da65's default `<addr> <bytes> <mnem>` columnar output.
|
|
162
|
+
*/
|
|
163
|
+
function annotateDisasmWithSymbols(asm, symbolMap) {
|
|
164
|
+
const lines = asm.split(/\r?\n/);
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const line of lines) {
|
|
167
|
+
const m = line.match(/^([0-9A-Fa-f]{4,6}):?\s/);
|
|
168
|
+
if (m) {
|
|
169
|
+
const addr = parseInt(m[1], 16);
|
|
170
|
+
const syms = symbolMap.at(addr);
|
|
171
|
+
if (syms.length > 0) {
|
|
172
|
+
out.push(syms.map((s) => `; ${s.name}:`).join("\n"));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
out.push(line);
|
|
176
|
+
}
|
|
177
|
+
return out.join("\n");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Read N-byte little-endian word from a buffer. Used for vector tables.
|
|
182
|
+
*/
|
|
183
|
+
function readLEWord(data, offset, byteCount) {
|
|
184
|
+
let v = 0;
|
|
185
|
+
for (let i = 0; i < byteCount; i++) v |= data[offset + i] << (i * 8);
|
|
186
|
+
return v >>> 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Read the iNES vector table at PRG-end - 6 bytes. Returns { nmi, reset,
|
|
191
|
+
* irq } as CPU addresses. Returns null if the file isn't iNES.
|
|
192
|
+
*/
|
|
193
|
+
function nesVectors(data) {
|
|
194
|
+
if (data[0] !== 0x4e || data[1] !== 0x45 || data[2] !== 0x53 || data[3] !== 0x1a) return null;
|
|
195
|
+
const prgSize = data[4] * 16384;
|
|
196
|
+
const vecOff = 16 + prgSize - 6;
|
|
197
|
+
return {
|
|
198
|
+
nmi: readLEWord(data, vecOff, 2),
|
|
199
|
+
reset: readLEWord(data, vecOff + 2, 2),
|
|
200
|
+
irq: readLEWord(data, vecOff + 4, 2),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* SNES native + emulation vectors. The internal header at $7FE4 / $FFE4
|
|
206
|
+
* (LoROM / HiROM) holds the native vectors; $7FF4 / $FFF4 the emulation
|
|
207
|
+
* vectors. Picks the right base from the mapping byte at $15.
|
|
208
|
+
*/
|
|
209
|
+
function snesVectors(data) {
|
|
210
|
+
const copierOff = (data.length % 0x8000 === 0x200) ? 0x200 : 0;
|
|
211
|
+
const loMapper = data[copierOff + 0x7FC0 + 0x15];
|
|
212
|
+
const hiMapper = data[copierOff + 0xFFC0 + 0x15];
|
|
213
|
+
const isLo = !(hiMapper === 0x21 || hiMapper === 0x31);
|
|
214
|
+
const headerBase = copierOff + (isLo ? 0x7FC0 : 0xFFC0);
|
|
215
|
+
// Native vectors (cop, brk, abort, nmi, reset, irq) at header+$24..
|
|
216
|
+
return {
|
|
217
|
+
native_cop: readLEWord(data, headerBase + 0x24, 2),
|
|
218
|
+
native_brk: readLEWord(data, headerBase + 0x26, 2),
|
|
219
|
+
native_abort: readLEWord(data, headerBase + 0x28, 2),
|
|
220
|
+
native_nmi: readLEWord(data, headerBase + 0x2A, 2),
|
|
221
|
+
native_reset: readLEWord(data, headerBase + 0x2C, 2),
|
|
222
|
+
native_irq: readLEWord(data, headerBase + 0x2E, 2),
|
|
223
|
+
emu_cop: readLEWord(data, headerBase + 0x34, 2),
|
|
224
|
+
emu_abort: readLEWord(data, headerBase + 0x38, 2),
|
|
225
|
+
emu_nmi: readLEWord(data, headerBase + 0x3A, 2),
|
|
226
|
+
emu_reset: readLEWord(data, headerBase + 0x3C, 2),
|
|
227
|
+
emu_irqbrk: readLEWord(data, headerBase + 0x3E, 2),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* da65 with `--comments 4` emits lines like:
|
|
233
|
+
* ` lda #$05 ; 8000 A9 05`
|
|
234
|
+
* with the CPU address in the trailing comment. Extract it.
|
|
235
|
+
*
|
|
236
|
+
* Returns the integer address, or null if the line has no address comment.
|
|
237
|
+
*/
|
|
238
|
+
function extractInstructionAddress(line) {
|
|
239
|
+
// The trailing comment has form `; <4-6 hex> <bytes...>`. Anchor on
|
|
240
|
+
// the *last* `;` in case the line has multiple.
|
|
241
|
+
const m = line.match(/;\s+([0-9A-Fa-f]{4,6})\s+(?:[0-9A-Fa-f]{2}\b)/);
|
|
242
|
+
return m ? parseInt(m[1], 16) : null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Annotate asm output with hardware register names. Matches operand patterns
|
|
247
|
+
* like `$NNNN` and `$XX:NNNN` in the source column and appends a register
|
|
248
|
+
* name to the trailing comment (or creates one).
|
|
249
|
+
*/
|
|
250
|
+
function annotateRegisters(asm, registers) {
|
|
251
|
+
if (!registers) return asm;
|
|
252
|
+
return asm.split(/\r?\n/).map((line) => {
|
|
253
|
+
if (line.startsWith(";")) return line;
|
|
254
|
+
// Split into source + existing-comment. Conservative — we only consider
|
|
255
|
+
// the FIRST `; ` as the comment boundary (da65 always emits one).
|
|
256
|
+
const sepIdx = line.indexOf(";");
|
|
257
|
+
const src = sepIdx >= 0 ? line.slice(0, sepIdx) : line;
|
|
258
|
+
let regName = null;
|
|
259
|
+
const m24 = src.match(/\$([0-9A-Fa-f]{2}):([0-9A-Fa-f]{4})/);
|
|
260
|
+
if (m24) {
|
|
261
|
+
const addr = (parseInt(m24[1], 16) << 16) | parseInt(m24[2], 16);
|
|
262
|
+
regName = registers[addr] ?? registers[addr & 0xFFFF] ?? null;
|
|
263
|
+
}
|
|
264
|
+
if (!regName) {
|
|
265
|
+
const m16 = src.match(/\$([0-9A-Fa-f]{3,4})\b/);
|
|
266
|
+
if (m16) {
|
|
267
|
+
regName = registers[parseInt(m16[1], 16)] ?? null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!regName) return line;
|
|
271
|
+
if (line.includes(regName)) return line;
|
|
272
|
+
if (sepIdx >= 0) {
|
|
273
|
+
// Tail-append into the existing comment, after the byte-count column.
|
|
274
|
+
return line.replace(/\s*$/, " " + regName);
|
|
275
|
+
}
|
|
276
|
+
return line.replace(/\s*$/, " ; " + regName);
|
|
277
|
+
}).join("\n");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Annotate asm output with file-offset comments. Given a CPU→file offset
|
|
282
|
+
* translator function `cpuToFile(addr)`, each line with a parseable
|
|
283
|
+
* trailing address comment gets `@0xNNNN` injected into it.
|
|
284
|
+
*/
|
|
285
|
+
function annotateFileOffsets(asm, cpuToFile, secondaryCpuToFile) {
|
|
286
|
+
return asm.split(/\r?\n/).map((line) => {
|
|
287
|
+
if (line.startsWith(";")) return line;
|
|
288
|
+
const addr = extractInstructionAddress(line);
|
|
289
|
+
if (addr == null) return line;
|
|
290
|
+
let off;
|
|
291
|
+
try { off = cpuToFile(addr); } catch { return line; }
|
|
292
|
+
if (off == null) return line;
|
|
293
|
+
let tag = "@0x" + off.toString(16).toUpperCase();
|
|
294
|
+
if (secondaryCpuToFile) {
|
|
295
|
+
let off2;
|
|
296
|
+
try { off2 = secondaryCpuToFile(addr); } catch { /* swallow */ }
|
|
297
|
+
if (off2 != null) {
|
|
298
|
+
tag += " (prg @0x" + off2.toString(16).toUpperCase() + ")";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (line.includes(tag)) return line;
|
|
302
|
+
return line.replace(/\s*$/, " " + tag);
|
|
303
|
+
}).join("\n");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Scan disassembled asm for the first end-of-routine instruction (rts, rti,
|
|
308
|
+
* rtl, or bare jmp at column 0). Returns the line index after it, or -1 if
|
|
309
|
+
* none found. Used by `untilReturn`.
|
|
310
|
+
*/
|
|
311
|
+
/**
|
|
312
|
+
* Inject vector-name labels into z80dasm output. The Z80 disassembler
|
|
313
|
+
* emits `LXXXX:` labels for every branch target it sees, but doesn't
|
|
314
|
+
* know which addresses are interrupt vectors. For each label whose
|
|
315
|
+
* address matches a known vector ($0000=reset, $0038=irq, $0066=nmi, etc.),
|
|
316
|
+
* emit an additional `<vec-name>:` comment line.
|
|
317
|
+
*/
|
|
318
|
+
function injectVectorLabels(asm, labels) {
|
|
319
|
+
const byAddr = new Map();
|
|
320
|
+
for (const l of labels) byAddr.set(l.addr, l.name);
|
|
321
|
+
const lines = asm.split(/\r?\n/);
|
|
322
|
+
const out = [];
|
|
323
|
+
for (const line of lines) {
|
|
324
|
+
// Match `LXXXX:` label lines (4 hex for Z80/SM83, 6 for m68k).
|
|
325
|
+
const m = line.match(/^L([0-9A-F]{4,6}):/);
|
|
326
|
+
if (m) {
|
|
327
|
+
const addr = parseInt(m[1], 16);
|
|
328
|
+
const name = byAddr.get(addr);
|
|
329
|
+
if (name) out.push(`${name}:`);
|
|
330
|
+
} else {
|
|
331
|
+
// Or match an instruction line whose trailing comment says ADDR is a vector.
|
|
332
|
+
const ma = line.match(/;\s+([0-9A-F]{4,6})\s+/);
|
|
333
|
+
if (ma) {
|
|
334
|
+
const addr = parseInt(ma[1], 16);
|
|
335
|
+
const name = byAddr.get(addr);
|
|
336
|
+
// Only insert the label if it wasn't already emitted as LXXXX.
|
|
337
|
+
if (name) {
|
|
338
|
+
// Don't double-insert if we just emitted the label above.
|
|
339
|
+
if (out.length > 0 && !out[out.length - 1].startsWith(name + ":")) {
|
|
340
|
+
out.push(`${name}:`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
out.push(line);
|
|
346
|
+
}
|
|
347
|
+
return out.join("\n");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function findFirstReturnLine(asm, cpuFamily = "6502") {
|
|
351
|
+
const lines = asm.split(/\r?\n/);
|
|
352
|
+
for (let i = 0; i < lines.length; i++) {
|
|
353
|
+
const line = lines[i];
|
|
354
|
+
const trimmed = line.trim();
|
|
355
|
+
if (cpuFamily === "z80" || cpuFamily === "sm83") {
|
|
356
|
+
// SM83 differs from Z80 only by missing `retn` (no NMI-return alias).
|
|
357
|
+
// Bare ret, reti, jp <addr>, jp <label> all terminate. Conditional
|
|
358
|
+
// ret (`ret nz` etc) does NOT — routine continues past it.
|
|
359
|
+
if (/^ret\s*(;|$)/.test(trimmed)) return i + 1;
|
|
360
|
+
if (/^reti\b/.test(trimmed)) return i + 1;
|
|
361
|
+
if (cpuFamily === "z80" && /^retn\b/.test(trimmed)) return i + 1;
|
|
362
|
+
if (/^jp\s+\$[0-9A-Fa-f]/.test(trimmed)) return i + 1;
|
|
363
|
+
if (/^jp\s+L[0-9A-Fa-f]/.test(trimmed)) return i + 1;
|
|
364
|
+
// sm83-only: `jp hl` is an indirect jump-table dispatch; treat as
|
|
365
|
+
// routine-end since the decoder can't follow it.
|
|
366
|
+
if (cpuFamily === "sm83" && /^jp\s+hl\b/.test(trimmed)) return i + 1;
|
|
367
|
+
} else {
|
|
368
|
+
if (/^(rts|rti|rtl)\b/.test(trimmed)) return i + 1;
|
|
369
|
+
if (/^jmp\s+\$[0-9A-Fa-f]/.test(trimmed)) return i + 1;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return -1;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* SMS / Game Gear address mapping. The first 48 KB of the ROM is mapped
|
|
377
|
+
* 1:1 to the Z80's lower 48 KB ($0000-$BFFF). Higher banks are
|
|
378
|
+
* page-mapped via the sega mapper, but for v1 we treat the 16 KB at
|
|
379
|
+
* $8000-$BFFF as "bank 0" by default.
|
|
380
|
+
*/
|
|
381
|
+
export function mapSmsAddress(data, cpuAddr, length) {
|
|
382
|
+
// Slot 0: $0000-$3FFF maps to file 0..$3FFF (bank 0, fixed).
|
|
383
|
+
// Slot 1: $4000-$7FFF maps to file $4000..$7FFF (bank 1, fixed by default).
|
|
384
|
+
// Slot 2: $8000-$BFFF maps to file $8000..$BFFF (banked — default = bank 2).
|
|
385
|
+
if (cpuAddr < 0xC000) {
|
|
386
|
+
const fileOffset = cpuAddr;
|
|
387
|
+
if (fileOffset >= data.length) {
|
|
388
|
+
throw new Error(`CPU address $${cpuAddr.toString(16)} past end of SMS ROM (${data.length} bytes)`);
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
bytes: data.slice(fileOffset, fileOffset + length),
|
|
392
|
+
fileOffset,
|
|
393
|
+
cpu: "z80",
|
|
394
|
+
note: `SMS/GG sega mapper, slot ${cpuAddr < 0x4000 ? 0 : cpuAddr < 0x8000 ? 1 : 2} (default bank)`,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
throw new Error(
|
|
398
|
+
`CPU address $${cpuAddr.toString(16)} is in RAM ($C000-$FFFF), not ROM. ` +
|
|
399
|
+
`For SMS disasm, target $0000-$BFFF.`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Game Boy / Game Boy Color address mapping.
|
|
405
|
+
* Slot 0: $0000-$3FFF = file 0..$3FFF (bank 0, fixed)
|
|
406
|
+
* Slot 1: $4000-$7FFF = file (bank * 0x4000)..(bank * 0x4000 + 0x3FFF)
|
|
407
|
+
* Default bank 1 — pass `bank` to disassembleRom for a different one.
|
|
408
|
+
* RAM ($8000+) and I/O are not in the ROM file.
|
|
409
|
+
*/
|
|
410
|
+
export function mapGbAddress(data, cpuAddr, length, bank = 1) {
|
|
411
|
+
if (cpuAddr < 0x4000) {
|
|
412
|
+
const fileOffset = cpuAddr;
|
|
413
|
+
if (fileOffset >= data.length) {
|
|
414
|
+
throw new Error(`CPU address $${cpuAddr.toString(16)} past end of GB ROM (${data.length} bytes)`);
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
bytes: data.slice(fileOffset, fileOffset + length),
|
|
418
|
+
fileOffset,
|
|
419
|
+
cpu: "sm83",
|
|
420
|
+
note: "GB mapper, slot 0 (bank 0, fixed)",
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
if (cpuAddr < 0x8000) {
|
|
424
|
+
const fileOffset = (bank * 0x4000) + (cpuAddr - 0x4000);
|
|
425
|
+
if (fileOffset >= data.length) {
|
|
426
|
+
throw new Error(
|
|
427
|
+
`CPU $${cpuAddr.toString(16)} (bank ${bank}, file 0x${fileOffset.toString(16)}) ` +
|
|
428
|
+
`past end of GB ROM (${data.length} bytes)`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
bytes: data.slice(fileOffset, fileOffset + length),
|
|
433
|
+
fileOffset,
|
|
434
|
+
cpu: "sm83",
|
|
435
|
+
note: `GB mapper, slot 1 (bank ${bank})`,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
throw new Error(
|
|
439
|
+
`CPU address $${cpuAddr.toString(16)} is in VRAM/WRAM/HRAM, not ROM. ` +
|
|
440
|
+
`For GB disasm, target $0000-$7FFF.`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Atari 2600 address mapping. 4 KB carts mirror through $F000-$FFFF (and
|
|
446
|
+
* $D000, $B000, etc. — the 2600 only decodes the low 13 bits of the
|
|
447
|
+
* address). 8 KB+ carts use bank-switching schemes (F8, F6, F4, etc.)
|
|
448
|
+
* that we don't fully model here — disassembling those needs the bank
|
|
449
|
+
* arg to slice into the right 4KB window.
|
|
450
|
+
*/
|
|
451
|
+
export function mapAtari2600Address(data, cpuAddr, length, bank = 0) {
|
|
452
|
+
// Strip mirrors: 2600 addresses are 13-bit. Anything in $1000-$1FFF
|
|
453
|
+
// (mirrored as $3000, $5000, ..., $F000) maps to ROM.
|
|
454
|
+
const stripped = cpuAddr & 0x1FFF;
|
|
455
|
+
if (stripped < 0x1000) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
`CPU address $${cpuAddr.toString(16)} is in TIA/RIOT/RAM space, not ROM. ` +
|
|
458
|
+
`For 2600 disasm, target $1000-$1FFF (or any mirror: $3000, $5000, ..., $F000).`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
const offsetInBank = stripped - 0x1000; // 0..0xFFF
|
|
462
|
+
// Default 4KB cart: single bank, file offset = offsetInBank.
|
|
463
|
+
// Larger carts: caller passes `bank` index.
|
|
464
|
+
const bankSize = Math.min(data.length, 0x1000);
|
|
465
|
+
const fileOffset = bank * bankSize + offsetInBank;
|
|
466
|
+
if (fileOffset >= data.length) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`CPU $${cpuAddr.toString(16)} (bank ${bank}, file 0x${fileOffset.toString(16)}) ` +
|
|
469
|
+
`past end of 2600 ROM (${data.length} bytes)`
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
bytes: data.slice(fileOffset, fileOffset + length),
|
|
474
|
+
fileOffset,
|
|
475
|
+
cpu: "6502",
|
|
476
|
+
note: data.length <= 0x1000
|
|
477
|
+
? "2600 4KB cart (no bank-switching)"
|
|
478
|
+
: `2600 banked cart, bank ${bank} of ${Math.ceil(data.length / 0x1000)}`,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Genesis / Mega Drive address mapping. The cartridge ROM maps 1:1 into
|
|
484
|
+
* the 68000 address space starting at $000000 — flat, big-endian, no
|
|
485
|
+
* mapper for ROMs ≤ 4 MB (the common case). $000000-$0000FF is the vector
|
|
486
|
+
* table (SP at $000000, reset PC at $000004); cart code typically begins
|
|
487
|
+
* at the reset vector. We treat the CPU address as the file offset
|
|
488
|
+
* directly.
|
|
489
|
+
*/
|
|
490
|
+
export function mapGenesisAddress(data, cpuAddr, length) {
|
|
491
|
+
if (cpuAddr + length > data.length) {
|
|
492
|
+
throw new Error(
|
|
493
|
+
`Genesis: CPU address $${cpuAddr.toString(16)} + ${length} extends past ROM ` +
|
|
494
|
+
`(${data.length} bytes). ROM maps 1:1 from $000000; for >4MB carts with mappers, ` +
|
|
495
|
+
`slice the file manually.`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
return {
|
|
499
|
+
bytes: data.slice(cpuAddr, cpuAddr + length),
|
|
500
|
+
fileOffset: cpuAddr,
|
|
501
|
+
cpu: "m68k",
|
|
502
|
+
note: "Genesis ROM maps 1:1 to 68000 $000000+ (flat, big-endian).",
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Atari 7800 address mapping. The cart has a 128-byte header at file
|
|
508
|
+
* offset 0 (skip it). ROM body is at file offset 128 and maps into the
|
|
509
|
+
* 6502 address space starting at varying high addresses depending on
|
|
510
|
+
* cart size:
|
|
511
|
+
* 16 KB: $C000-$FFFF
|
|
512
|
+
* 32 KB: $8000-$FFFF
|
|
513
|
+
* 48 KB: $4000-$FFFF (rare)
|
|
514
|
+
* 144 KB SuperGame: bank-switched at $8000-$BFFF + fixed at $C000
|
|
515
|
+
*/
|
|
516
|
+
export function mapAtari7800Address(data, cpuAddr, length, bank = 0) {
|
|
517
|
+
// Detect header. "ATARI7800" magic at offset 1.
|
|
518
|
+
const hasHeader =
|
|
519
|
+
data.length > 128 &&
|
|
520
|
+
String.fromCharCode(data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9])
|
|
521
|
+
=== "ATARI7800";
|
|
522
|
+
const headerSize = hasHeader ? 128 : 0;
|
|
523
|
+
const romSize = data.length - headerSize;
|
|
524
|
+
// Default behavior: figure out which file-offset corresponds to cpuAddr.
|
|
525
|
+
// For "fixed" carts (no banking), CPU $C000 maps to romStart + (romSize - 16KB)
|
|
526
|
+
// etc. The simplest, most-correct model: assume cart fills high address
|
|
527
|
+
// space ending at $FFFF.
|
|
528
|
+
const cartStartCpu = 0x10000 - romSize; // e.g. 32KB = $8000
|
|
529
|
+
if (cpuAddr < cartStartCpu) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
`CPU address $${cpuAddr.toString(16)} is below cart ROM (cart starts at $${cartStartCpu.toString(16)}). ` +
|
|
532
|
+
`For 7800 disasm, target $${cartStartCpu.toString(16)}-$FFFF.`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
const fileOffset = headerSize + (cpuAddr - cartStartCpu);
|
|
536
|
+
if (fileOffset >= data.length) {
|
|
537
|
+
throw new Error(`CPU $${cpuAddr.toString(16)} past end of 7800 ROM`);
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
bytes: data.slice(fileOffset, fileOffset + length),
|
|
541
|
+
fileOffset,
|
|
542
|
+
cpu: "6502",
|
|
543
|
+
note: hasHeader
|
|
544
|
+
? `7800 ATARI7800 header detected (128B skipped); cart maps to $${cartStartCpu.toString(16).toUpperCase()}-$FFFF`
|
|
545
|
+
: `7800 headerless cart; cart maps to $${cartStartCpu.toString(16).toUpperCase()}-$FFFF`,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Map a C64 CPU address to a file offset in a .prg image.
|
|
551
|
+
*
|
|
552
|
+
* .prg layout: 2-byte little-endian LOAD ADDRESS, then the program bytes.
|
|
553
|
+
* file[0..1] = load address (e.g. $0801 for BASIC start)
|
|
554
|
+
* file[2..] = program bytes loaded contiguously starting at that addr
|
|
555
|
+
*
|
|
556
|
+
* For .crt cart images we'd need to honor the C64 cart header (see CCS64
|
|
557
|
+
* docs); not implemented here — pass `bank` instead and call with the raw
|
|
558
|
+
* binary if you're hand-mapping.
|
|
559
|
+
*/
|
|
560
|
+
export function mapC64Address(data, cpuAddr, length, bank = 0) {
|
|
561
|
+
// Detect .prg by reading the load address and seeing if it makes sense.
|
|
562
|
+
// (Anything is a valid load addr in theory, so we just trust the first
|
|
563
|
+
// 2 bytes here.)
|
|
564
|
+
const loadAddr = data[0] | (data[1] << 8);
|
|
565
|
+
const fileOffset = 2 + (cpuAddr - loadAddr);
|
|
566
|
+
if (cpuAddr < loadAddr) {
|
|
567
|
+
throw new Error(
|
|
568
|
+
`CPU $${cpuAddr.toString(16)} is below the .prg load address $${loadAddr.toString(16)}. ` +
|
|
569
|
+
`This file loads at $${loadAddr.toString(16)} — start disasm there.`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
if (fileOffset < 2 || fileOffset >= data.length) {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`CPU $${cpuAddr.toString(16)} maps to file offset ${fileOffset}, outside this ${data.length}-byte .prg.`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
bytes: data.slice(fileOffset, fileOffset + length),
|
|
579
|
+
fileOffset,
|
|
580
|
+
cpu: "6502",
|
|
581
|
+
note: `c64 .prg load addr = $${loadAddr.toString(16).toUpperCase()}; first byte after header is at file offset 2`,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async function disassembleCore({ path: inPath, base64, startAddress = 0x8000, cpu = "6502", addOrigin = true, symbolsPath, symbolsText, symbolsFormat, outputPath, inline }) {
|
|
586
|
+
if (!inline && !outputPath) {
|
|
587
|
+
throw new Error("disassemble: pass outputPath (write the asm to disk, returns {path}) or inline:true (return the asm in the response).");
|
|
588
|
+
}
|
|
589
|
+
if (!inPath && !base64) {
|
|
590
|
+
throw new Error("disassemble: pass `path` (a binary file on disk) or `base64` (the bytes).");
|
|
591
|
+
}
|
|
592
|
+
const { runDa65 } = await import("../../toolchains/cc65/da65.js");
|
|
593
|
+
const bytes = inPath
|
|
594
|
+
? new Uint8Array(await readFile(inPath))
|
|
595
|
+
: new Uint8Array(Buffer.from(base64, "base64"));
|
|
596
|
+
const r = await runDa65({ bytes, startAddress, cpu });
|
|
597
|
+
let asm = r.asm;
|
|
598
|
+
|
|
599
|
+
// On failure, surface the error in the response — never write the raw
|
|
600
|
+
// da65 error text into outputPath (it'd masquerade as partial asm).
|
|
601
|
+
if (r.exitCode !== 0) {
|
|
602
|
+
return jsonContent({
|
|
603
|
+
ok: false,
|
|
604
|
+
exitCode: r.exitCode,
|
|
605
|
+
bytes: bytes.length,
|
|
606
|
+
startAddress,
|
|
607
|
+
cpu,
|
|
608
|
+
error: firstErrorLine(asm) ?? `da65 exited ${r.exitCode}`,
|
|
609
|
+
errorText: asm,
|
|
610
|
+
note: "Disassembly FAILED — outputPath not written. See `error`.",
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Round-trip origin (cc65 family only — da65 is always 6502-family here).
|
|
615
|
+
if (addOrigin) asm = injectOrigin(asm, startAddress);
|
|
616
|
+
|
|
617
|
+
let symbolCount = 0;
|
|
618
|
+
if (symbolsPath || symbolsText) {
|
|
619
|
+
const text = symbolsPath ? await readFile(symbolsPath, "utf-8") : symbolsText;
|
|
620
|
+
const symbols = parseSymbols({ text, path: symbolsPath, format: symbolsFormat });
|
|
621
|
+
const map = buildSymbolMap(symbols);
|
|
622
|
+
asm = annotateDisasmWithSymbols(asm, map);
|
|
623
|
+
symbolCount = symbols.length;
|
|
624
|
+
}
|
|
625
|
+
const meta = {
|
|
626
|
+
ok: true,
|
|
627
|
+
exitCode: r.exitCode,
|
|
628
|
+
bytes: bytes.length,
|
|
629
|
+
startAddress,
|
|
630
|
+
cpu,
|
|
631
|
+
symbolCount,
|
|
632
|
+
...(inPath ? { sourcePath: inPath } : {}),
|
|
633
|
+
};
|
|
634
|
+
if (inline) {
|
|
635
|
+
return jsonContent({ ...meta, asm });
|
|
636
|
+
}
|
|
637
|
+
const { path } = writeOutput(asm, { outputPath, what: "disassembly asm" });
|
|
638
|
+
return jsonContent({ ...meta, path, asmBytes: asm.length });
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function disassembleRomCore(args) {
|
|
642
|
+
const {
|
|
643
|
+
path: romPath, platform, mapper,
|
|
644
|
+
untilReturn, dataRanges,
|
|
645
|
+
autoLabelVectors,
|
|
646
|
+
outputPath,
|
|
647
|
+
} = args;
|
|
648
|
+
const addOrigin = args.addOrigin ?? true;
|
|
649
|
+
// `let` because Genesis re-points an unset startAddress to the reset vector.
|
|
650
|
+
let startAddress = args.startAddress;
|
|
651
|
+
const annotateRegistersFlag = args.annotateRegisters ?? true;
|
|
652
|
+
const annotateFileOffsetsFlag = args.annotateFileOffsets ?? true;
|
|
653
|
+
|
|
654
|
+
const { runDa65 } = await import("../../toolchains/cc65/da65.js");
|
|
655
|
+
const data = new Uint8Array(await readFile(romPath));
|
|
656
|
+
|
|
657
|
+
// Resolve platform.
|
|
658
|
+
const resolved = platform ?? (
|
|
659
|
+
/\.nes$/i.test(romPath) ? "nes" :
|
|
660
|
+
/\.(sfc|smc)$/i.test(romPath) ? "snes" :
|
|
661
|
+
/\.sms$/i.test(romPath) ? "sms" :
|
|
662
|
+
/\.gg$/i.test(romPath) ? "gg" :
|
|
663
|
+
/\.gb$/i.test(romPath) ? "gb" :
|
|
664
|
+
/\.gbc$/i.test(romPath) ? "gbc" :
|
|
665
|
+
/\.a26$/i.test(romPath) ? "atari2600" :
|
|
666
|
+
/\.a78$/i.test(romPath) ? "atari7800" :
|
|
667
|
+
/\.prg$/i.test(romPath) ? "c64" :
|
|
668
|
+
/\.gba$/i.test(romPath) ? "gba" :
|
|
669
|
+
/\.(lnx|lyx)$/i.test(romPath) ? "lynx" :
|
|
670
|
+
/\.(gen|md|bin)$/i.test(romPath) ? "genesis" :
|
|
671
|
+
null
|
|
672
|
+
);
|
|
673
|
+
if (!resolved) {
|
|
674
|
+
throw new Error(`could not detect platform from path '${romPath}'. Pass platform explicitly.`);
|
|
675
|
+
}
|
|
676
|
+
const cpuFamily = (resolved === "sms" || resolved === "gg") ? "z80"
|
|
677
|
+
: (resolved === "gb" || resolved === "gbc") ? "sm83"
|
|
678
|
+
: (resolved === "genesis") ? "m68k"
|
|
679
|
+
: (resolved === "gba") ? "arm"
|
|
680
|
+
: "6502";
|
|
681
|
+
|
|
682
|
+
// Resolve length: endAddress wins over length; default 256.
|
|
683
|
+
let length;
|
|
684
|
+
if (args.endAddress != null) {
|
|
685
|
+
if (args.length != null) {
|
|
686
|
+
throw new Error("disassembleRom: pass either `length` OR `endAddress`, not both.");
|
|
687
|
+
}
|
|
688
|
+
length = args.endAddress - startAddress + 1;
|
|
689
|
+
if (length <= 0) throw new Error(`disassembleRom: endAddress ($${args.endAddress.toString(16)}) must be >= startAddress ($${startAddress.toString(16)}).`);
|
|
690
|
+
} else {
|
|
691
|
+
length = args.length ?? 256;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Genesis code lives in the low ROM; the default $8000 start (a
|
|
695
|
+
// 6502/Z80 convention) is meaningless for 68000. If the caller
|
|
696
|
+
// didn't override startAddress, begin at the reset vector ($000004
|
|
697
|
+
// holds the reset PC).
|
|
698
|
+
// Genesis code lives in low ROM; the $8000 schema default (a 6502/Z80
|
|
699
|
+
// convention) is meaningless on 68000. When the caller left it at the
|
|
700
|
+
// default, start at the reset vector ($000004 holds the reset PC).
|
|
701
|
+
if (resolved === "genesis" && startAddress === 0x8000 && args.endAddress === undefined) {
|
|
702
|
+
startAddress = (data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]) >>> 0;
|
|
703
|
+
if (startAddress >= data.length) startAddress = 0x200; // fallback
|
|
704
|
+
}
|
|
705
|
+
// GBA ROM maps flat at 0x08000000; the cart entry is at file offset 0
|
|
706
|
+
// (a branch over the 192-byte header). Default to disassembling from the
|
|
707
|
+
// start of ROM in 0x08000000 space when the caller left the 6502 default.
|
|
708
|
+
if (resolved === "gba" && startAddress === 0x8000 && args.endAddress === undefined) {
|
|
709
|
+
startAddress = 0x08000000;
|
|
710
|
+
}
|
|
711
|
+
// Lynx: 65C02 cart image (after a 64-byte "LYNX" header). Homebrew runs
|
|
712
|
+
// from $0200, not the $8000 6502 default — auto-bump when unspecified.
|
|
713
|
+
if (resolved === "lynx" && startAddress === 0x8000 && args.endAddress === undefined) {
|
|
714
|
+
startAddress = 0x0200;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const mapped = resolved === "lynx"
|
|
718
|
+
? (() => {
|
|
719
|
+
const hasHdr = data.length >= 64 && data[0] === 0x4c && data[1] === 0x59 && data[2] === 0x4e && data[3] === 0x58; // "LYNX"
|
|
720
|
+
const base = hasHdr ? 64 : 0;
|
|
721
|
+
const off = base + (startAddress - 0x0200); // flat image loaded at $0200
|
|
722
|
+
if (off < base || off >= data.length) throw new Error(`Lynx: startAddress $${startAddress.toString(16)} is outside the cart image.`);
|
|
723
|
+
return { bytes: data.slice(off, Math.min(data.length, off + length)), fileOffset: off, cpu: "6502" };
|
|
724
|
+
})()
|
|
725
|
+
: resolved === "gba"
|
|
726
|
+
? (() => {
|
|
727
|
+
const base = 0x08000000;
|
|
728
|
+
const off = (startAddress >>> 0) - base;
|
|
729
|
+
if (off < 0 || off >= data.length) throw new Error(`GBA: startAddress $${startAddress.toString(16)} is outside ROM (maps 0x08000000..0x08000000+${data.length}).`);
|
|
730
|
+
return { bytes: data.slice(off, Math.min(data.length, off + length)), fileOffset: off, cpu: "arm" };
|
|
731
|
+
})()
|
|
732
|
+
: resolved === "snes"
|
|
733
|
+
? mapSnesAddress(data, startAddress, length, mapper)
|
|
734
|
+
: resolved === "sms" || resolved === "gg"
|
|
735
|
+
? mapSmsAddress(data, startAddress, length)
|
|
736
|
+
: resolved === "gb" || resolved === "gbc"
|
|
737
|
+
? mapGbAddress(data, startAddress, length, args.bank)
|
|
738
|
+
: resolved === "atari2600"
|
|
739
|
+
? mapAtari2600Address(data, startAddress, length, args.bank ?? 0)
|
|
740
|
+
: resolved === "atari7800"
|
|
741
|
+
? mapAtari7800Address(data, startAddress, length, args.bank ?? 0)
|
|
742
|
+
: resolved === "c64"
|
|
743
|
+
? mapC64Address(data, startAddress, length, args.bank ?? 0)
|
|
744
|
+
: resolved === "genesis"
|
|
745
|
+
? mapGenesisAddress(data, startAddress, length)
|
|
746
|
+
: mapNesAddress(data, startAddress, length, args.bank);
|
|
747
|
+
|
|
748
|
+
// Build vector labels + data ranges. For SMS, no vector table to
|
|
749
|
+
// auto-read (Z80 uses reset $0000 + interrupt vector based on IM
|
|
750
|
+
// mode). For NES/SNES we synthesize a da65 info file.
|
|
751
|
+
let info = null;
|
|
752
|
+
const labels = [];
|
|
753
|
+
if (autoLabelVectors && cpuFamily === "6502") {
|
|
754
|
+
let vecs = null;
|
|
755
|
+
if (resolved === "nes") vecs = nesVectors(data);
|
|
756
|
+
else if (resolved === "snes") vecs = snesVectors(data);
|
|
757
|
+
else if (resolved === "atari2600") {
|
|
758
|
+
// 2600 vectors at $FFFA-$FFFF (NMI/RESET/IRQ); all carts map them
|
|
759
|
+
// to top of 4KB bank. Read last 6 bytes of the file.
|
|
760
|
+
const off = data.length - 6;
|
|
761
|
+
vecs = {
|
|
762
|
+
nmi: data[off + 0] | (data[off + 1] << 8),
|
|
763
|
+
reset: data[off + 2] | (data[off + 3] << 8),
|
|
764
|
+
irq: data[off + 4] | (data[off + 5] << 8),
|
|
765
|
+
};
|
|
766
|
+
} else if (resolved === "atari7800") {
|
|
767
|
+
// 7800 vectors at $FFFA-$FFFF (NMI/RESET/IRQ).
|
|
768
|
+
const off = data.length - 6;
|
|
769
|
+
vecs = {
|
|
770
|
+
nmi: data[off + 0] | (data[off + 1] << 8),
|
|
771
|
+
reset: data[off + 2] | (data[off + 3] << 8),
|
|
772
|
+
irq: data[off + 4] | (data[off + 5] << 8),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
if (vecs) {
|
|
776
|
+
for (const [name, addr] of Object.entries(vecs)) {
|
|
777
|
+
if (addr >= startAddress && addr <= startAddress + length - 1) {
|
|
778
|
+
labels.push({ name, addr });
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (autoLabelVectors && cpuFamily === "z80") {
|
|
784
|
+
// Z80 fixed vectors: reset = $0000, interrupt vectors at $0008
|
|
785
|
+
// ($28 = rst 8), $0010, $0018, $0020, $0028, $0030, $0038. NMI = $0066.
|
|
786
|
+
// SMS uses $38 (IM 1) for vblank, $66 (NMI) for the pause button.
|
|
787
|
+
const z80Vectors = {
|
|
788
|
+
reset: 0x0000,
|
|
789
|
+
rst08: 0x0008, rst10: 0x0010, rst18: 0x0018,
|
|
790
|
+
rst20: 0x0020, rst28: 0x0028, rst30: 0x0030,
|
|
791
|
+
irq: 0x0038, // SMS vblank handler when IM 1
|
|
792
|
+
nmi: 0x0066, // SMS pause button
|
|
793
|
+
};
|
|
794
|
+
for (const [name, addr] of Object.entries(z80Vectors)) {
|
|
795
|
+
if (addr >= startAddress && addr <= startAddress + length - 1) {
|
|
796
|
+
labels.push({ name, addr });
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (autoLabelVectors && cpuFamily === "sm83") {
|
|
801
|
+
// GB vectors: rst $00-$38, plus dedicated IRQ vectors:
|
|
802
|
+
// $0040 vblank IRQ
|
|
803
|
+
// $0048 lcd-stat IRQ
|
|
804
|
+
// $0050 timer IRQ
|
|
805
|
+
// $0058 serial IRQ
|
|
806
|
+
// $0060 joypad IRQ
|
|
807
|
+
// $0100 entry point (cart code starts here after Nintendo logo)
|
|
808
|
+
// $0104-$0133 logo bytes (NOT code — auto-skip if window includes them)
|
|
809
|
+
const gbVectors = {
|
|
810
|
+
reset: 0x0000,
|
|
811
|
+
rst08: 0x0008, rst10: 0x0010, rst18: 0x0018,
|
|
812
|
+
rst20: 0x0020, rst28: 0x0028, rst30: 0x0030, rst38: 0x0038,
|
|
813
|
+
vblank: 0x0040,
|
|
814
|
+
lcd_stat: 0x0048,
|
|
815
|
+
timer: 0x0050,
|
|
816
|
+
serial: 0x0058,
|
|
817
|
+
joypad: 0x0060,
|
|
818
|
+
entry: 0x0100,
|
|
819
|
+
};
|
|
820
|
+
for (const [name, addr] of Object.entries(gbVectors)) {
|
|
821
|
+
if (addr >= startAddress && addr <= startAddress + length - 1) {
|
|
822
|
+
labels.push({ name, addr });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
if (autoLabelVectors && cpuFamily === "m68k") {
|
|
827
|
+
// Genesis 68000 vector table at $000000-$0000FF. The useful ones:
|
|
828
|
+
// $000000 initial SP, $000004 reset PC, $000060 HBL, $000070 VBL.
|
|
829
|
+
// (The full table is 64 long-word vectors; these are the ones game
|
|
830
|
+
// code reaches.) Read the targets from the ROM image and label any
|
|
831
|
+
// that fall inside the disassembly window.
|
|
832
|
+
const vec = (off) => (data[off] << 24 | data[off + 1] << 16 | data[off + 2] << 8 | data[off + 3]) >>> 0;
|
|
833
|
+
const genVectors = {
|
|
834
|
+
reset: vec(0x04),
|
|
835
|
+
hbl: vec(0x70), // level-4 autovector (H-blank)
|
|
836
|
+
vbl: vec(0x78), // level-6 autovector (V-blank)
|
|
837
|
+
};
|
|
838
|
+
for (const [name, t] of Object.entries(genVectors)) {
|
|
839
|
+
if (t >= startAddress && t <= startAddress + length - 1) labels.push({ name, addr: t });
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
// Dedup vector labels by ADDRESS. Two interrupt vectors legitimately
|
|
843
|
+
// sharing one target is valid 6502 (Rygar points NMI and IRQ both at
|
|
844
|
+
// $C0F6) — but da65's LABELDEF and every dasm injector reject two labels
|
|
845
|
+
// at the same address ("Label for address $XXXX already defined"). Keep
|
|
846
|
+
// the first name (vector iteration order is reset/nmi/irq) and record the
|
|
847
|
+
// dropped aliases so the response can surface them.
|
|
848
|
+
const labelAliases = [];
|
|
849
|
+
if (labels.length > 1) {
|
|
850
|
+
const seen = new Map(); // addr -> first label name
|
|
851
|
+
const deduped = [];
|
|
852
|
+
for (const lab of labels) {
|
|
853
|
+
if (seen.has(lab.addr)) {
|
|
854
|
+
labelAliases.push({ alias: lab.name, sameAs: seen.get(lab.addr), addr: lab.addr });
|
|
855
|
+
} else {
|
|
856
|
+
seen.set(lab.addr, lab.name);
|
|
857
|
+
deduped.push(lab);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
labels.length = 0;
|
|
861
|
+
labels.push(...deduped);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const dataRangesInWindow = (dataRanges ?? []).filter((r) => {
|
|
865
|
+
return r.start + r.length - 1 >= startAddress && r.start <= startAddress + length - 1;
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
let asm;
|
|
869
|
+
let exitCode = 0;
|
|
870
|
+
// Non-6502 CPUs disassemble through the native binutils objdump for that
|
|
871
|
+
// ISA (m68k / arm+thumb / z80 / gbz80). The objdump WASM ships in the
|
|
872
|
+
// matching toolchain package (a hard dependency), so it's always present.
|
|
873
|
+
if (cpuFamily === "m68k" || cpuFamily === "arm" || cpuFamily === "z80" || cpuFamily === "sm83") {
|
|
874
|
+
const { runObjdump, objdumpAvailable } = await import("../../toolchains/objdump.js");
|
|
875
|
+
const arch = cpuFamily === "arm" ? (args.thumb ? "thumb" : "arm")
|
|
876
|
+
: cpuFamily === "sm83" ? "gbz80"
|
|
877
|
+
: cpuFamily; // m68k / z80
|
|
878
|
+
if (!objdumpAvailable(arch)) {
|
|
879
|
+
throw new Error(`disassembly needs the ${arch} objdump WASM (in the matching romdev toolchain package). Reinstall the toolchain package.`);
|
|
880
|
+
}
|
|
881
|
+
const r = await runObjdump({ bytes: mapped.bytes, arch, startAddress });
|
|
882
|
+
asm = r.asm; exitCode = r.exitCode;
|
|
883
|
+
if (labels.length > 0 && cpuFamily !== "arm") asm = injectVectorLabels(asm, labels);
|
|
884
|
+
} else {
|
|
885
|
+
if (labels.length > 0 || dataRangesInWindow.length > 0 || mapped.cpu !== "6502") {
|
|
886
|
+
info = buildInfoFile({
|
|
887
|
+
startAddress, length,
|
|
888
|
+
cpu: mapped.cpu,
|
|
889
|
+
labels,
|
|
890
|
+
dataRanges: dataRangesInWindow,
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
const needAddressColumn = annotateRegistersFlag || annotateFileOffsetsFlag;
|
|
894
|
+
const r = await runDa65({
|
|
895
|
+
bytes: mapped.bytes, startAddress, cpu: mapped.cpu, info,
|
|
896
|
+
options: needAddressColumn ? ["--comments", "4"] : [],
|
|
897
|
+
});
|
|
898
|
+
asm = r.asm;
|
|
899
|
+
exitCode = r.exitCode;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// File-offset annotation: per-line cpu→file translator.
|
|
903
|
+
if (annotateFileOffsetsFlag) {
|
|
904
|
+
const cpuToFile = (cpuAddr) => {
|
|
905
|
+
try {
|
|
906
|
+
if (resolved === "snes") return mapSnesAddress(data, cpuAddr, 1, mapper).fileOffset;
|
|
907
|
+
if (resolved === "sms" || resolved === "gg") return mapSmsAddress(data, cpuAddr, 1).fileOffset;
|
|
908
|
+
if (resolved === "gb" || resolved === "gbc") return mapGbAddress(data, cpuAddr, 1, args.bank).fileOffset;
|
|
909
|
+
if (resolved === "atari2600") return mapAtari2600Address(data, cpuAddr, 1, args.bank ?? 0).fileOffset;
|
|
910
|
+
if (resolved === "atari7800") return mapAtari7800Address(data, cpuAddr, 1, args.bank ?? 0).fileOffset;
|
|
911
|
+
if (resolved === "c64") return mapC64Address(data, cpuAddr, 1, args.bank ?? 0).fileOffset;
|
|
912
|
+
if (resolved === "genesis") return mapGenesisAddress(data, cpuAddr, 1).fileOffset;
|
|
913
|
+
// `args.bank` maps $8000-$BFFF to the chosen switchable bank;
|
|
914
|
+
// mapNesAddress ignores it for $C000+ (fixed top bank), so each
|
|
915
|
+
// annotated line points at the correct PRG offset.
|
|
916
|
+
return mapNesAddress(data, cpuAddr, 1, args.bank).fileOffset;
|
|
917
|
+
} catch {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
// Secondary translator for NES — also report the header-stripped
|
|
922
|
+
// PRG offset, since patchFile against `prg.bin` (from extractCart)
|
|
923
|
+
// needs the header-less frame.
|
|
924
|
+
let secondaryCpuToFile;
|
|
925
|
+
if (resolved === "nes") {
|
|
926
|
+
secondaryCpuToFile = (cpuAddr) => {
|
|
927
|
+
try {
|
|
928
|
+
const off = mapNesAddress(data, cpuAddr, 1, args.bank).fileOffset;
|
|
929
|
+
if (off == null) return null;
|
|
930
|
+
return off - 16; // strip iNES header
|
|
931
|
+
} catch {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
asm = annotateFileOffsets(asm, cpuToFile, secondaryCpuToFile);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
if (annotateRegistersFlag) {
|
|
940
|
+
const regs = registersForPlatform(resolved);
|
|
941
|
+
asm = annotateRegisters(asm, regs);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Emit an origin directive so the output ASSEMBLES UNMODIFIED. da65 emits
|
|
945
|
+
// absolute label references for out-of-window branch/jump targets (e.g.
|
|
946
|
+
// `bvs LC006` where LC006 := $C006); in ca65 relocatable mode the
|
|
947
|
+
// assembler can't compute the signed branch offset to an absolute target
|
|
948
|
+
// without knowing the segment's load address → "Range error". An `.org`
|
|
949
|
+
// after `.setcpu` pins it. Only for the cc65/6502 family (da65 output);
|
|
950
|
+
// the z80/sm83/m68k dasms have their own conventions. addOrigin:false
|
|
951
|
+
// opts out (e.g. when feeding into a multi-segment linker config that
|
|
952
|
+
// sets the origin itself).
|
|
953
|
+
if (addOrigin && (cpuFamily === "6502") && exitCode === 0) {
|
|
954
|
+
asm = injectOrigin(asm, startAddress);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Truncate at first return (after annotations so we get the same line
|
|
958
|
+
// count we wrote out).
|
|
959
|
+
let truncatedAtReturn = false;
|
|
960
|
+
if (untilReturn) {
|
|
961
|
+
const cutAt = findFirstReturnLine(asm, cpuFamily);
|
|
962
|
+
if (cutAt > 0) {
|
|
963
|
+
asm = asm.split(/\r?\n/).slice(0, cutAt).join("\n") + "\n";
|
|
964
|
+
truncatedAtReturn = true;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const baseResult = {
|
|
969
|
+
exitCode,
|
|
970
|
+
path: romPath,
|
|
971
|
+
platform: resolved,
|
|
972
|
+
startAddress,
|
|
973
|
+
length: mapped.bytes.length,
|
|
974
|
+
fileOffset: mapped.fileOffset,
|
|
975
|
+
cpu: mapped.cpu,
|
|
976
|
+
note: mapped.note,
|
|
977
|
+
labels: labels.length > 0 ? labels.map((l) => ({ name: l.name, addr: "$" + l.addr.toString(16).toUpperCase() })) : undefined,
|
|
978
|
+
labelAliases: labelAliases.length > 0
|
|
979
|
+
? labelAliases.map((a) => ({ alias: a.alias, sameAs: a.sameAs, addr: "$" + a.addr.toString(16).toUpperCase() }))
|
|
980
|
+
: undefined,
|
|
981
|
+
dataRanges: dataRangesInWindow.length > 0 ? dataRangesInWindow : undefined,
|
|
982
|
+
truncatedAtReturn: truncatedAtReturn || undefined,
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
// On a disassembler failure, do NOT write outputPath — otherwise the raw
|
|
986
|
+
// da65 error string lands in the file the caller intends to Read as asm,
|
|
987
|
+
// and looks like partial output. Surface a top-level `error` + ok:false
|
|
988
|
+
// and keep the (error) text in `errorText` for diagnosis.
|
|
989
|
+
if (exitCode !== 0) {
|
|
990
|
+
return jsonContent({
|
|
991
|
+
...baseResult,
|
|
992
|
+
ok: false,
|
|
993
|
+
error: firstErrorLine(asm) ?? `disassembler exited ${exitCode}`,
|
|
994
|
+
errorText: asm,
|
|
995
|
+
note: (baseResult.note ? baseResult.note + " " : "") +
|
|
996
|
+
"Disassembly FAILED — outputPath was NOT written (so a prior good file isn't clobbered with an error). See `error`.",
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (outputPath) {
|
|
1001
|
+
await writeFile(outputPath, asm);
|
|
1002
|
+
return jsonContent({
|
|
1003
|
+
...baseResult,
|
|
1004
|
+
ok: true,
|
|
1005
|
+
outputPath,
|
|
1006
|
+
asmBytes: asm.length,
|
|
1007
|
+
asmLines: asm.split(/\r?\n/).length - 1,
|
|
1008
|
+
hint: `${asm.length} bytes of asm written to ${outputPath}. Read it directly with your file tools.`,
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
return jsonContent({ ...baseResult, ok: true, asm });
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
async function disassembleProjectCore({ path: romPath, outputDir, platform }) {
|
|
1016
|
+
const { reassembleForPlatform } = await import("../../toolchains/common/reassemble.js");
|
|
1017
|
+
const data = new Uint8Array(await readFile(romPath));
|
|
1018
|
+
const resolved = platform ?? sniffPlatformFromPath(romPath);
|
|
1019
|
+
if (!resolved) throw new Error(`disassembleProject: could not detect platform from '${romPath}'. Pass platform explicitly.`);
|
|
1020
|
+
await mkdir(outputDir, { recursive: true });
|
|
1021
|
+
|
|
1022
|
+
// Plan the regions to disassemble for this platform (per-bank for banked
|
|
1023
|
+
// ROMs; one region for flat ROMs). Each region → its own byte-exact .asm.
|
|
1024
|
+
const regions = planRegions(resolved, data);
|
|
1025
|
+
if (!regions.length) throw new Error(`disassembleProject: no regions planned for '${resolved}' (unsupported or empty ROM).`);
|
|
1026
|
+
|
|
1027
|
+
const out = [];
|
|
1028
|
+
for (const reg of regions) {
|
|
1029
|
+
// Known-data regions (e.g. the GBA cartridge header) are emitted as a
|
|
1030
|
+
// clean `.byte` dump — byte-exact by construction, NOT a failed disasm.
|
|
1031
|
+
const r = reg.kind === "data"
|
|
1032
|
+
? { ok: true, readablePercent: 0, source: dataRegionSource(reg.bytes, reg.startAddress), note: "data region (not code)" }
|
|
1033
|
+
: await reassembleForPlatform({ platform: resolved, bytes: reg.bytes, startAddress: reg.startAddress });
|
|
1034
|
+
const header = `; ${reg.label} — ${reg.bytes.length} bytes @ $${reg.startAddress.toString(16).toUpperCase()} ` +
|
|
1035
|
+
`(file 0x${reg.fileOffset.toString(16).toUpperCase()}), ${resolved}\n` +
|
|
1036
|
+
`; round-trip: ${r.ok ? "BYTE-EXACT" : "FAILED"} · readable ${r.readablePercent}%` +
|
|
1037
|
+
(r.note ? ` · ${r.note}` : "") + "\n\n";
|
|
1038
|
+
await writeFile(nodePath.join(outputDir, reg.file), header + r.source);
|
|
1039
|
+
out.push({
|
|
1040
|
+
region: reg.name, file: reg.file, startAddress: "$" + reg.startAddress.toString(16).toUpperCase(),
|
|
1041
|
+
bytes: reg.bytes.length, roundTripOk: r.ok, readablePercent: r.readablePercent,
|
|
1042
|
+
...(reg.kind === "data" ? { kind: "data" } : {}),
|
|
1043
|
+
...(r.note ? { note: r.note } : {}),
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const allOk = out.every((r) => r.roundTripOk);
|
|
1048
|
+
const avgReadable = Math.round(out.reduce((s, r) => s + r.readablePercent, 0) / out.length);
|
|
1049
|
+
return jsonContent({
|
|
1050
|
+
ok: allOk,
|
|
1051
|
+
path: romPath,
|
|
1052
|
+
platform: resolved,
|
|
1053
|
+
regions: out,
|
|
1054
|
+
roundTrip: { regions: out.length, allByteExact: allOk, failed: out.filter((r) => !r.roundTripOk).map((r) => r.region) },
|
|
1055
|
+
readablePercentAvg: avgReadable,
|
|
1056
|
+
note: allOk
|
|
1057
|
+
? `All ${out.length} region(s) round-trip BYTE-EXACT (avg ${avgReadable}% disassembled as instructions, the rest as .byte data). Edit the .asm files and rebuild.`
|
|
1058
|
+
: `Some regions did NOT round-trip byte-exact — see regions[].note.`,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
export function registerDisasmTools(server, z) {
|
|
1063
|
+
server.tool(
|
|
1064
|
+
"disasm",
|
|
1065
|
+
"Disassemble code — raw bytes, a whole ROM (mapper-aware), a full re-buildable project, or find references to " +
|
|
1066
|
+
"an address. `target`: 'bytes' | 'rom' | 'project' | 'references'.\n" +
|
|
1067
|
+
"'bytes' = RAW da65 over a chunk (path/base64), 6502-family CPU via `cpu`; emits `.org` so it re-assembles.\n" +
|
|
1068
|
+
"'rom' = mapper-aware ROM disassembly with agent annotations: auto-tagged reset/nmi/irq vectors, hardware " +
|
|
1069
|
+
"register names on operands, per-line `; @0xNNNN` file offsets ready for romPatch (NES reports .nes AND PRG " +
|
|
1070
|
+
"offsets). Platform sniffed from extension. GBA=ARM7TDMI (ARM by default, `thumb:true` for Thumb). Use " +
|
|
1071
|
+
"`endAddress`/`untilReturn` for one routine, `dataRanges` to mark non-code, `bank` for a banked slot. " +
|
|
1072
|
+
"pce/msx are 'project'-only here — 'rom' doesn't map them yet.\n" +
|
|
1073
|
+
"'project' = turn a ROM into a complete re-buildable disassembly in one call across all systems; splits into " +
|
|
1074
|
+
"regions, REASSEMBLES each and verifies BYTE-EXACT (`roundTripOk`); non-faithful lines fall back to `.byte` so " +
|
|
1075
|
+
"it ALWAYS rebuilds; `readablePercent` reports instruction-vs-data. (SNES 65816 usually lands at the byte-exact " +
|
|
1076
|
+
"data-only floor — its `.a8/.i8` width state desyncs when instructions and pinned `.byte` mix.)\n" +
|
|
1077
|
+
"'references' = scan a ROM's code for operands matching a CPU `address` and classify each (call/jump/branch/" +
|
|
1078
|
+
"read/write); also walks the vector table. LIMITATION: direct addressing only (indirect/computed jumps + " +
|
|
1079
|
+
"cross-bank refs are missed).",
|
|
1080
|
+
{
|
|
1081
|
+
target: z.enum(["bytes", "rom", "project", "references"]).describe("bytes = raw chunk; rom = mapper-aware ROM; project = full rebuildable disasm; references = find refs to an address."),
|
|
1082
|
+
// shared
|
|
1083
|
+
path: z.string().optional().describe("target=bytes: raw binary path. target=rom/project/references: ROM file path."),
|
|
1084
|
+
base64: z.string().optional().describe("target=bytes: base64 of the bytes (OR `path`)."),
|
|
1085
|
+
platform: z.enum(["nes", "snes", "sms", "gg", "gb", "gbc", "atari2600", "atari7800", "c64", "genesis", "gba", "pce", "msx", "lynx"]).optional().describe("target=rom/project/references: override platform (else sniffed from extension)."),
|
|
1086
|
+
startAddress: z.number().int().min(0).max(0xffffffff).default(0x8000).describe("target=bytes/rom: address of the first byte (GBA auto-bumped to 0x08000000)."),
|
|
1087
|
+
length: z.number().int().min(1).max(65536).optional().describe("target=rom: bytes to disassemble (default 256; mutually exclusive with endAddress)."),
|
|
1088
|
+
addOrigin: z.boolean().default(true).describe("target=bytes/rom: prepend `.org` so the asm re-assembles through ca65."),
|
|
1089
|
+
outputPath: z.string().optional().describe("target=bytes/rom: write asm to disk and return {path}; required for bytes unless inline:true."),
|
|
1090
|
+
inline: z.boolean().default(false).describe("target=bytes: return asm in the response instead of writing to disk."),
|
|
1091
|
+
// bytes
|
|
1092
|
+
cpu: z.enum(["6502", "65c02", "65sc02", "65816", "huc6280"]).default("6502").describe("target=bytes: CPU dialect for da65."),
|
|
1093
|
+
symbolsPath: z.string().optional().describe("target=bytes: symbol file to annotate with labels (asar/WLA .sym or cc65 .lbl)."),
|
|
1094
|
+
symbolsText: z.string().optional().describe("target=bytes: inline symbol-file text."),
|
|
1095
|
+
symbolsFormat: z.enum(["wla", "cc65-lbl"]).optional().describe("target=bytes: explicit symbol-file format override."),
|
|
1096
|
+
// rom
|
|
1097
|
+
bank: z.number().int().min(0).max(255).optional().describe("target=rom: switchable ROM bank to map into the windowed slot (NES mapper>0 $8000 / GB $4000; also 2600/7800/c64)."),
|
|
1098
|
+
thumb: z.boolean().default(false).describe("target=rom: GBA — disassemble as THUMB (16-bit) instead of ARM."),
|
|
1099
|
+
endAddress: z.number().int().min(0).max(0xffffff).optional().describe("target=rom: CPU end address (inclusive); alternative to length."),
|
|
1100
|
+
untilReturn: z.boolean().default(false).describe("target=rom: stop at the first return/unconditional-jump (rts/rti/rtl/jmp, or ret/reti/jp per CPU) — grab one routine."),
|
|
1101
|
+
mapper: z.enum(["lorom", "hirom"]).optional().describe("target=rom/references: SNES mapper override (header-less homebrew defaults lorom)."),
|
|
1102
|
+
dataRanges: z.array(z.object({
|
|
1103
|
+
start: z.number().int().min(0).max(0xffffff),
|
|
1104
|
+
length: z.number().int().min(1),
|
|
1105
|
+
})).optional().describe("target=rom: address ranges to emit as `.byte` data instead of code."),
|
|
1106
|
+
autoLabelVectors: z.boolean().default(true).describe("target=rom: pre-seed reset/nmi/irq labels from the vector table."),
|
|
1107
|
+
annotateRegisters: z.boolean().default(true).describe("target=rom: append `; PPUMASK` etc. to operands hitting a known hardware register."),
|
|
1108
|
+
annotateFileOffsets: z.boolean().default(true).describe("target=rom: append `; @0xNNNN` file offset to every line (for romPatch)."),
|
|
1109
|
+
// project
|
|
1110
|
+
outputDir: z.string().optional().describe("target=project: directory to write the project into (one .asm per region)."),
|
|
1111
|
+
// references
|
|
1112
|
+
address: z.number().int().min(0).max(0xFFFFFF).optional().describe("target=references: CPU address to find references TO."),
|
|
1113
|
+
maxRefsReturned: z.number().int().min(1).max(2048).default(256).describe("target=references: cap the references returned."),
|
|
1114
|
+
},
|
|
1115
|
+
safeTool(async (args) => {
|
|
1116
|
+
switch (args.target) {
|
|
1117
|
+
case "bytes": return await disassembleCore(args);
|
|
1118
|
+
case "rom": return await disassembleRomCore(args);
|
|
1119
|
+
case "project": return await disassembleProjectCore(args);
|
|
1120
|
+
case "references": return jsonContent(await findReferencesCore(args));
|
|
1121
|
+
default: throw new Error(`disasm: unknown target '${args.target}'`);
|
|
1122
|
+
}
|
|
1123
|
+
}),
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/** Trim a long trailing run of a single pad byte ($00 or $FF) from a flat ROM
|
|
1128
|
+
* region so we don't disassemble megabytes of padding. Keeps everything up to
|
|
1129
|
+
* the last non-pad byte (rounded up a little for alignment). */
|
|
1130
|
+
function trimTrailingPad(bytes) {
|
|
1131
|
+
let end = bytes.length;
|
|
1132
|
+
while (end > 0 && (bytes[end - 1] === 0x00 || bytes[end - 1] === 0xFF)) end--;
|
|
1133
|
+
// keep a tiny tail so a final real instruction isn't clipped; align to 16.
|
|
1134
|
+
end = Math.min(bytes.length, (end + 16) & ~0xF);
|
|
1135
|
+
return end < bytes.length ? bytes.slice(0, end) : bytes;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/** Emit a known-data region as a clean, byte-exact `.byte` dump (GAS/cc65 both
|
|
1139
|
+
* accept `.byte` with `.org`). 16 bytes per line, with the address in a comment. */
|
|
1140
|
+
function dataRegionSource(bytes, startAddress) {
|
|
1141
|
+
const rows = [`\t.org $${startAddress.toString(16).toUpperCase()}`];
|
|
1142
|
+
for (let i = 0; i < bytes.length; i += 16) {
|
|
1143
|
+
const slice = Array.from(bytes.slice(i, i + 16));
|
|
1144
|
+
rows.push(
|
|
1145
|
+
"\t.byte " + slice.map((b) => "$" + b.toString(16).padStart(2, "0").toUpperCase()).join(",") +
|
|
1146
|
+
`\t; ${(startAddress + i).toString(16).toUpperCase().padStart(6, "0")}`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
return rows.join("\n") + "\n";
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** Sniff a platform from a ROM file extension (disassembleProject). */
|
|
1153
|
+
function sniffPlatformFromPath(p) {
|
|
1154
|
+
if (/\.nes$/i.test(p)) return "nes";
|
|
1155
|
+
if (/\.(sfc|smc)$/i.test(p)) return "snes";
|
|
1156
|
+
if (/\.gbc$/i.test(p)) return "gbc";
|
|
1157
|
+
if (/\.gb$/i.test(p)) return "gb";
|
|
1158
|
+
if (/\.sms$/i.test(p)) return "sms";
|
|
1159
|
+
if (/\.gg$/i.test(p)) return "gg";
|
|
1160
|
+
if (/\.a26$/i.test(p)) return "atari2600";
|
|
1161
|
+
if (/\.a78$/i.test(p)) return "atari7800";
|
|
1162
|
+
if (/\.prg$/i.test(p)) return "c64";
|
|
1163
|
+
if (/\.(lnx|lyx)$/i.test(p)) return "lynx";
|
|
1164
|
+
if (/\.gba$/i.test(p)) return "gba";
|
|
1165
|
+
if (/\.(gen|md|bin)$/i.test(p)) return "genesis";
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Plan the disassembly regions for a ROM. Banked NES → one region per 16KB PRG
|
|
1171
|
+
* bank (switchable at $8000, fixed top at $C000). Everything else → one region
|
|
1172
|
+
* covering the code body at its load address. Each region:
|
|
1173
|
+
* { name, file, bytes, startAddress, fileOffset, label }
|
|
1174
|
+
*/
|
|
1175
|
+
function planRegions(platform, data) {
|
|
1176
|
+
const regions = [];
|
|
1177
|
+
if (platform === "nes") {
|
|
1178
|
+
if (data[0] !== 0x4e || data[1] !== 0x45 || data[2] !== 0x53 || data[3] !== 0x1a) {
|
|
1179
|
+
throw new Error("disassembleProject: not a valid iNES (.nes) file.");
|
|
1180
|
+
}
|
|
1181
|
+
const prgSize = data[4] * 16384;
|
|
1182
|
+
const numBanks = prgSize >> 14;
|
|
1183
|
+
const PRG = 16;
|
|
1184
|
+
for (let b = 0; b < numBanks; b++) {
|
|
1185
|
+
const isFixedTop = b === numBanks - 1 && numBanks > 1;
|
|
1186
|
+
const org = isFixedTop ? 0xC000 : 0x8000;
|
|
1187
|
+
const fileOffset = PRG + b * 16384;
|
|
1188
|
+
regions.push({
|
|
1189
|
+
name: `bank${b}`, file: `bank${b}.asm`,
|
|
1190
|
+
bytes: data.slice(fileOffset, fileOffset + 16384),
|
|
1191
|
+
startAddress: org, fileOffset,
|
|
1192
|
+
label: `bank ${b}${isFixedTop ? " (fixed $C000)" : " (switchable $8000)"}`,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
return regions;
|
|
1196
|
+
}
|
|
1197
|
+
if (platform === "snes") {
|
|
1198
|
+
// LoROM: 32KB banks each mapped at $8000. One region per 32KB chunk.
|
|
1199
|
+
const hasHeader = (data.length % 1024) === 512; // 512-byte copier header
|
|
1200
|
+
const base = hasHeader ? 512 : 0;
|
|
1201
|
+
const body = data.subarray(base);
|
|
1202
|
+
const BANK = 0x8000;
|
|
1203
|
+
for (let off = 0; off < body.length; off += BANK) {
|
|
1204
|
+
const idx = off / BANK;
|
|
1205
|
+
regions.push({
|
|
1206
|
+
name: `bank${idx}`, file: `bank${idx}.asm`,
|
|
1207
|
+
bytes: body.slice(off, off + BANK),
|
|
1208
|
+
startAddress: 0x8000, fileOffset: base + off,
|
|
1209
|
+
label: `LoROM bank ${idx}`,
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
return regions;
|
|
1213
|
+
}
|
|
1214
|
+
if (platform === "gb" || platform === "gbc") {
|
|
1215
|
+
// 16KB banks; bank 0 fixed at $0000, banks 1+ at $4000.
|
|
1216
|
+
const BANK = 0x4000;
|
|
1217
|
+
for (let off = 0; off < data.length; off += BANK) {
|
|
1218
|
+
const idx = off / BANK;
|
|
1219
|
+
regions.push({
|
|
1220
|
+
name: `bank${idx}`, file: `bank${idx}.asm`,
|
|
1221
|
+
bytes: data.slice(off, off + BANK),
|
|
1222
|
+
startAddress: idx === 0 ? 0x0000 : 0x4000, fileOffset: off,
|
|
1223
|
+
label: `ROM bank ${idx}${idx === 0 ? " ($0000)" : " ($4000)"}`,
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
return regions;
|
|
1227
|
+
}
|
|
1228
|
+
if (platform === "sms" || platform === "gg") {
|
|
1229
|
+
// Z80 mapped at $0000; one region for the whole ROM (Sega mapper banks
|
|
1230
|
+
// beyond 48KB are rarer in homebrew — treat as flat for now).
|
|
1231
|
+
regions.push({ name: "rom", file: "rom.asm", bytes: trimTrailingPad(data.slice(0)), startAddress: 0x0000, fileOffset: 0, label: "ROM ($0000)" });
|
|
1232
|
+
return regions;
|
|
1233
|
+
}
|
|
1234
|
+
if (platform === "genesis") {
|
|
1235
|
+
// 68k flat ROM at $000000; the reset PC is in the vector table. Disassemble
|
|
1236
|
+
// from the reset vector to end (the header + vectors before it are data),
|
|
1237
|
+
// trimming the trailing $00/$FF padding that fills a sized cart.
|
|
1238
|
+
const resetPc = (data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]) >>> 0;
|
|
1239
|
+
const start = (resetPc < data.length && resetPc >= 0x200) ? resetPc : 0x200;
|
|
1240
|
+
regions.push({ name: "rom", file: "rom.asm", bytes: trimTrailingPad(data.slice(start)), startAddress: start, fileOffset: start, label: `code from reset PC $${start.toString(16)}` });
|
|
1241
|
+
return regions;
|
|
1242
|
+
}
|
|
1243
|
+
if (platform === "c64") {
|
|
1244
|
+
const loadAddr = data[0] | (data[1] << 8);
|
|
1245
|
+
regions.push({ name: "prg", file: "prg.asm", bytes: data.slice(2), startAddress: loadAddr, fileOffset: 2, label: `PRG @ $${loadAddr.toString(16)}` });
|
|
1246
|
+
return regions;
|
|
1247
|
+
}
|
|
1248
|
+
if (platform === "atari2600") {
|
|
1249
|
+
regions.push({ name: "rom", file: "rom.asm", bytes: data.slice(0), startAddress: 0xF000, fileOffset: 0, label: "4KB cart @ $F000" });
|
|
1250
|
+
return regions;
|
|
1251
|
+
}
|
|
1252
|
+
if (platform === "atari7800") {
|
|
1253
|
+
const org = 0x10000 - data.length; // cart maps to top of address space
|
|
1254
|
+
regions.push({ name: "rom", file: "rom.asm", bytes: data.slice(0), startAddress: org & 0xFFFF, fileOffset: 0, label: `cart @ $${(org & 0xFFFF).toString(16)}` });
|
|
1255
|
+
return regions;
|
|
1256
|
+
}
|
|
1257
|
+
if (platform === "lynx") {
|
|
1258
|
+
// The Lynx CPU is a 65C02 — the 6502-family da65/ca65 reassembly path
|
|
1259
|
+
// already handles it (CPU_FAMILY.lynx === "6502" in reassemble.js). A `.lnx`
|
|
1260
|
+
// file is a 64-byte LNX header ("LYNX" magic) followed by the raw cart
|
|
1261
|
+
// image; an unheadered `.o`/`.bin` is the image directly. Strip the header,
|
|
1262
|
+
// then disassemble the image as a flat 65C02 region.
|
|
1263
|
+
const hasLnxHeader = data.length >= 64 &&
|
|
1264
|
+
data[0] === 0x4c && data[1] === 0x59 && data[2] === 0x4e && data[3] === 0x58; // "LYNX"
|
|
1265
|
+
const base = hasLnxHeader ? 64 : 0;
|
|
1266
|
+
// Load address is loader-dependent on Lynx (the boot ROM copies the cart to
|
|
1267
|
+
// RAM); homebrew typically runs from $0200 up. We disassemble from $0200 and
|
|
1268
|
+
// LABEL it as an assumption so the user can re-org if their loader differs.
|
|
1269
|
+
const body = trimTrailingPad(data.slice(base));
|
|
1270
|
+
regions.push({
|
|
1271
|
+
name: "cart", file: "cart.asm", bytes: body,
|
|
1272
|
+
startAddress: 0x0200, fileOffset: base,
|
|
1273
|
+
label: `Lynx 65C02 cart @ $0200 (ASSUMED load addr — loader-dependent)${hasLnxHeader ? ", LNX header stripped" : ""}`,
|
|
1274
|
+
});
|
|
1275
|
+
return regions;
|
|
1276
|
+
}
|
|
1277
|
+
if (platform === "pce") {
|
|
1278
|
+
// PC Engine HuCard: the HuC6280 (65C02-family) image. cc65 pce.cfg maps the
|
|
1279
|
+
// reset/IRQ vectors at $FFF6+ and the program high; homebrew typically runs
|
|
1280
|
+
// from the ROM mapped at the top of the address space. Disassemble the image
|
|
1281
|
+
// as a flat region from the cart base — a HuCard has no header to strip.
|
|
1282
|
+
const body = trimTrailingPad(data.slice(0));
|
|
1283
|
+
const org = (0x10000 - body.length) & 0xffff; // cart maps to top of space
|
|
1284
|
+
regions.push({
|
|
1285
|
+
name: "rom", file: "rom.asm", bytes: body,
|
|
1286
|
+
startAddress: org, fileOffset: 0,
|
|
1287
|
+
label: `HuCard @ $${org.toString(16)} (HuC6280)`,
|
|
1288
|
+
});
|
|
1289
|
+
return regions;
|
|
1290
|
+
}
|
|
1291
|
+
if (platform === "msx") {
|
|
1292
|
+
// MSX cartridge maps at $4000-$BFFF; the 16-byte "AB" header at $4000 is
|
|
1293
|
+
// data (magic + INIT/STATEMENT/DEVICE/TEXT pointers), code follows. Skip the
|
|
1294
|
+
// header and disassemble the Z80 image from $4010.
|
|
1295
|
+
const hdr = data.length >= 2 && data[0] === 0x41 && data[1] === 0x42;
|
|
1296
|
+
const base = hdr ? 16 : 0;
|
|
1297
|
+
regions.push({
|
|
1298
|
+
name: "rom", file: "rom.asm", bytes: trimTrailingPad(data.slice(base)),
|
|
1299
|
+
startAddress: 0x4000 + base, fileOffset: base,
|
|
1300
|
+
label: `cart @ $${(0x4000 + base).toString(16)} (Z80)`,
|
|
1301
|
+
});
|
|
1302
|
+
return regions;
|
|
1303
|
+
}
|
|
1304
|
+
if (platform === "gba") {
|
|
1305
|
+
// GBA = ARM7TDMI. ROM maps flat at 0x08000000.
|
|
1306
|
+
// 0x000-0x0BF (192 B) = the cartridge HEADER (entry branch, Nintendo logo,
|
|
1307
|
+
// title, checksums) — pure DATA. Disassembling it as code = garbage, so
|
|
1308
|
+
// it's its own data-only region.
|
|
1309
|
+
// 0x0C0+ = code. Disassembled in ARM mode. HONEST CAVEAT: most GBA C code
|
|
1310
|
+
// is compiled as THUMB (16-bit), which an ARM-mode disassembly decodes
|
|
1311
|
+
// as garbage → it falls back to byte-exact `.byte` (low readability).
|
|
1312
|
+
// Per-region ARM/Thumb mode-tracking isn't built yet (it's a real
|
|
1313
|
+
// feature — the Thumb spans LOOK like valid ARM, including fake `bx`es,
|
|
1314
|
+
// so naive mode-switch following is unreliable). To READ Thumb code use
|
|
1315
|
+
// disassembleRom({platform:'gba', thumb:true}) on the span. The project
|
|
1316
|
+
// ALWAYS rebuilds byte-exact regardless.
|
|
1317
|
+
regions.push({
|
|
1318
|
+
name: "header", file: "header.asm", bytes: data.slice(0, 0xC0), kind: "data",
|
|
1319
|
+
startAddress: 0x08000000, fileOffset: 0,
|
|
1320
|
+
label: "GBA cartridge header (192 B data: entry branch + Nintendo logo + title + checksums)",
|
|
1321
|
+
});
|
|
1322
|
+
regions.push({
|
|
1323
|
+
name: "code", file: "code.asm", bytes: trimTrailingPad(data.slice(0xC0)),
|
|
1324
|
+
startAddress: 0x080000C0, fileOffset: 0xC0,
|
|
1325
|
+
label: "GBA code @ 0x080000C0 (ARM7TDMI, ARM mode) — Thumb spans fall back to byte-exact data; use disassembleRom({thumb:true}) to read them",
|
|
1326
|
+
});
|
|
1327
|
+
return regions;
|
|
1328
|
+
}
|
|
1329
|
+
return regions;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
/**
|
|
1333
|
+
* Prepend a `.org <addr>` directive to da65 output so it re-assembles
|
|
1334
|
+
* unmodified through ca65 (without it, absolute branch targets fail
|
|
1335
|
+
* relocatable assembly with "Range error"). Inserts AFTER any leading
|
|
1336
|
+
* `.setcpu` line da65 emits; idempotent (no-op if an `.org` is already
|
|
1337
|
+
* present near the top).
|
|
1338
|
+
* @param {string} asm
|
|
1339
|
+
* @param {number} addr
|
|
1340
|
+
* @returns {string}
|
|
1341
|
+
*/
|
|
1342
|
+
function injectOrigin(asm, addr) {
|
|
1343
|
+
const org = `.org $${(addr & 0xFFFF).toString(16).toUpperCase().padStart(4, "0")}`;
|
|
1344
|
+
const lines = asm.split(/\r?\n/);
|
|
1345
|
+
// Already has an origin in the first ~10 lines? leave it.
|
|
1346
|
+
if (lines.slice(0, 10).some((l) => /^\s*\.org\b/i.test(l))) return asm;
|
|
1347
|
+
// Insert right after the last leading directive (.setcpu / .segment /
|
|
1348
|
+
// comment / blank) so it sits with the other preamble.
|
|
1349
|
+
let insertAt = 0;
|
|
1350
|
+
for (let i = 0; i < lines.length && i < 10; i++) {
|
|
1351
|
+
if (/^\s*(\.setcpu|\.segment|;|$)/i.test(lines[i])) insertAt = i + 1;
|
|
1352
|
+
else break;
|
|
1353
|
+
}
|
|
1354
|
+
lines.splice(insertAt, 0, org);
|
|
1355
|
+
return lines.join("\n");
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/** Best-effort: pull the first error-looking line out of a failed da65 dump. */
|
|
1359
|
+
function firstErrorLine(text) {
|
|
1360
|
+
if (!text) return null;
|
|
1361
|
+
for (const line of text.split(/\r?\n/)) {
|
|
1362
|
+
if (/\b[Ee]rror\b|:\d+:/.test(line)) return line.trim();
|
|
1363
|
+
}
|
|
1364
|
+
const first = text.split(/\r?\n/).find((l) => l.trim());
|
|
1365
|
+
return first ? first.trim() : null;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Synthesize a da65 info file with vector labels + data ranges in addition
|
|
1370
|
+
* to non-overlapping Code RANGEs so the whole window is decoded as code
|
|
1371
|
+
* except where the agent marked data.
|
|
1372
|
+
*
|
|
1373
|
+
* da65 errors with "Duplicate style for address ..." if any byte is covered
|
|
1374
|
+
* by two RANGEs — so we partition [startAddress, endAddress] into Code
|
|
1375
|
+
* gaps around the data ranges, rather than declaring one big Code RANGE
|
|
1376
|
+
* with data RANGEs on top.
|
|
1377
|
+
*/
|
|
1378
|
+
function buildInfoFile({ startAddress, length, cpu, labels, dataRanges }) {
|
|
1379
|
+
const lo = (n) => "$" + (n & 0xFFFF).toString(16).toUpperCase();
|
|
1380
|
+
const endAddress = startAddress + length - 1;
|
|
1381
|
+
const lines = [];
|
|
1382
|
+
|
|
1383
|
+
// Sort + clip data ranges to window.
|
|
1384
|
+
const drs = dataRanges
|
|
1385
|
+
.map((r) => ({ start: Math.max(r.start, startAddress), end: Math.min(r.start + r.length - 1, endAddress) }))
|
|
1386
|
+
.filter((r) => r.start <= r.end)
|
|
1387
|
+
.sort((a, b) => a.start - b.start);
|
|
1388
|
+
|
|
1389
|
+
// Emit alternating Code / ByteTable ranges.
|
|
1390
|
+
const isHighCpu = cpu === "65816";
|
|
1391
|
+
const codeRange = (s, e) => isHighCpu
|
|
1392
|
+
? `RANGE { START ${lo(s)}; END ${lo(e)}; TYPE Code; ADDRMODE "MX"; };`
|
|
1393
|
+
: `RANGE { START ${lo(s)}; END ${lo(e)}; TYPE Code; };`;
|
|
1394
|
+
const dataRange = (s, e) =>
|
|
1395
|
+
`RANGE { START ${lo(s)}; END ${lo(e)}; TYPE ByteTable; };`;
|
|
1396
|
+
|
|
1397
|
+
let cursor = startAddress;
|
|
1398
|
+
for (const dr of drs) {
|
|
1399
|
+
if (cursor < dr.start) lines.push(codeRange(cursor, dr.start - 1));
|
|
1400
|
+
lines.push(dataRange(dr.start, dr.end));
|
|
1401
|
+
cursor = dr.end + 1;
|
|
1402
|
+
}
|
|
1403
|
+
if (cursor <= endAddress) lines.push(codeRange(cursor, endAddress));
|
|
1404
|
+
|
|
1405
|
+
for (const lab of labels) {
|
|
1406
|
+
if (lab.addr > 0xFFFF) continue;
|
|
1407
|
+
lines.push(`LABEL { NAME "${lab.name}"; ADDR ${lo(lab.addr)}; };`);
|
|
1408
|
+
}
|
|
1409
|
+
return lines.join("\n") + "\n";
|
|
1410
|
+
}
|