Skip to content

Latest commit

 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bk-decompiler — Bandit Kings of Ancient China (NES, USA; JP "Suikoden: Tenmei no Chikai", 1989)

KOEI's 6th NES strategy title and the next target after the five decompiled (ro3k / na1 / gk / na2 / rot3k2). Same historical-sim playstyle as the corpus, set in the Water Margin (Liangshan bandits) era — the draw is the unfamiliar setting on a familiar engine. It is the tightest NA2 sibling yet: byte-for-byte the same ROM shape as NA2 (MMC5, 256K PRG, 128K CHR-ROM, 393232 B) and the same $FFF8 private-vector syscall. Thin game repo over the shared koei-nes engine library (sibling clone); imports it via ../koei-nes.

Status

RE COMPLETE (2026-07-01). All 20 bytecode banks named (988/988 subs, 0 anon), the native fixed-region floor, the data walk (RAM globals + ROM tables + ~1865 strings), and the one in-bank native routine (the bank-28 sound engine) — ~3356 symbols, 0 placeholder names. The bytecode VM transfers from NA2 with zero koei-nes code changes — same machine as na1/ro3k/gk/na2/rot3k2, only the addresses moved; the family-invariant dispatcher = vm_entry + 0x44 offset holds again. Every constant below was verified against the ROM (vectors, frame-pull prologue, dispatcher handler-table loads, syscall/ext-op dispatch), not assumed from NA2. The 8-chapter walkthrough is below; method in walk-method.

const value note
mapper / CHR MMC5 / 128K CHR-ROM == NA2 (rot3k2 was 256K); ROM file = 393232 B, NES 2.0, battery
RESET / NMI / IRQ-BRK $F500 / $F641 / $FFD6 (bare RTI) RESET == NA2/rot3k2; NMI byte-identical to NA2; HW IRQ ignored
OS entry jmp $E000 → $E0E6 OS main is itself bytecode (jsr vm_entry + inline operands)
vm_entry / dispatcher $E42A / $E46E (= entry+0x44) family-invariant offset holds; frame-pull fingerprint verified
handler_lo / handler_hi $EC3F / $ED3F read out of the dispatcher body (LDA $EC3F,X / LDA $ED3F,X)
syscall fakebrk_priv via $FFF8$F8ED NA2's mechanism (rot3k2 moved it to $FFDE); table $F917, invoker $EE3F/$EE52
ext-op ($B7) dispatch $EE71, table $EE8C 32-bit-math postfix sub-machine
bytecode banks 8K windows; 17 carry the 20 2a e4 stub $8000-lib {0,1,9,12,14,26}, $A000-app {2,3,4,5,6,7,8,10,11}, banks 16/31 → fixed-OS (window resolves in walk)
fixed region banks 30/31 ($C000/$E000) fixed_section = prg.bank31, banks_at_c000 = [30]

Entry point

Engine constants live in the [bk] block of koei-nes/tools/games.toml; the symbol table (the RE "linker output") is mesen-labels.toml (seeded with the VM anchors; filled by the walk). Render a bank's bytecode VM-asm with the shared toolchain:

cd ../koei-nes/tools && py -3 koei_vm.py disasm bk <bank>

Chapters (the standard family set)

Reference: MEMORY_MAP · schema (record fields) · data-map (RAM globals + ROM tables) · bank-map (call graph + walk order) · walk-method (how it was done).

Source

The rendered decompilation is committed under source/: source/c/bankNN.c (the fully-labeled decompiled C — the readable deliverable) and source/asm/bankNN.asm (the VM-bytecode disassembly) for all 20 bytecode banks, plus source/asm/bank28.asm (the native 6502 sound engine). Regenerate with the shared toolchain (koei_vm.py disasm/decompile) against mesen-labels.toml.

Cross-game record schemas: koei-nes/tools/RECORD_SCHEMAS.md. The ROM is not committed (copyright); it lives at bk-decompiler/<rom> per the local-paths resolver.

About

Reverse-engineering / decompilation of Bandit Kings of Ancient China (NES, KOEI game 6) over the shared koei-nes VM engine. 988 bytecode subs + native floor + sound engine; 8-chapter walkthrough.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages