Shared Codebase Map
Shared Codebase Map (KN5000 ↔ KN7000 ↔ WSA1)
Many Technics keyboards appear to have had their firmware developed by reusing a common source codebase. This page maps where the KN5000 and KN7000 firmwares match — which is valuable precisely because the two machines use different CPU architectures, so any similarity must originate above the machine-code level, in shared source.
The key constraint: the KN5000 runs a Toshiba TLCS-900/H2; the KN7000 runs a Panasonic MN10300/AM33. Compiled machine code therefore cannot be shared. Everything that does match — identifier names, resource tables, message text, numeric constants — is evidence of a shared source tree that was recompiled for each target.
★ And there is now a control for that constraint. The 1995 SX-WSA1 / SX-WSA1R — a synthesizer, not an arranger — runs the same CPU family as the KN5000, and there the reuse shows up as literal machine code: 32,795 shared bytes against a measured null of zero. Same house style, one product line earlier, at the one layer the KN5000↔KN7000 pair could not use. See The WSA1 case.
Verdict
The evidence establishes, at high confidence, that the KN5000 and KN7000 firmwares are two re-targets of one shared application source tree. The application layer — an object-oriented UI framework, its localization tables, and its resource/audio/menu data — was ported and evolved onto a new CPU and OS, while the platform layer (CPU port, RTOS, compression, image pipeline) was rewritten.
Every match below was located at exact byte offsets in both program ROMs
(kn5000_v10_program.rom and kn7000_program.rom) or table ROMs, and the
headline claims were independently re-verified.
The smoking gun: a shared misspelling
The single most conclusive proof is an idiosyncratic spelling error carried verbatim into both firmwares: “Comporser” (for “Composer”).
| KN5000 | KN7000 | |
|---|---|---|
ComporserNameBoxProc |
0x20406 |
0x23D782 |
MT_GetComporserName |
0x20E94 |
0x23D65C |
A typo cannot arise by coincidence or by convergent naming across two independent teams targeting two different CPUs. It can only be inherited from a common source tree. (Confirmed present in both program ROMs by direct search.)
What cannot be shared: machine code
| KN5000 | KN7000 | |
|---|---|---|
| Main CPU | Toshiba TLCS-900/H2 (TMP94C241F) | Panasonic MN10300/AM33 |
| Program flash mapping | CPU 0xE00000 |
CPU 0x48400000 |
| Instruction encoding | TLCS-900 | MN10300 (byte-aligned CISC) |
| RTOS | custom | named MILK MN10300 Ver1.0R1 |
Because the instruction sets differ, no byte-run of executable code is common to the two images. Every match below is therefore source-level reuse, made visible only because the compiler embeds these strings/tables verbatim regardless of target CPU.
The WSA1 case: when the CPUs match, the machine code IS shared too
The section above is an argument from a constraint — the KN5000 and the KN7000 cannot share object code, so whatever they do share must live above it. That constraint is real, but on its own it leaves one thing untested: is Technics reuse actually a source-level habit, or is it only ever the assets a compiler happens to embed?
The Technics SX-WSA1 / SX-WSA1R (1995) answers
that, because it is the case where the constraint is lifted. It is not an
arranger at all — it is an “acoustic modelling” synthesizer, keyboard and rack —
and it is built around two Toshiba TMP95C061 processors, TLCS-900/H. The
KN5000’s sub-CPU is a TMP94C241, TLCS-900/H2: same instruction encoding, and
the same LLVM tlcs900_backend assembles both disassembly trees. So here there
is no CPU boundary to stop machine code crossing — and it crosses.
32,795 bytes of literal shared object code, against a null of zero
kn5000_shared_runs.py in the WSA1 disassembly tree finds every maximal run of
≥ 16 bytes shared between the four WSA1 images and the KN5000 sub-CPU payload,
and grades each one:
| kept (survived the entropy guard) | 32,795 B |
| rejected as low-entropy fill | 291,802 B |
| shuffle null — same byte histogram, sequence destroyed | 0 B |
| signal-to-null ratio | 32,795× |
| of the kept mass, held in the WSA1’s prom_c | 28,916 B |
(Figures re-run 2026-08-26.)
The entropy guard is the result, not a detail. Without it the “shared” mass is nine parts erase-fill and padding: runs are rejected when fewer than 12 distinct byte values appear, when the modal byte is over 60 % of the run, or when the run is a repeating period of ≤ 4 bytes — and the rejected set is printed rather than silently dropped, because “the kinship is entirely in padding” is an outcome that script exists to be able to report. The second guard is the shuffle null, which preserves the byte histogram and destroys sequence: any run length reachable by chance at this histogram would score there. Nothing does.
That 28,916 of the 32,795 bytes sit in prom_c — the image belonging to the WSA1’s second processor — is the structural finding. The KN5000’s sub-CPU is its tone-generator controller, so the WSA1’s CPU 2 and the KN5000’s sub-CPU are the same design, and the data format follows the controller: the WSA1’s tone database shares the KN5000’s 81-byte per-element voice-parameter block, 63 of 81 columns agreeing on their modal byte against byte-shift nulls of 18–29 and rotation nulls of 19–28.
⚠ Two different numbers exist for this and must not be conflated. An earlier,
separate script (technics_roms/tools/wsa1_kinship.py) reports 31,046 B in 588
runs, with its own null of 0 B against an unrelated Technics ROM. Different
script, different corpus, no entropy guard. Pick one and name its script; do not
average them.
The panel driver is shared, and it is a bijection
A second, independent measurement makes the same point at routine granularity. Both machines carry a Mitsubishi M37471M2196S panel microcontroller. Take the 3,150 bytes of the WSA1’s serial-channel-1 module and the whole 2 MiB KN5000 v10 main program ROM, and list every common substring of ≥ 16 bytes: there are eight, 154 bytes in all, and all eight land inside the KN5000’s control-panel driver — a 3,535-byte window, 0.169 % of that ROM.
The null is what makes it a result. The same scan over the whole 512 KiB of
the WSA1’s prom_b gives 4,399 runs / 126,327 bytes, and exactly eight of
them land in the panel driver: the same eight. A bijection, not a cluster.
⚠ (The count 8 is window-sensitive — a ninth run begins at the module’s last byte
and ends at the panel driver’s first.)
The two packet dispatchers are literally the same instruction with a different
table pointer — ten bytes, of which three differ — and even the delay constants
match (SC1_WaitTicks2/6/51 against DELAY_{2,6,51}_TICKS).
⚠ The counter-example that keeps this honest.
In the same pair of machines, DSP_WriteChannelRegs_Inner is 80 of 81 bytes
identical to the KN5000’s — and the one differing byte is the peripheral
base. Always diff the bytes before reusing a name. Byte identity establishes
that the code is the same; it does not establish that the surrounding machine is.
Three parts of silicon, not just code
The sharing is not only in the firmware. Three custom parts on the 1995 synthesizer carry the same part numbers already reverse-engineered for the KN5000:
| part | WSA1 | KN5000 counterpart | certainty |
|---|---|---|---|
| panel MCU | Mitsubishi M37471M2196S | the same part | solid |
| effects DSP | NEC uPD6383GF-3BA (×3) | IC311 | solid |
| tone generator | Matsushita TC183C230002 | IC303 | ⚠ OCR-ambiguous |
⚠ The parts-list OCR prints the tone generator both as TC183C230002 — matching
the KN5000 — and as TC1830230002, differing in one character. It is not
established, and it is the most consequential of the three, because it would
mean an acoustic-modelling synth still carries the KN5000’s PCM tone generator.
★ And the framework is absent — which is the point
Provenance, because this section rests on ROM bytes. The SX-WSA1 images are not dumps this project made. They are a publicly redistributed set, and the claim that they came out of a rack is the uploader’s testimony. The byte measurements below are reproducible against those images; what is second-hand is where the images came from, not the arithmetic. See the WSA1 overview for the full provenance note.
The WSA1 does not run the MILK toolkit. Zero MT_ or *Proc hits
across its full 2 MB, against working positive controls on the machines that do
have it. So the WSA1 predates the KN line’s application framework: what it
shares is silicon, kernel and assets, not the UI framework this page
otherwise tracks.
That absence is exactly what turns a suggestive result into a shape. Read the two findings together:
| KN5000 ↔ KN7000 | KN5000 ↔ WSA1 | |
|---|---|---|
| CPUs | differ (TLCS-900 vs MN10300) | match (both TLCS-900) |
| machine code shared | none possible | 32,795 B, null 0 B |
| kernel / RTOS | rewritten | one kernel, four processors, two products — see below |
| MILK UI framework | shared, and the evidence for this page’s thesis | absent — it had not been written yet |
| assets | tone-name tables byte-identical incl. padding | the 81-byte voice-parameter block shared with prom_d |
★★ One kernel, four processors, two products
The kernel result is the strongest cross-machine finding in these trees, and it is stronger than “the same RTOS”:
- The WSA1R’s two TMP95C061s build their kernel from ONE SOURCE FILE.
wsa1/kernel/kernel.sassembles twice — into 2,180 bytes ofprom_aand 2,180 bytes ofprom_c, both byte-identical to the EPROMs. Of 941 instruction slots, 81 carry a per-CPU value, and those 81 sites are 21 constants: RAM addresses, array sizes and ROM pointers. The two processors run the same kernel over different RAM maps with different task, semaphore and queue counts. - The same kernel is in both of the KN5000’s processors, in the sub-CPU
payload and — as a separate build, not a copy — in the main program ROM
(
wsa1/notes/kernel_structural_match.py). - ⚠ Byte identity cannot answer this question. A byte search finds zero kernel routines in any of the 41 KN5000 images, because two processors in the same product from the same build already differ in 81 of 941 slots. The match is structural, over decoded token sequences, and it is graded against a foil — non-kernel WSA1R routines of the same lengths, run through the identical search. Kernel routines score 0.742–1.000 (median 0.909, 20 of 20 above 0.70); the 26 foils top out at 0.333, none above 0.70. No overlap, and a gap of 0.41.
The adaptation shows in one detail worth keeping: the WSA1 holds the
interrupt-nesting depth in control register 0x3C and reads it back, while the
KN5000 keeps it in a RAM word and only mirrors it into cr 0x7C. Same RTOS,
two family members, two ways of using the hardware the part provides.
And the asset lineage reaches across all three machines at once:
technics_roms/tools/wsa1_kinship.py finds 195 of the WSA1’s 252 16-character
tone-name fields (77.4 %) occurring verbatim in the KN7000’s table ROM — a 1995
synthesizer and a 2002 arranger, seven years and a CPU architecture apart.
The reuse was never a framework phenomenon. It was a house style that operated at every layer the target allowed: object code where the instruction set permitted it, silicon where the part was still current, and — once the CPU changed and object code stopped travelling — the source tree, the resource tables and the framework that this page documents. The KN5000 sits in the middle of that story rather than at its start: it shares a kernel and a panel driver with the earlier WSA1 generation, and hands a UI framework forward to the KN7000 generation.
Full detail is on the SX-WSA1 pages, and the measurements are re-runnable from the scripts named above; see SX-WSA1 Disassembly.
What is shared: the MILK UI toolkit
Both ROMs embed the runtime symbol tables of the same object-oriented,
event-dispatch UI framework — the “MILK Toolkit” (MT_ API), named by the
KN7000 kernel banner. These are consumed by the framework’s own
class-introspection / RTTI at runtime, so the compiler stores them as literal
strings.
| Framework element | KN5000 | KN7000 | Overlap |
|---|---|---|---|
*Proc class/widget handler names (ViewableProc, InheritedProc, DefaultClassProc, ObjectProc, WindowProc, Vw*/Ac*/Ps*/Iv* taxonomy) |
348 unique, table at 0xB1040–0xB15FF |
537 unique, table at 0x1B3B40+ |
187 exact-identical names; same class hierarchy DefaultClassProc→ClassProc→ObjectProc→ViewableProc |
MT_* toolkit API (MT_GetLanguagePtr, MT_GetProcedure, MT_FLASHWRITE, MT_SleepApTask, MT_WakeUpApTask, MT_IWillWakeUp, MT_VST_PST_OK) |
450 unique | 427 unique | 216 exact-identical, including highly idiosyncratic names and the cooperative-task API |
Framework core (RegisterObjectTable, InitializeObjectTable, DispatchEvent, SendEvent, RegisterTitle, GetTitleNow, CheckViewObject) |
0xB153E–0xB15B4 |
0x1B3B65–0x1B406A |
identical names, same contiguous symbol table |
RTTI / resolution API (MT_GetProcedure, MT_GetModeProc, MT_GetTitleProc, MT_AreYouClassProc) |
present | 0x1B2A4E–0x1B3058 |
identical |
TT_* title-tag vocabulary (TT_SEMENU, TT_SEEASY, TT_SEDIGEFF; SE/DK/CM families) |
211 tags (TT_) at 0xD588+ |
375 tags (_TT_) at 0x1AEF61+ |
141 identical bodies (KN7000 adds a _ prefix) |
| Symbol-table storage format | NUL-terminated + 0xFF even-alignment pad (ALIGNED_STRING) |
same contiguous NUL-terminated table | shared data-format design |
| Developer/system objects | Panel Simulator for HK, PanelSimulator, ClipBoard |
Panel Simulator 2.1 / for IK, ClipBoard, DefaultWindow |
same tool, matured (2.1) |
The Panel Simulator for <model-code> template (HK on the KN5000, IK on the
KN7000) and the whole class-introspection table survive the CPU change intact —
the shared code is the entire event-dispatch + object-registration core, not just
leaf widgets.
Framework internals: the dispatch & object model
Decoding the KN7000 tables reveals how this shared toolkit works internally — mechanism that transfers to the KN5000 too:
- A unified 32-bit message-id space, partitioned by category (the high 16
bits). The same encoding recurs everywhere:
0x0002_xxxxtoolkit opcodes,0x0004_xxxx/0x0008_xxxxMT_method selectors,0x0005_xxxxUI events,0x0006_xxxxsystem/task messages.SleepMainTask, for instance, sends opcode0x00020009with message id0x0006009D. - The
MT_dispatch table (KN7000 name array at0x326CE4, 241 entries) is heterogeneous: each method resolves to either a direct code pointer (≈29, e.g.MT_GetProcedureitself), a selector id (0x0004_xxxx, routed through the kernel), or a pointer into the class-descriptor region — i.e. the toolkit mixes direct calls, late-bound message dispatch and RTTI in one table. - An object property/class metadata region (KN7000 ≈
0x1B1000+): class descriptors delimited by0xFFFFFF00, carrying property lists whose names (parent,top,super,fontcolor,pagemax,editsw, …) are what theMT_GetPropName/MT_GetPropData/MT_GetClass/MT_GetParentClassRTTI reads. - A named-constant table for the property values — recovered by
kn7000_disassembly/tools/gen_constants.py:VF_*view flags,CL_*palette colours,BD_*border styles, and part/track/step enums. These cross-check the rest of the reverse engineering exactly —VF_Invisible=1,VF_Change=4,VF_Const=8are precisely the object flag bits the KN7000’sSetVisible/SetChange/SetConsttest withbtst, andCL_Transparent=0xF7names the transparent palette index used by both machines’ sprite blitters.
What is shared: localization & resource tables
| Element | KN5000 | KN7000 |
|---|---|---|
Multi-language dialog blocks (ATTENTION! / ACHTUNG ! / Perhatian !; Sind Sie sicher ?; Indonesian Apakah yakin akan dihapus ?) in identical EN/DE/FR/ES/Indonesian order |
0x1E4D0+ (repeats 0x25884, 0x3382C, …) |
0x2276B8+ (repeats 0x261E6C, 0x2AE468) |
Vendor-custom 8-bit codepage — ¿Está seguro? = bytes BF 45 73 74 E1 … (¿=0xBF, á=0xE1, û=0xFB), not Latin-1 |
0x1E55E |
0x227BFF (byte-for-byte identical) |
Private engineering test-menu selector (~90 bytes: \|-\|SAVE REMINDER\|'COMPLETED' MESSAGE\|ARE YOU SURE?\|…\|EASY SETTING) |
0xD4D5A |
0x20E735 (byte-identical) |
User prompt Please Insert the Style Convert Disk! |
0x1EE44 |
0x207124 |
A non-user-facing engineering test-menu string present verbatim in both is a direct fingerprint of a shared source/asset tree; the identical vendor codepage implies shared font and string tooling.
What is shared: audio & menu data tables
| Element | KN5000 (table ROM) | KN7000 (table ROM) |
|---|---|---|
Tone-name inventory in 16/17-char centered fields (SymphonicStrings , Concert Strings , CupMuteTrombone , ` Bottle Marimba , Country Fiddle ) — byte-identical incl. padding | SymphonicStrings @ 0x36DD6 | @ 0xA2266` |
||
Easy-Setting / Music-Stylist genre menu ( 8 Beat , 16 Beat, Dance Pop, Jazz Fusion, …) — 10 labels, identical centered padding |
0x1DEFC (prog) |
0x1CCF2C (prog); only traversal order differs |
Evolved, but recognizably the same lineage
Some elements were carried forward with modifications — the signature of a shared codebase that kept developing:
- Version screen: both share the
SOFT VERSIONcaption and the internalMPVersionsymbol; the KN7000 addedRHYTHM/PICTURErows and dropped the MAIN/SUB split (reflecting its single-CPU design). <model> SOUND RAMconvention: the KN7000 extends it to a predecessor list at0x1B8517—KN6000 / KN5000 / KN3000 / KN2000 / KN1600 SOUND RAM— i.e. it explicitly reaches back to import the KN5000’s own sound-RAM data format.- Sound-group / style categories:
DIGITAL DRAWBAR,DRUM KITS,JAZZ COMBO,MARCH & WALTZ,COUNTRY,CUSTOMcarry over exactly; others are reorganized or spelled out (ACCORDION REG.→ACCORDION REGISTER).
The divergence boundary (what was rewritten)
Shared reuse stops cleanly at the application layer:
| Layer | KN5000 | KN7000 |
|---|---|---|
| RTOS/kernel | no banner — but not bespoke: it is the same TLCS-900 kernel the SX-WSA1R’s two processors run, present in both KN5000 CPUs (above) | MILK MN10300 Ver1.0R1 @ 0x3B8AAC |
| Compression | LZSS | LZSS and zlib/deflate 1.0.4 @ 0x3B8604 |
| Photo/demo images | headerless 8bpp bitmaps | adds JPEG (Adobe Photoshop) + Windows BMP |
| Tone-init helper | SwbtWr @ 0x1F410 |
absent (sound-init layer reworked) |
| Version widgets | — | KN7000-only AcProgVerBoxProc, IvMpVerWinProc, DefaultWindow (same naming grammar) |
The KN7000-only additions follow the same framework naming grammar
(Ac*BoxProc, Iv*WinProc), exactly what you expect from new features authored
downstream in a shared codebase.
Note that the image divergence is only partial: the KN7000 adds JPEG for
photos and demo art but still stores its UI icons as headerless
palette-indexed bitmaps reached through a {width, height, pointer} descriptor
hierarchy — the same design as the KN5000 (293 such bitmaps, including a 240
sprite icon set; see the KN7000 image gallery).
So even the graphics pipeline is shared at the UI layer; only the photo/demo
asset format was modernised.
The update-disc container is shared too
Independently of the firmware, the update subsystem is the same across models —
same .SLD container, same 24-bit big-endian size field, same 4 KB
LZSS, same .INF checksum sidecar — only the magic string
differs (SLIDE4K → JKPRG4K/JKTB14K/JKTB24K). See
KN7000 System Update Discs and the KN5000’s
System Update Discs.
Why this matters
Mapping the shared portions lets discoveries transfer between models: a data
format decoded on the well-documented KN5000 gives a decoding head-start on the
KN7000, and the shared MT_/*Proc symbol tables let KN5000 symbol knowledge
seed the KN7000 disassembly directly. Across the whole Technics keyboard line,
this reuse map is a route to understanding the family as a single evolving
system rather than a set of unrelated instruments.
The WSA1 extends that route backwards in time, and past the keyboard line altogether: it is not an arranger, and it still shares a kernel, a panel driver and a voice-parameter format with the KN5000. Anything decoded on the KN5000’s sub-CPU is a head start on the WSA1’s CPU 2, and — because the WSA1 names real effects that the KN5000 ships as programs byte-identical to NO OPERATION on the very same uPD6383GF DSP — the traffic may yet run the other way as well. ⚠ Whether the WSA1’s DSP microprograms are usable on the KN5000 is not demonstrated; the test is to find the WSA1’s upload routine and check its tables against the grammar this site already documents.
Method note
Matches were found by intersecting whole-token string inventories of the two
program ROMs and comparing the table ROMs field-by-field, then verifying exact
byte offsets on both sides. Counts (e.g. “187 identical *Proc names”) are
whole-string set intersections; “byte-identical” means an exact byte-run match
including padding. The comparison covered UI-framework symbols, container/file
formats, localized string tables, and numeric/audio data tables.