romdevtools 0.27.0 → 0.28.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.
Files changed (144) hide show
  1. package/AGENTS.md +5 -3
  2. package/CHANGELOG.md +309 -0
  3. package/README.md +1 -1
  4. package/examples/README.md +1 -1
  5. package/examples/atari2600/templates/platformer.asm +18 -9
  6. package/examples/atari2600/templates/racing.asm +25 -4
  7. package/examples/atari2600/templates/shmup.asm +30 -5
  8. package/examples/atari2600/templates/sports.asm +41 -9
  9. package/examples/atari7800/templates/hello_sprite.c +8 -4
  10. package/examples/atari7800/templates/platformer.c +12 -8
  11. package/examples/atari7800/templates/puzzle.c +7 -4
  12. package/examples/atari7800/templates/racing.c +5 -2
  13. package/examples/atari7800/templates/shmup.c +8 -4
  14. package/examples/atari7800/templates/sports.c +6 -3
  15. package/examples/c64/templates/platformer.c +28 -24
  16. package/examples/c64/templates/puzzle.c +77 -16
  17. package/examples/c64/templates/racing.c +9 -0
  18. package/examples/c64/templates/shmup.c +13 -1
  19. package/examples/c64/templates/sports.c +9 -4
  20. package/examples/gb/templates/platformer.c +6 -2
  21. package/examples/gb/templates/puzzle.c +279 -101
  22. package/examples/gb/templates/racing.c +13 -1
  23. package/examples/gb/templates/shmup.c +13 -1
  24. package/examples/gb/templates/sports.c +9 -3
  25. package/examples/gba/templates/platformer.c +7 -13
  26. package/examples/gba/templates/puzzle.c +93 -15
  27. package/examples/gba/templates/racing.c +13 -1
  28. package/examples/gba/templates/shmup.c +13 -1
  29. package/examples/gba/templates/sports.c +17 -5
  30. package/examples/gbc/templates/platformer.c +6 -2
  31. package/examples/gbc/templates/puzzle.c +878 -178
  32. package/examples/gbc/templates/racing.c +13 -1
  33. package/examples/gbc/templates/shmup.c +13 -1
  34. package/examples/gbc/templates/sports.c +9 -3
  35. package/examples/genesis/templates/puzzle.c +76 -15
  36. package/examples/genesis/templates/racing.c +13 -1
  37. package/examples/genesis/templates/shmup_2p.c +13 -1
  38. package/examples/gg/templates/platformer.c +4 -0
  39. package/examples/gg/templates/puzzle.c +80 -14
  40. package/examples/gg/templates/racing.c +17 -1
  41. package/examples/gg/templates/shmup.c +17 -1
  42. package/examples/gg/templates/sports.c +4 -0
  43. package/examples/lynx/templates/platformer.c +25 -6
  44. package/examples/lynx/templates/puzzle.c +77 -14
  45. package/examples/lynx/templates/shmup.c +13 -1
  46. package/examples/lynx/templates/sports.c +5 -2
  47. package/examples/msx/platformer/main.c +2 -0
  48. package/examples/msx/puzzle/main.c +78 -15
  49. package/examples/msx/racing/main.c +1 -0
  50. package/examples/msx/shmup/main.c +1 -0
  51. package/examples/msx/sports/main.c +3 -2
  52. package/examples/nes/templates/platformer.c +11 -3
  53. package/examples/nes/templates/puzzle.c +81 -21
  54. package/examples/nes/templates/racing.c +15 -1
  55. package/examples/nes/templates/shmup.c +1 -0
  56. package/examples/nes/templates/sports.c +1 -0
  57. package/examples/pce/platformer/main.c +3 -1
  58. package/examples/pce/puzzle/main.c +78 -12
  59. package/examples/pce/racing/main.c +1 -0
  60. package/examples/pce/shmup/main.c +5 -4
  61. package/examples/pce/sports/main.c +4 -3
  62. package/examples/sms/templates/platformer.c +4 -0
  63. package/examples/sms/templates/puzzle.c +80 -14
  64. package/examples/sms/templates/racing.c +17 -1
  65. package/examples/sms/templates/shmup.c +17 -1
  66. package/examples/sms/templates/shmup_2p.c +17 -1
  67. package/examples/sms/templates/sports.c +4 -0
  68. package/examples/snes/templates/platformer.c +32 -15
  69. package/examples/snes/templates/puzzle.c +84 -16
  70. package/examples/snes/templates/racing.c +20 -1
  71. package/examples/snes/templates/shmup.c +20 -2
  72. package/examples/snes/templates/sports.c +7 -0
  73. package/package.json +12 -12
  74. package/src/cores/wasm/bluemsx_libretro.js +1 -1
  75. package/src/cores/wasm/bluemsx_libretro.wasm +0 -0
  76. package/src/cores/wasm/fceumm_libretro.js +1 -1
  77. package/src/cores/wasm/fceumm_libretro.wasm +0 -0
  78. package/src/cores/wasm/gambatte_libretro.js +1 -1
  79. package/src/cores/wasm/gambatte_libretro.wasm +0 -0
  80. package/src/cores/wasm/geargrafx_libretro.js +1 -1
  81. package/src/cores/wasm/geargrafx_libretro.wasm +0 -0
  82. package/src/cores/wasm/genesis_plus_gx_libretro.js +1 -1
  83. package/src/cores/wasm/genesis_plus_gx_libretro.wasm +0 -0
  84. package/src/cores/wasm/handy_libretro.js +1 -1
  85. package/src/cores/wasm/handy_libretro.wasm +0 -0
  86. package/src/cores/wasm/mgba_libretro.js +1 -1
  87. package/src/cores/wasm/mgba_libretro.wasm +0 -0
  88. package/src/cores/wasm/prosystem_libretro.js +1 -1
  89. package/src/cores/wasm/prosystem_libretro.wasm +0 -0
  90. package/src/cores/wasm/snes9x_libretro.js +1 -1
  91. package/src/cores/wasm/snes9x_libretro.wasm +0 -0
  92. package/src/cores/wasm/stella2014_libretro.js +1 -1
  93. package/src/cores/wasm/stella2014_libretro.wasm +0 -0
  94. package/src/cores/wasm/vice_x64_libretro.js +1 -1
  95. package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
  96. package/src/host/LibretroHost.js +245 -10
  97. package/src/mcp/server.js +6 -0
  98. package/src/mcp/tools/disasm-rebuild.js +315 -65
  99. package/src/mcp/tools/disasm.js +149 -28
  100. package/src/mcp/tools/find-references.js +216 -51
  101. package/src/mcp/tools/frame.js +11 -4
  102. package/src/mcp/tools/index.js +15 -1
  103. package/src/mcp/tools/input.js +26 -3
  104. package/src/mcp/tools/memory.js +208 -39
  105. package/src/mcp/tools/playtest.js +56 -4
  106. package/src/mcp/tools/project.js +35 -9
  107. package/src/mcp/tools/toolchain.js +43 -10
  108. package/src/mcp/tools/watch-memory.js +141 -24
  109. package/src/platforms/_guides/ROMHACKING_PLAYBOOK.md +64 -17
  110. package/src/platforms/atari2600/MENTAL_MODEL.md +5 -1
  111. package/src/platforms/atari2600/TROUBLESHOOTING.md +40 -0
  112. package/src/platforms/atari7800/MENTAL_MODEL.md +27 -6
  113. package/src/platforms/gb/MENTAL_MODEL.md +16 -1
  114. package/src/platforms/gb/TROUBLESHOOTING.md +42 -0
  115. package/src/platforms/gb/lib/c/patch-header.js +7 -4
  116. package/src/platforms/gbc/MENTAL_MODEL.md +12 -0
  117. package/src/platforms/gbc/TROUBLESHOOTING.md +21 -0
  118. package/src/platforms/gbc/lib/c/font.h +43 -0
  119. package/src/platforms/gbc/lib/c/patch-header.js +7 -4
  120. package/src/platforms/genesis/MENTAL_MODEL.md +40 -6
  121. package/src/platforms/genesis/lib/c/genesis_sfx.c +37 -0
  122. package/src/platforms/genesis/lib/c/genesis_sfx.h +1 -0
  123. package/src/platforms/gg/TROUBLESHOOTING.md +13 -17
  124. package/src/platforms/gg/lib/c/gg_crt0.s +14 -2
  125. package/src/platforms/lynx/lib/c/lynx_sfx.c +38 -2
  126. package/src/platforms/lynx/lib/c/lynx_sfx.h +1 -0
  127. package/src/platforms/msx/MENTAL_MODEL.md +6 -0
  128. package/src/platforms/msx/TROUBLESHOOTING.md +21 -0
  129. package/src/platforms/msx/lib/c/msx_crt0.s +27 -0
  130. package/src/platforms/msx/lib/c/msx_hw.h +2 -0
  131. package/src/platforms/msx/lib/c/msx_vdp.c +45 -0
  132. package/src/platforms/nes/MENTAL_MODEL.md +10 -3
  133. package/src/platforms/nes/lib/c/nes_runtime.c +41 -0
  134. package/src/platforms/nes/lib/c/nes_runtime.h +2 -0
  135. package/src/platforms/pce/MENTAL_MODEL.md +9 -0
  136. package/src/platforms/pce/TROUBLESHOOTING.md +9 -0
  137. package/src/platforms/pce/lib/c/pce_hw.h +2 -1
  138. package/src/platforms/pce/lib/c/pce_sound.c +22 -0
  139. package/src/platforms/sms/MENTAL_MODEL.md +5 -0
  140. package/src/platforms/sms/TROUBLESHOOTING.md +6 -0
  141. package/src/platforms/sms/lib/c/sms_crt0.s +14 -2
  142. package/src/platforms/snes/MENTAL_MODEL.md +5 -0
  143. package/src/playtest/playtest.js +73 -3
  144. package/src/toolchains/index.js +37 -8
@@ -727,7 +727,17 @@ export async function buildForPlatform(args) {
727
727
  // the cartridge header + reset vectors which the custom crt0 provides.
728
728
  // MSX: _CODE goes at $4010 — a cartridge maps at $4000-$BFFF and the first
729
729
  // 16 bytes are the ROM header ("AB" + INIT vector) the crt0 emits.
730
- const codeLoc = args.codeLoc ?? (args.platform === "msx" ? MSX_CODE_LOC : 0x0000);
730
+ // SMS/GG: _CODE goes at $0100 $0000-$00FF belongs to the crt0's ABS
731
+ // _HEADER area (reset + RST/IRQ/NMI vectors + _boot). The old default of
732
+ // $0000 linked _CODE ON TOP of the vector table: makebin emitted gsinit
733
+ // at $0000 and the di/im 1/SP-init/ISR vectors were GONE — it booted in a
734
+ // BIOS-less emulator by accident (gsinit happened to sit at the reset
735
+ // vector) but had no working IRQ/NMI/pause handling and was one EI away
736
+ // from jumping into garbage on real hardware.
737
+ const codeLoc = args.codeLoc ?? (
738
+ args.platform === "msx" ? MSX_CODE_LOC
739
+ : (args.platform === "sms" || args.platform === "gg") ? 0x0100
740
+ : 0x0000);
731
741
  const romSize = SDCC_ROM_SIZE[args.platform] ?? 32 * 1024;
732
742
 
733
743
  // crt0 + headers + sources come straight from the caller. The build
@@ -809,32 +819,51 @@ export async function buildForPlatform(args) {
809
819
  // rejected it. Checksum = sum of bytes $0000..$7FEF (everything before
810
820
  // the header), stored little-endian. GG BIOS doesn't check, but writing
811
821
  // it is harmless. Only touches ROMs that actually have the header.
812
- if (binary && r.exitCode === 0 && (args.platform === "sms" || args.platform === "gg") && binary.length >= 0x8000) {
822
+ if (binary && r.exitCode === 0 && (args.platform === "sms" || args.platform === "gg")) {
823
+ // Pad to a full 32KB bank FIRST. sdld emits up to the highest used
824
+ // address, so a small program can come out under $8000 — which (a)
825
+ // skipped this whole header block before (the header guard required
826
+ // 32KB) and (b) odd-size ROMs misbehave on real mappers/flashcarts.
827
+ if (binary.length < 0x8000) {
828
+ const padded = new Uint8Array(0x8000);
829
+ padded.set(binary);
830
+ binary = padded;
831
+ }
813
832
  const hdr = 0x7FF0;
814
833
  const hasHeader = String.fromCharCode(...binary.slice(hdr, hdr + 8)) === "TMR SEGA";
834
+ // Region nibble is PLATFORM-SPECIFIC and load-bearing: 4 = SMS export,
835
+ // 7 = GG international. A .gg ROM stamped with an SMS region (3/4) makes
836
+ // Genesis Plus GX (RetroArch/RetroDECK's SMS+GG core) boot it in "GG
837
+ // running SMS software" COMPATIBILITY mode — wrong video mode + wrong
838
+ // CRAM format for a native-GG program → black/garbled screen on the
839
+ // user's device while our BIOS-less host looked fine. Size nibble $C =
840
+ // 32KB checksum range ($0000-$7FEF).
841
+ const regionSize = args.platform === "gg" ? 0x7C : 0x4C;
815
842
  if (!hasHeader) {
816
843
  // No header emitted by the crt0 → write a complete TMR SEGA header
817
844
  // into the last 16 bytes of bank 0 ($7FF0-$7FFF). Without this the
818
845
  // export (US/EU) SMS BIOS shows "SOFTWARE ERROR" and refuses to run.
819
846
  // $7FF0-$7FF7 "TMR SEGA"; $7FF8-$7FF9 reserved ($00); $7FFA-$7FFB
820
847
  // checksum (filled below); $7FFC-$7FFE product code/version (zeros
821
- // ok for homebrew); $7FFF region+size = $4C (region 4 = export,
822
- // size $C = 32KB, the checksum range that covers $0000-$7FEF).
848
+ // ok for homebrew); $7FFF region+size (see regionSize above).
823
849
  const TMR = [0x54,0x4D,0x52,0x20,0x53,0x45,0x47,0x41]; // "TMR SEGA"
824
850
  for (let i = 0; i < 8; i++) binary[hdr + i] = TMR[i];
825
851
  binary[hdr + 8] = 0x00; binary[hdr + 9] = 0x00; // reserved
826
852
  binary[hdr + 12] = 0x00; binary[hdr + 13] = 0x00; // product code lo
827
853
  binary[hdr + 14] = 0x00; // product/version
828
- binary[hdr + 15] = 0x4C; // region 4 (export) + size $C (32KB)
829
854
  }
855
+ // Always stamp the platform-correct region/size — a crt0-provided header
856
+ // with an SMS region on a .gg build has the same compat-mode problem.
857
+ binary[hdr + 15] = regionSize;
830
858
  // Checksum = sum of bytes $0000..$7FEF (everything before the header),
831
- // stored little-endian at $7FFA. Region/size $4C declares the 32KB
832
- // range, so the BIOS checksums $0000-$7FEF.
859
+ // stored little-endian at $7FFA. Size nibble $C declares the 32KB
860
+ // range, so the BIOS checksums $0000-$7FEF. (The GG BIOS doesn't
861
+ // checksum, but writing it is harmless and correct.)
833
862
  let sum = 0;
834
863
  for (let i = 0; i < 0x7FF0; i++) sum = (sum + binary[i]) & 0xFFFF;
835
864
  binary[0x7FFA] = sum & 0xFF;
836
865
  binary[0x7FFB] = (sum >> 8) & 0xFF;
837
- r.log += `\n--- SMS header ${hasHeader ? "checksum fixed" : "written + checksummed"} ($7FFA=${sum.toString(16).toUpperCase().padStart(4,"0")}, region/size=$4C) ---`;
866
+ r.log += `\n--- ${args.platform.toUpperCase()} header ${hasHeader ? "checksum fixed" : "written + checksummed"} ($7FFA=${sum.toString(16).toUpperCase().padStart(4,"0")}, region/size=$${regionSize.toString(16).toUpperCase()}) ---`;
838
867
  }
839
868
  // MSX: the binary built with codeLoc=$4010 is a $4000-based page image.
840
869
  // SDCC/sdldz80 emit an ihx that, converted to bin, starts at the lowest