gmoben/lsdj-decompiled
GitHub: gmoben/lsdj-decompiled
LSDj 9.4.0 for Game Boy 的逐字节精确反汇编项目,提供完整符号覆盖和可重现构建。
Stars: 0 | Forks: 0
# lsdj-decompiled
这是一个为 Nintendo Game Boy 制作的 **Little Sound Dj (LSDj) 9.4.0 的反汇编**,
采用了 [pret](https://github.com/pret) 项目的风格(`pokered`、
`pokecrystal` 等)。
[RGBDS](https://rgbds.gbdev.io/) 源代码树 **可以重新汇编出一个与原版 ROM 逐字节完全一致的副本**。
当前状态:
- 逐字节复刻,每次提交均已验证
- 完整的符号覆盖:涵盖所有 13 个程序库和 21 个采样包的约 8,800 个已命名的例程、表、字符串和变量 —— 加载生成的 `.sym` 的调试器可以解析一切,不再保留任何自动标签
- ROM 接触到的每个 WRAM/HRAM 字节均已命名:`constants/ram.inc` 中包含约 820 个语义地址(数组带有 `[N]` 大小标签,内部访问渲染为 `base + N`),以及分库的 SRAM 工作歌曲结构(`SramPhraseNotes`、`SramInstruments` 等);仅剩 10 个字节被如实标注为未知
- C 反编译分支取决于能否复现原始的 GBDK 2.95 / SDCC 工具链(参见 `docs/toolchain.md`)
## 为什么
LSDj 是一款面向初代 Game Boy 的音乐 tracker,拥有一个深厚且有着数十年历史的芯片音乐社区。对 ROM 进行可复现的源码级解析有助于对其进行保护、编写文档、挖掘历史 bug 以及研究其引擎。
## 获取 ROM
本仓库不包含 ROM —— 你可以从
获取,那里的 ROM 镜像是免费下载的,并且其 [许可证](https://www.littlesounddj.com/lsd/latest/rom_images/LICENSE.txt)
允许个人使用。LSDj 是 Johan Kotlinski 的作品,本项目不会对其进行任何形式的再分发;你需要将构建指向你自己的副本,所有内容都将由此生成(`*.gb` 已被 git 忽略,并且 pre-commit hook 会拒绝 ROM 作为双重保险措施)。
## 目标 ROM
| 字段 | 值 |
|-------|-------|
| 版本 | LSDj 9.4.0 (稳定版) |
| 文件 | `lsdj9_4_0.gb` |
| 大小 | 1 MiB · MBC5 + RAM + 电池 · 128 KiB SRAM · CGB-enhanced |
| sha1 | `58ba2e44c726c61d8c91423ec82152319dddc2ca` |
| md5 | `b4c353122033f538e88eb00c083c0572` |
有关解码后的文件头和内存映射,请参见 [`docs/rom_map.md`](docs/rom_map.md)。
## 前置条件
- [RGBDS](https://rgbds.gbdev.io/) **v1.0.1** (`rgbasm`, `rgblink`, `rgbfix`, `rgbgfx`)
- `make`
- `python3`(`tools/` 中的引导/代码生成辅助工具)
在 Arch 上:`sudo pacman -S rgbds make python`。
## 快速开始
```
# 1. 从 littlesounddj.com 下载 LSDj 9.4.0,然后可以将 ROM
# 复制到 repo 根目录:
cp /path/to/lsdj9_4_0.gb .
# ...或者将其保留在原处并传递路径:
# make BASEROM=/path/to/lsdj9_4_0.gb
# 2. 确认它是确切的预期 revision:
make verify-rom
# 3. 从你的 ROM 生成反汇编,构建它,并验证结果
# 与原始版本 byte-for-byte 完全一致:
make compare
```
成功的 `make compare` 会打印:
```
==> compare OK: build matches the original ROM byte-for-byte.
```
## 工作原理
反汇编文件本身(`src/*.asm`)是在你的机器上生成的,并且已被 git 忽略 —— 逐字节精确的反汇编只是 ROM 的另一种编码,因此跟踪它毫无意义,也不需要将其打包发布。真正被跟踪的是那些耗费了大量心血的部分:
- `src/bankNN.sym` —— 分库的符号映射表:数千个经过人工验证的例程、表和字符串名称及其注释。
- `constants/ram.inc` —— RAM 变量映射表。
- `tools/carve_clean.py` 中的代码/数据划分元数据。
- `docs/` —— 深度分析文档(内存映射、歌曲格式、存档文件系统、声音引擎、同步协议、编辑器架构、方法论)。
工作流程:`tools/coverage.py` 从中断向量、库切换跳板和每个已命名的符号开始追踪可达代码;随后 `tools/carve_clean.py` +
`tools/gen_main.py` 会根据你的 ROM 生成 `src/bankNN.asm` 和 `src/main.asm` 并应用所有名称;RGBDS 将其重新汇编;`make compare` 会断言结果与你的原版 ROM 逐字节相等。如果字节发生偏移,则说明改动是错误的 —— 这个不变量在每次提交中始终保持成立。
## 策略:先汇编,后匹配 C
该项目基于**两条路线**进行(详情见 [`AGENTS.md`](AGENTS.md)):
- **路线 1 —— 字节匹配的汇编反汇编(进行中)。** 符号映射表和切分元数据是事实依据;生成的汇编树是验证工具。
- **路线 2 —— 匹配的 C 反编译(最终目标,受限开启)。** LSDj 是使用*修改过的 GBDK 2.95* (SDCC) 构建的 C 程序。最终目标是**匹配的** C 语言 —— 即使用原始工具链编译后,能生成逐字节完全相同代码的源代码 —— 通过在保持 `make compare` 通过(绿灯)的前提下,逐个替换 asm 函数(这是 pret 的 `pokeemerald` 配合 `agbcc` 采用的方法)。这*不是*静态重编译(那会生成原生的 C 代码 + runtime,并且永远无法重新构建 ROM)。
路线 2 受限于一个**工具链恢复探索任务**:锁定特定时期的 GBDK 2.95 + SDCC,并在投入 C 语言之前,先逐字节复现一个真实的 LSDj 函数。状态请参见 [`docs/toolchain.md`](docs/toolchain.md)。
## 仓库布局
```
lsdj-decompiled/
├── Makefile # build + `compare` (byte-for-byte verification)
├── rgbdscheck.asm # RGBDS version guard
├── lsdj9_4_0.sha1 # expected base-ROM checksum
├── src/
│ ├── bankNN.sym # per-bank symbol maps (tracked; the curation)
│ ├── bankNN.asm # generated disassembly (git-ignored, from your ROM)
│ └── main.asm # generated top level (git-ignored)
├── include/
│ └── hardware.inc # standard GB hardware definitions (gbdev, CC0)
├── constants/ # symbolic constants
│ ├── ram.inc # RAM variable map (hundreds of named wXxx/hXxx vars)
│ └── song.inc # song-data memory map (offsets, counts)
├── data/ # extracted data tables (added as carved)
├── gfx/ # graphics assets (added as carved)
├── docs/
│ ├── rom_map.md # ROM / memory map and per-bank notes
│ ├── bank_map.md # per-bank classification (generated)
│ ├── functionality.md # what the code does (interrupts, engines)
│ ├── sync.md # link cable sync protocol (serial IRQ, master/slave)
│ ├── editors.md # screen/editor architecture + carving plan
│ ├── data_model.md # song format: chains/phrases/instruments/tables/synths
│ ├── save_format.md # .sav filesystem, compression, kits, lsdsng
│ ├── reversal.md # methodology + the road to full symbol coverage
│ └── toolchain.md # Track 2: period GBDK 2.95 / SDCC 2.2.1 recovery
├── tools/
│ ├── disasm.py # byte-exact SM83->RGBDS disassembler (self-tested)
│ ├── coverage.py # whole-ROM reachability tracing
│ ├── carve_clean.py # regenerate src/bankNN.asm (code/data split + symbols)
│ ├── gen_main.py # generate main.asm (INCLUDE carved banks, INCBIN kits)
│ ├── gen_sym.py # build the debugger symbol file (code + RAM + SRAM)
│ ├── gen_ram_sections.py # named WRAM/SRAM sections for the linker map
│ ├── gen_listing.py # emit build/listing.asm — whole ROM as one annotated file
│ ├── audit_export.py # diff a pasted debugger export against our symbols
│ ├── name_locals_full.py # semantic naming of function-local labels
│ ├── show_func.py # dump one function's disassembly by bank:addr
│ ├── callgraph.py, farcall_targets.py, find_orphans.py, label_gaps.py,
│ │ data_refs.py # audits that drove symbol coverage to 100%
│ ├── emu/ # SameBoy-based live-verification harness
│ ├── toolchain/ # fetch + run the period compiler (under Wine)
│ └── match/ # matching-decompilation harness (compile C, diff vs ROM)
└── .githooks/pre-commit # ROM-leak guard + `make compare`
```
## 开发
启用 pre-commit hook(防止 ROM 泄露 + 逐字节检查):
```
git config core.hooksPath .githooks
```
常用命令:
```
make # build build/lsdj9_4_0.gb
make compare # build, then assert it equals the original ROM
make verify-rom # check your supplied ROM is the right revision
make sym # emit the BANK:ADDR symbol file for emulator debugging
make listing # emit build/listing.asm — the whole ROM as one annotated file
make audit # diff a pasted debugger export (emulicious.out) vs our symbols
make clean # remove build/
```
`make sym` 会生成一个 `BANK:ADDR Name` 格式的符号文件(`build/lsdj9_4_0.sym`,
BGB/SameBoy/Emulicious 格式),并将其部署在 ROM 旁边,同时还会生成一份*仅包含 sections* 的链接器 `.map` 副本(剥离了符号行):Emulicious 会将带有符号的 map 视为其唯一的符号来源,但没有符号的 map 会附加已命名的 WRAM/SRAM sections,并且依然会回退到 `.sym` —— 因此你可以获得真实的 section 名称*以及*精心整理的逐符号注释。每一个例程、数据表、字符串、RAM 变量和 SRAM 歌曲结构都已被命名 —— 在 Emulicious 中,符号树会被完整解析,不会残留任何
`_LABEL_`/`_DATA_` 自动标签。即使你从不阅读汇编代码,这个文件也可以说是整个项目中最有用的产物。
有关完整的贡献者/智能体工作流、编码约定和切分方法,请参见 [`AGENTS.md`](AGENTS.md)。
## 参考
- [pret](https://github.com/pret) —— 确立了该方法的反汇编项目
(`pokered`, `pokecrystal`)。
- [GBDK 2.95 源码](https://github.com/rbong/gbdk/tree/master/gbdk-2.95) ——
构建 LSDj 所使用的工具包(经过修改)。
- [Pan Docs](https://gbdev.io/pandocs/) —— Game Boy 硬件参考。
- [GB Complete Technical Reference](https://gekkio.fi/files/gb-docs/gbctr.pdf) ——
精确的 CPU/指令语义和时序。
- [RGBDS 文档](https://rgbds.gbdev.io/docs/) —— 汇编器/链接器参考。
- [gbdev/hardware.inc](https://github.com/gbdev/hardware.inc) —— 已存入
`include/`。
- [LSDj 文档](https://github.com/jkotlinski/lsdj-doc) 和
[liblsdj](https://github.com/gb-archive/liblsdj) —— 存档/格式内部原理。
## 致谢
LSDj 由 Johan Kotlinski 创作。这是一项独立的、非商业性质的逆向工程与保护工作,与作者没有隶属关系,也未获得其认可。感谢 pret 社区为 Game Boy 反汇编项目指明了方向。
## 许可证
原始的 LSDj ROM **不**受本仓库许可证的保护,本仓库也未以任何形式包含它。此处编写的工具、符号映射、构建脚本和文档均基于 MIT 许可证(参见 [`LICENSE`](LICENSE))。
`include/hardware.inc` 遵循 CC0 许可证 (gbdev)。
标签:Chiptune, Game Boy, 云资产清单, 开源反编译, 汇编, 游戏开发, 逆向工具, 逆向工程