Feature Demo & Presentation System
Feature Demo & Presentation System
The KN5000 firmware contains a full XML-based presentation scripting system used for the built-in Feature Demo. This system is significantly more sophisticated than a simple slideshow — it includes an XML parser, an event-driven presentation controller, and a rich tag vocabulary suggesting it was designed for general-purpose use.
SSF Presentation Format
Files in Table Data ROM
Two SSF (presumably “Show/Script/Sequence File”) files are embedded in the Table Data ROM:
| File | ROM Address | Purpose |
|---|---|---|
hkst_55.ssf |
0x87FFF0 (metadata), 0x88000E (XML data) | Feature Demo presentation script |
hkt_87.ssf |
Near boot vectors (table_data.asm line 3855) | Unknown — possibly a second presentation |
Source file: ../../kn5000-roms-disasm/table_data/includes/hkst_55.ssf
XML Format
The Feature Demo script is real XML with a defined structure:
<ACTION>
<ACT NO=1><SHOW OBJ="ftdemo01"></ACT>
<ACT NO=2><SHOW OBJ="ftdemo04"></ACT>
<ACT NO=3><SHOW OBJ="ftdemo05"></ACT>
...
<ACT NO=27><SHOW OBJ="ftdemo48"></ACT>
</ACTION>
The script defines 27 sequential actions, each referencing a named UI object. Objects include ftdemo01–ftdemo48 plus instrument-specific displays like Accordion, Drawbar, and Sdmixer.
XML Tag Vocabulary
The firmware contains a complete XML tag name table at approximately line 87475 of the Program ROM disassembly (kn5000_v10_program.asm):
Structure Tags
| Tag | Close Tag | Purpose |
|---|---|---|
PRESENTATION |
/PRESENTATION |
Top-level presentation wrapper |
ACTION |
/ACTION |
Action sequence container |
ACT |
/ACT |
Individual action step (with NO= attribute) |
Content Tags
| Tag | Close Tag | Purpose |
|---|---|---|
SHOW |
— | Show/display a named UI object |
IMG |
— | Display an image |
SONG |
— | Play a song or MIDI sequence |
FONT |
/FONT |
Font selection for text rendering |
CENTER |
/CENTER |
Text centering |
BR |
— | Line break |
Attribute Tags
| Tag | Purpose |
|---|---|
SRC |
Source reference (file/resource) |
NAME |
Name identifier |
Command Tags
| Tag | Purpose |
|---|---|
EXEC |
Execute a command or routine |
The EXEC tag is particularly notable — it strongly suggests the presentation format was designed to support executing code or commands as part of scripted presentations, going beyond simple display and playback.
Presentation Handler Functions
The firmware implements a full event-driven presentation controller:
Core Handlers
| Function | ROM Address | Line | Purpose |
|---|---|---|---|
AcFdemoScreenProc |
0xF84149 | 308910 | Feature Demo screen handler |
AcPresentationBoxProc |
0xF842B4 | 309070 | Presentation display widget |
AcPresentationControlProc |
0xF8450B | 309287 | Main presentation controller |
IvDemofeature1Proc |
— | ~310702 | Feature Demo event handler 1 |
IvDemofeature2Proc |
— | ~310736 | Feature Demo event handler 2 |
AcPresentationControlProc is the main dispatch routine. It uses a jump table at 0xE9F9B2 to handle different presentation states/events.
Event System
The firmware defines dedicated events for presentation processing:
| Event Constant | Line | Purpose |
|---|---|---|
EV_READPRESENTATION |
78181 | Read and parse a presentation file |
EV_READACTION |
78180 | Read and process an action step |
EV_READSONG |
78178 | Load and play a song reference |
These events integrate with the KN5000’s broader UI event system (see UI Framework).
Feature Demo Assets
Bitmap Images (Table Data ROM)
The Feature Demo includes 6 bitmap images stored in Windows BMP format:
| Asset | ROM Address | Size (bytes) | Content |
|---|---|---|---|
FTBMP01.BMP |
0x880418 | 77,878 | Technics logo with world globe |
FTBMP02.BMP |
0x89344E | 42,678 | Subwoofers |
FTBMP03.BMP |
0x89DB04 | 39,478 | Floppy discs |
FTBMP04.BMP |
0x8A753A | 39,478 | Inserting discs |
FTBMP05.BMP |
0x8B0F70 | 41,078 | Surround sound arrows |
FTBMP06.BMP |
0x8BAFE6 | 77,878 | KN5000 name with rainbow comet |
Total bitmap storage: 318,468 bytes (~311 KB)
File Entry Index (0x8CE01C)
The bitmap file entries use a fixed record format:
struct FeatureDemo_FileEntry {
char filename[12]; // +0x00: null-terminated filename (e.g., "FTBMP01.BMP")
uint32 reserved; // +0x0C: always 0
uint32 data_ptr; // +0x10: pointer to BMP data in ROM
uint32 file_size; // +0x14: size in bytes
}; // Total: 24 bytes per entry
Six entries are stored contiguously at 0x8CE01C, referenced by the metadata header at 0x87FFF0 via FeatureDemo_FileEntry1.
Metadata Header (0x87FFF0)
The presentation file metadata at 0x87FFF0 links the SSF script and asset index:
struct FeatureDemo_FileMetadata {
char filename[12]; // +0x00: "hkst_55.ssf"
uint32 reserved; // +0x0C: 0
uint32 padding_ptr; // +0x10: pointer to 2-byte padding (HKstSSF_Padding)
uint32 xml_ptr; // +0x14: pointer to XML data (Feature_Demo_XML at 0x88000E)
uint32 entries_ptr; // +0x18: pointer to file entries (FeatureDemo_FileEntry1 at 0x8CE01C)
};
UI Objects (Program ROM)
The NAKA UI widget system manages all structured UI elements. See UI Widget Types for full documentation of the 9 widget type bytes, structure layouts, and dispatch mechanism.
48 named UI objects are referenced in the Program ROM:
ftdemo01throughftdemo48— bitmap resource names (aligned strings at lines 6240-6648)Accordion— accordion register display (container object with sub-objects:Accordion1,Accordion2,AcAccordionTab)Drawbar— digital drawbar organ display (container withIvDrawbar1,IvDrawbar2,AcDrawbarName)Sdmixer— sound/sequencer mixer display (container with child parameter structures)
The ftdemo01–ftdemo48 names are bitmap resource references, not complex widget structures. They are simple filename strings (e.g., aligned_string "ftdemo43") that identify image resources loaded during the demo.
The interactive objects (Accordion, Drawbar, Sdmixer) are container objects with child sub-objects, handler procedures, and rendering routines spanning hundreds of lines each:
| Object | Handler | Code Location |
|---|---|---|
| Accordion | IvAccordionProc |
Lines 257980-258362 |
| Drawbar | IvDrawbarProc |
Lines 262262-264436 |
| Sdmixer | (TT_SDMIXER) | Line 45566 |
Object Dispatch Architecture
When AcPresentationControlProc (0xF8450B) processes a <SHOW OBJ="..."> action:
- The XML parser extracts the OBJ name string
- The name is matched against the resource/object name table
- For bitmap resources (ftdemo01-48): loads the image and renders to VRAM
- For container objects (Accordion, Drawbar, Sdmixer): activates the interactive UI handler
The dispatch uses a jump table at 0xE9F9B2 indexed by event type:
- Event codes 0x1C00002 through 0x1C0000C branch to specific handlers
- Each handler extracts its object pointer from the workspace and processes render/update/event actions
Handler call pattern:
AcPresentationControlProc:
; Calculate jump table offset from event code
sub xbc, 0x1C00002
add xbc, xbc ; multiply by 2
add xbc, 0xE9F9B2 ; jump table base
ld bc, (xbc) ; load handler pointer
; ... dispatch to handler
AcPresentationBoxProc (0xF842B4) handles the visual frame rendering, responding to message types 0x1E0003C, 0x1E0003A, 0x1C0001B and calling display routines at 0xFA6266, 0xFA4409.
Demo Mode Handlers (Program ROM)
The Feature Demo uses a multi-mode event-driven architecture with separate handlers for different demo stages:
| Handler | Address | Event | Purpose |
|---|---|---|---|
DemoModeFunc |
demo_routines.s |
0x1C00013 | Main demo mode dispatcher (init vs. run) |
DemoMenuTtlFunc |
demo_routines.s |
— | Demo menu title (stub) |
DemoStyleTtlFunc |
demo_routines.s |
0x1C00007/0x1C00013 | Style selection with encoder/direction input |
DemoSoundTtlFunc |
demo_routines.s |
0x1C00007/0x1C00013 | Sound selection with encoder/direction input |
DemoRhyTtlFunc |
demo_routines.s |
0x1C00007/0x1C00013 | Rhythm selection with encoder/direction input |
DemoMode_Initialize |
0xF869E3 | — | First-time demo setup (voice save, audio init) |
DemoMode_Main_Operation |
0xF8696F | — | Main demo playback loop |
AcDemoSongBoxProc |
~line 194533 | — | Song selection display widget |
DemoSongSelFunc |
~line 194728 | — | Song selection handler |
AcDemoMedleyDispBoxProc |
~line 195115 | — | Medley display widget |
Each selection handler (Style/Sound/Rhythm) has a dispatch table and supports three input types:
- Direction (up/down): Posts event 0xE2 (style) or 0xE3 (sound/rhythm)
- Encoder (rotary): Posts event 0xE2 (rhythm) or 0xE1 (sound) via 0xF99490
- Enter: Calls shared handler at 0xF86A47
No Floppy Loading Path
Despite the sophisticated XML presentation infrastructure, the current firmware does NOT load SSF files from floppy disc:
-
SSF parser reads from hardcoded ROM addresses. The
AcPresentationControlProcand related handlers are wired to data in the Table Data ROM (0x800000+), not to a floppy loading pipeline. -
No floppy disc type for presentations. The disc type signature table has 8 entries covering firmware updates, custom data, and HDAE5000 — none for SSF or presentation files.
-
Floppy I/O handles only data formats. Beyond system updates, the floppy subsystem handles: MIDI songs (.MID), sequencer tracks (.SQT), styles (.STY), performance memories (.PMT), rhythm data (.RCM) — but NOT SSF presentations.
Bitmap Loading Chain
When AcPresentationControlProc processes a <SHOW OBJ="ftdemo01"> action, the firmware:
-
Looks up the object name (
ftdemo01) in the UI object table (NAKA_UIObjectTable, ~141 entries). The corresponding entry is aFTDEMO_SCREEN*structure. -
Extracts the filename from the structure — the last field is a
.longpointer to a null-terminated string like"FTBMP01"(stored asFTDEMO_BMP01_TECHNICS_GLOBE). -
Looks up the filename in the file entry index at
0x8CE01C(Table Data ROM). The index stores 6 entries of 24 bytes each:struct FeatureDemo_FileEntry { char filename[12]; // "FTBMP01.BMP" uint32 reserved; // 0 uint32 data_ptr; // ROM address (e.g. 0x880418) uint32 file_size; // in bytes }; -
Reads the BMP data directly from the Table Data ROM using the pointer.
-
Renders to VRAM (0x1A0000–0x1DFFFF, 256KB) via
VwUserBitmapByNameProc/DrawBitmapFile. The 8-bit BMP palette is loaded to the hardware palette register; pixel data is DMA-copied to the display.
The entire pipeline is ROM-resident — no disk I/O occurs for the Feature Demo. The "FTBMP01" filename in the FTDEMO_SCREEN structure is a logical key, not a filesystem path.
Button Sequence for Feature Demo
The correct button sequence to activate the Feature Demo from the home screen:
- Press DEMO button →
8D38transitions from0x01(normal) to0xE0(DEMONSTRATION menu appears) - Press LEFT 4 (4th from top, CPL_SEG9) →
8D38transitions to0xE4(FEATURE PRESENTATION sub-menu appears) - Press LEFT 2 (CPL_SEG10) →
8D38transitions to0xE1(demonstration/performances begin playing)
Demo Menu UI States
State (0x8D38) |
Description |
|---|---|
0x01 |
Normal operation (home screen) |
0xE0 |
DEMONSTRATION menu (top-level) |
0xE4 |
FEATURE PRESENTATION sub-menu |
0xE1 |
Demonstration/performances playing |
Two Distinct Activation Paths
The Feature Demo’s visual SSF presentation has TWO completely different activation paths. Understanding the distinction is critical to diagnosing why the automated demo fails in MAME.
Path 1 — Manual (Physical Button Press): WORKS
This is the path triggered by a real user pressing a physical panel button while in the Feature Presentation sub-menu:
Physical button press
→ UIState_KeyScan_Dispatch
→ event 0x1C00038
→ GroupBoxProc (UI container widget handler)
→ GroupBoxProc_StartSSFPresentation (0xF9A273)
→ allocates workspace with tag 0xB80A
→ event 0x1C0001C
→ AcPresentCtrl_CheckSSFStart (0xF84625)
→ tag == 0xB80A? YES
→ event 0x1C00006
→ SSF XML parser starts
→ FTBMP bitmaps rendered to VRAM
Key: GroupBoxProc_StartSSFPresentation builds the workspace bytes individually from stack-resident parameters, producing workspace[0]=0x0A, workspace[1]=0xB8, workspace[2..3]=0x00 — the type-tag 0x0000B80A. It then sends event 0x1C0001C via direct SendEvent (0xFA9660).
Path 2 — Automated (Demo Timer/Sequencer): FAILS
This is the path taken by the automated demo sequencer when cycling through demo items:
Demo timer/sequencer
→ DemoMenu_BuildItemWorkspace (0xF83CEA)
→ allocates workspace with tag 0x82xx (from table at 0xE9F88C)
→ event 0x1C0001C (via PostEventWithParam, queued)
→ AcPresentCtrl_CheckSSFStart (0xF84625)
→ tag == 0xB80A? NO (tag is 0x82xx)
→ event 0x1C00006 NEVER SENT
→ SSF parser never starts
→ FTBMP bitmaps never render
DemoMenu_BuildItemWorkspace reads a “part select” index R from DRAM 0x8D3A and computes workspace[0..3] = table[0xE9F88C + iz*2] + R*1024. The table holds 16-bit values in the 0x82xx-0x82CC range. This formula can never produce 0x0000B80A for any byte R, because the difference 0xB80A - 0x82xx is never divisible by 1024.
Why the Automated Path Cannot Trigger SSF
The workspace tag mismatch is architectural: the automated demo sequencer uses DemoMenu_BuildItemWorkspace which creates workspaces tagged for song/style selection (0x82xx), not for SSF presentation (0xB80A). The SSF-correct tag is only produced by GroupBoxProc_StartSSFPresentation, which is only reached via the manual button-press path through event 0x1C00038.
Additionally, the demo state machine variable at DRAM 0x0251D8 stays 0x0000 throughout the automated demo. This state machine is what should drive the visual SSF through its presentation sequence, but it never advances because the sequencer parts (DRAM[0x10420] = 0xFFFF, all 16 parts active) never complete without waveform ROMs.
SSF Gate State 0xE4
The gate table entry for state 0xE4 (FEATURE PRESENTATION sub-menu) contains the unconditional marker 0xFFFE. This means any key press in state 0xE4 should broadcast event 0x1C00038 — the gate is wide open. This confirms the manual path would work if physical button input were simulated.
Key Code Locations
| Function | ROM Address | Source File | Purpose |
|---|---|---|---|
GroupBoxProc_StartSSFPresentation |
0xF9A273 | presentation_sound_nav.s:33 |
Builds correct 0xB80A workspace, sends direct event |
AcPresentCtrl_CheckSSFStart |
0xF84625 | drawbar_panel_ui.s:15535 |
Checks workspace tag, gates SSF start |
DemoMenu_BuildItemWorkspace |
0xF83CEA | drawbar_panel_ui.s:14643 |
Builds 0x82xx workspace (wrong tag for SSF) |
| Workspace tag table | 0xE9F88C | — | 16-bit values in 0x82xx range |
MAME Emulation Status
Current status (March 2026): Partially working. The Demo Timer System (song cycling) works correctly in MAME — the timer counts down, songs cycle, and PlaySong returns after ~16 seconds of SwbtWr buffer processing. The SSF Visual Presentation (FTBMP bitmap rendering) does not trigger because the automated demo path uses workspace tags (0x82xx) that fail the 0xB80A check in AcPresentCtrl_CheckSSFStart.
Root cause summary:
- The automated demo sequencer (
DemoMenu_BuildItemWorkspace) creates workspaces with0x82xxtags - The SSF presentation requires workspace tag
0xB80A, which is only created by the manual button-press path (GroupBoxProc_StartSSFPresentation) demo_stateat DRAM0x0251D8stays0x0000— the visual state machine never advances- Sequencer parts never complete (
DRAM[0x10420] = 0xFFFF) without waveform ROMs
Two independent bugs remain:
- Song cycling stuck: Sequencer parts never complete without waveform ROMs, preventing the timer from resetting for the next song
- SSF never triggers in automated mode: The automated path fundamentally cannot produce the correct workspace tag; only the manual button-press path through
GroupBoxProc_StartSSFPresentationcreates tag0xB80A
Reference Lua script: /tmp/ftdemo_v3.lua — automated navigation from boot to Feature Demo activation.
Workspace Allocation — Concept Explained
Before describing the root cause, it helps to understand what “allocating a workspace” means in the KN5000 firmware’s event system.
The KN5000 event system (centered on SendEvent at 0xFA9660 and PostEventWithParam at 0xFA9D58) passes three registers as event parameters: XWA (target), XBC (event code), and XDE (data/parameter). For events that need to carry more than a single 32-bit value, the firmware uses a workspace pattern:
- A small block of memory (typically 12 bytes) is allocated from the firmware heap by calling
Malloc(the workspace allocator). The returned pointer is stored in DRAM (usually somewhere in0x000000–0x0FFFFF). - The workspace fields are populated: the first 4 bytes act as a type-tag (a magic 32-bit value identifying the kind of data the workspace carries), followed by payload fields.
- The workspace pointer is passed as XDE when calling
SendEventorPostEventWithParam. - The receiving handler reads the workspace via the pointer, checks the type-tag, and processes the payload.
Think of the workspace as a small heap-allocated struct passed by pointer via the event system.
Because workspaces are allocated from a pool in DRAM and may be reused, it is critical that the event fires and the workspace is consumed before the memory is overwritten by another allocation or unrelated code. This is why the two distinct event-dispatch paths (queued vs. direct) produce different results.
Investigation History: How the Solution Was Found
Two paths for event 0x1C0001C
Deep investigation (Feb 2026, Lua trace scripts + ROM disassembly analysis) identified that there are two completely different code paths that send event 0x1C0001C to AcPresentationControlProc:
Path 1 — DemoMenu_BuildItemWorkspace (0xF83CEA) — QUEUED, WRONG TAG
This function is called in a loop for each of the ~15 demo menu items (styles, sounds, rhythms). For each item it:
- Allocates a 12-byte workspace via the heap allocator (
FF0E80). - Reads the “part select” index
Rfrom DRAM address0x8D3AviaGetPartSelect(). - Computes
workspace[0..3] = table[0xE9F88C + iz*2] + R*1024. The table (at ROM0xE9F88C) holds 16-bit values all in the range0x82xx–0x82CC. This formula can never produce the value0x0000B80Afor any byteR, because the difference0xB80A − 0x82xxis never divisible by 1024. - Posts event
0x1C0001CviaPostEventWithParam(FA9D58) — a queued (deferred) dispatch.
When this queued event eventually reaches AcPresentationControlProc (0xF8450B), it is handled by AcPresentCtrl_CheckSSFStart (0xF84625), which checks *(XDE) == 0xB80A. The check always fails because the workspace type-tag is never 0xB80A.
Path 2 — GroupBoxProc_StartSSFPresentation (0xF9A273) — DIRECT, CORRECT TAG
GroupBoxProc (~0xF998xx) is a UI container widget handler. Its event dispatch table includes:
cp xde, 0x1C00038
jrl z, GroupBoxProc_StartSSFPresentation ; direct entry
cp xde, 0x1C00030
jrl z, GroupBoxProc_Ev1C00030 ; via show-item handler
GroupBoxProc_StartSSFPresentation (0xF9A273) builds the workspace bytes individually from stack-resident parameters, producing workspace[0]=0x0A, workspace[1]=0xB8, workspace[2..3]=0x00 — the type-tag 0x0000B80A. It then sends event 0x1C0001C via direct SendEvent (FA9660).
When this event reaches AcPresentCtrl_CheckSSFStart, the type-tag check passes, and the handler sends event 0x1C00006 — which starts SSF presentation parsing, loads the XML from ROM, and begins rendering FTBMP images.
Root cause summary
The Feature Demo has two distinct activation paths with fundamentally different workspace tag behavior (see “Two Distinct Activation Paths” above):
- Manual path (physical button press in state
0xE4):UIState_KeyScan_Dispatch→ event0x1C00038→GroupBoxProc_StartSSFPresentation→ workspace tag0xB80A→AcPresentCtrl_CheckSSFStartpasses → SSF starts - Automated path (demo timer/sequencer):
DemoMenu_BuildItemWorkspace→ workspace tag0x82xx→AcPresentCtrl_CheckSSFStartfails → SSF never starts
The automated path cannot produce the correct workspace tag by design. The 0x82xx tags from the table at 0xE9F88C serve a different purpose (song/style selection). Only GroupBoxProc_StartSSFPresentation constructs the 0xB80A tag needed for SSF, and it is only reachable through the manual button-press event chain.
Call-chain overview
Feature Demo activated
→ DemoModeFunc (0xF222CC) [event 0x1C00013, XDE=1]
→ DemoMode_Initialize (0xF869E3)
→ DemoMode_Main_Operation (0xF8696F)
→ DemoMenu_BuildItemWorkspace (0xF83CEA) [×15, for each menu item]
→ PostEventWithParam (0xFA9D58)
→ AcPresentCtrl_CheckSSFStart (0xF84625)
*(workspace) == 0xB80A? NO → SSF never starts
Expected (but missing in MAME):
GroupBoxProc receives event 0x1C00038
→ GroupBoxProc_StartSSFPresentation (0xF9A273)
→ SendEvent (0xFA9660) with workspace tag 0x0000B80A
→ AcPresentCtrl_CheckSSFStart
*(workspace) == 0xB80A? YES
→ sends 0x1C00006 → SSF parser starts → FTBMP images rendered
Why GroupBoxProc doesn’t receive 0x1C00038 in MAME
Confirmed root cause (March 2026, MAME Lua trace investigation):
UIState_KeyScan_Dispatch IS called during boot initialization — approximately 900+ times — but all calls occur when DRAM 0x8D38 = 0x00. This maps to ROM table entry[0] at 0xE014CE, which contains the sentinel array {0xFFFF} (immediately terminating). Every boot-time call returns immediately with no event sent.
The dispatcher mechanism consists of:
- FDB3D1 (event buffer writer): fills a circular buffer at DRAM
0xBD3Cwith 4-byte entries encoding chain index (C080) and param (C07D) - FDB328/FDB32E (main dispatcher loop): reads entries from
0xBD3C, selects handler chains from tableEE7CA7, calls ALL functions in the selected chain
During boot initialization, FDB3D1 sweeps all chain indices 0x00→0x9A with 24 C07D params each (~4,608 total events). UIState_KeyScan_Dispatch is invoked for every event whose chain (at EE7CA7[C080*4]) includes it. At boot time, 8D38=0x00 → table[0]={0xFFFF} → returns early every time.
After boot stabilizes (8D38 changes to 0x01), the event buffer at 0xBD3C drains and FDB3D1 is never called again — because no user input occurs in MAME’s automated test run. With an empty buffer, the dispatcher has nothing to process, so UIState_KeyScan_Dispatch is never invoked again.
Critically, the logic would work correctly with user input: Table entry[1] (at ROM 0xE014D0, used when 8D38=0x01) contains a 16-bit array with 0x7002 at index [79] (ROM address 0xE0156E). The comparison key is (C080 << 8) | C07D = (0x70 << 8) | 0x02 = 0x7002. So if UIState_KeyScan_Dispatch were called post-boot with chain 0x70 and param 0x02, it WOULD find a match and send event 0x1C00038.
The real hardware presumably triggers FDB3D1 via user button presses or directional encoder navigation, which posts events to the 0xBD3C buffer. In MAME’s automated Feature Demo test mode, no such input is simulated. The Feature Demo would work on real hardware if a user navigated to it with actual button input.
Previously investigated (and ruled out) blocking points
| Issue | Ruling |
|---|---|
| Audio initialization delay | Audio_WaitForReady has a 61,440-iteration timeout — exits gracefully even if SubCPU doesn’t respond; not a hard block |
| VRAM display mode | VRAM writes do occur; display mode itself is not the primary blocker |
| XML parser state | SSF parser never starts because 0x1C00006 is never sent |
DemoMode_Main_Operation loop |
Expected behaviour (jp Seq_StartMainControl); not a hang |
MAME floppy disk type bug (separate issue)
The MAME driver previously registered only a "35dd" (720 KB double-density) floppy connector in kn5000_floppies. The real hardware uses 1.44 MB HD (high-density) drives — confirmed by:
- FDC format configuration supporting 1440K (18 sectors/track, 80 tracks)
update_disc.imganalysis: FAT12, OEM-ID"Technics", 2880 sectors, 18 sectors/track, 2 heads
This has been fixed in the MAME driver. It is a separate issue from the Feature Demo image display failure.
How event 0x1C00038 is generated — detailed analysis
UIState_KeyScan_Dispatch — the event sender
The function UIState_KeyScan_Dispatch (ROM 0xF98697) is the code that sends event 0x1C00038. It is not called directly — instead, it appears as a function-pointer entry in many UI widget handler chains (in the ROM pointer tables at UIState_HandlerTable_WithProbe, UIState_HandlerTable_Standard, UIState_HandlerTable_Compact, etc.). When any widget using one of these handler chains processes certain events (typically user interaction events), it walks its handler chain and calls UIState_KeyScan_Dispatch.
UIState_KeyScan_Dispatch logic:
- Calls
0xEF0797to check bit 7 of DRAM0x0406. This flag is SET once during boot byBoot_DisplayScreen(call at line 89160) and CLEARED only during flash memory updates. It should be set throughout normal operation, so this check passes. - Reads a byte from DRAM
0x8D38(an array selector indexR). - Reads a ROM pointer
P = ROM[0xE01F80 + R×4]. The pointerPpoints to a ROM array of 16-bit state values ending in0xFFFF. - Walks the array at
P, comparing each entry to the current selection state built from DRAM bytes0xC07Dand0xC080(packed as(0xC080 << 8) | 0xC07D). - If a match is found (or if the first array entry is
0xFFFE, indicating “always send”), builds a 32-bit XDE parameter from0xC07D–0xC080and sends event0x1C00038toXWA=0xFFFFFFFFviaFA9945.
FA9945 — the broadcast event router
FA9945 is the intermediate event-routing function called by UIState_KeyScan_Dispatch. It checks a dynamic routing table in DRAM at 0x02BC34 (populated at runtime by FA9752). For each registered entry, it checks whether the entry’s event code matches 0x1C00038 and whether the entry’s “match value” equals the upper 16 bits of the XDE parameter. If a match is found, the event is forwarded to the registered handler (GroupBoxProc instance).
FA9752 — the event queue writer (PostEvent)
FA9752 (labeled PostEvent in the disassembly) inserts events into a circular event queue at DRAM 0x02BC34 (head/tail pointers at 0x02EC34/0x02EC36). Each 12-byte queue entry holds:
entry[0..3] = self pointer (XWA) — object ID posting the event
entry[4..7] = event code (XBC)
entry[8..11] = param (XDE)
FA9945 is the queue processor/router: it reads pending entries from 0x02BC34, and for each entry with event code 0x1C00038, checks whether the upper 16 bits of the parameter match a value stored in the entry. If the queue is empty, it posts the new event directly via FA9D58.
The event routing for 0x1C00038 therefore works through the standard event queue. UIState_KeyScan_Dispatch acts as the PRODUCER: it checks state bytes, then pushes 0x1C00038 + packed XDE into the queue via FA9945. The CONSUMER is whichever widget handler receives the queued event — presumably GroupBoxProc, once it has received its own event setup during initialization.
For GroupBoxProc to receive the queued 0x1C00038, it must either:
- Be the current “active” widget receiving events from the queue, OR
- Have registered via some dispatch mechanism that routes
0x1C00038to it specifically
Lua trace investigation findings (March 2026)
Investigation used MAME Lua autoboot scripts to monitor UIState_KeyScan_Dispatch, FA9945, and GroupBoxProc_StartSSFPresentation during MAME runs. Key findings:
MAME Lua API constraints discovered:
install_read_taprequires word-aligned ranges (even→odd address pair, e.g.,0xF98696-0xF98697)- Notifier handles from
add_machine_frame_notifier/add_machine_stop_notifiermust be saved to outer-scope variables to prevent garbage collection — otherwise callbacks silently stop - Critical conflict:
emu.add_machine_frame_notifier+install_read_tapare mutually exclusive — when both are active, read taps stop firing entirely
Early trace results (pre-solution):
| Monitor | Count | Observations |
|———|——-|————-|
| UIState_KeyScan_Dispatch tap (0xF98696-0xF98697) | 900+ calls | All during boot init; all with 8D38=0x00 (empty table); C080 sweeps 0x00→0x9A, C07D cycles 0x01..0x17 |
| FA9945 for XBC=0x1C00038 | 0 calls | Never fired — F98697 always returned early |
| GroupBoxProc_StartSSFPresentation | 0 real calls | One false positive (tap at F9A272 fires from 3rd word fetch of call 0xfa9660 instruction at F9A26F) |
| DRAM 0x8D38 at ~12s, ~25s, ~37s | 0x00, 0xEF, 0x01 | Transitions from boot init → stable post-boot |
False positive explained: The “SSF START” tap at 0xF9A272-0xF9A273 fires from the instruction call 0xfa9660 at 0xF9A26F (a 4-byte instruction encoding 1D 60 96 FA). Its 3rd word occupies 0xF9A272-0xF9A273, triggering the tap on every call to 0xFA9660 (the direct SendEvent dispatcher), not from GroupBoxProc_StartSSFPresentation actually executing.
ftdemo_v3.lua results (REVISED — originally misinterpreted):
| Monitor | Count | Observations |
|———|——-|————-|
| UIState_KeyScan_Dispatch | 994 calls | Called but EF0797 pre-condition blocked all (internal RAM 0x0406 bit 7 not set in emulation context) |
| FA9945 for 0x1C00038 | 0 calls | Event 0x1C00038 was never dispatched |
| GroupBoxProc_StartSSFPresentation tap | 9 hits | FALSE POSITIVE — tap at 0xF9A272-0xF9A273 fires from call 0xFA9660 encoding, not from actual SSF execution |
Correction (March 9): The original conclusion that “the Feature Demo works correctly” was wrong. The 9 “SSF START” hits were false positives caused by the ROM read tap overlapping with the instruction encoding of call 0xFA9660 at address 0xF9A26F. The “FTBMP01.BMP in VRAM” observation was likely from boot initialization, not from the demo.
Current status: Pressing DEMO in state 0xE4 transitions directly to 0x01 (normal mode), exiting the demo menu entirely. It does NOT trigger SSF. Pressing LEFT 2 starts the demo timer (song cycling) but does NOT trigger SSF visual presentation. The demo_state at DRAM 0x0251D8 remains 0x0000 throughout — the visual state machine never advances because sequencer parts never complete (DRAM[0x10420] = 0xFFFF without waveform ROMs).
Two independent bugs remain:
- Song cycling stuck: Sequencer parts (
DRAM[0x10420]=0xFFFF) never complete without waveform ROMs, preventing the timer from resetting for the next song - SSF never triggers in automated mode: The automated demo path through
DemoMenu_BuildItemWorkspacecreates workspace tags in the0x82xxrange (from table at0xE9F88C), which always fail the0xB80Acheck inAcPresentCtrl_CheckSSFStart. Only the manual button-press path throughGroupBoxProc_StartSSFPresentation(0xF9A273, inpresentation_sound_nav.s:33) can produce the correct tag
SSF Presentation Gate Table (SSF_PresentationGateTable, 0xE01F80)
The firmware uses a 256-entry ROM lookup table at 0xE01F80 to control which UI states allow the Feature Demo to be triggered. This table is the gating mechanism that determines whether pressing a panel button sends event 0x1C00038 — the event that ultimately starts the SSF presentation.
Structure
The table occupies 1024 bytes (0xE01F80–0xE02380) and consists of 256 .long (32-bit little-endian) pointer entries. Each pointer points to a state-value array — a list of 16-bit values terminated by 0xFFFF — stored elsewhere in the ROM (0xE014CE–0xE01F7E).
SSF_PresentationGateTable:
.long SSF_GateStates_Mode00 ; entry[0x00] — boot/init state
.long SSF_GateStates_Mode01 ; entry[0x01] — normal operation
.long SSF_GateStates_Mode02 ; entry[0x02]
... ; (256 entries total)
.long SSF_GateStates_ModeFF ; entry[0xFF]
How It Works
- Index selection: The current UI mode is stored as a byte at DRAM address
0x8D38. The getter function at0xF0618Floads this value. Common values include:0x00— boot/initialization0x01— normal operation0xE0— DEMONSTRATION menu0xE4— FEATURE PRESENTATION sub-menu
-
Table lookup:
GroupBoxNotify_SendSSFEvent(0xF98697) reads the byteRfrom0x8D38and loads the pointerP = SSF_PresentationGateTable[R]. - State matching: The 16-bit array at
Pis checked against the current panel selection state (packed from DRAM bytes0xC07Dand0xC080as(C080 << 8) | C07D):{0xFFFE}— always match: the event fires unconditionally{0xFFFF}— never match: the entry is disabled, no event sent{val1, val2, ..., 0xFFFF}— conditional: each value is compared to the packed state; if any matches, the event fires
- Event dispatch: When a match is found,
GroupBoxNotify_SendSSFEventsends event0x1C00038via the broadcast router at0xFA9945, which forwards it to registered widgets (typicallyGroupBoxProc).
Role in Feature Demo Activation
The gate table ensures the Feature Demo can only be activated from specific UI states. During boot (0x8D38 = 0x00), entry[0] points to {0xFFFF} — immediately terminating, blocking any SSF events. After boot stabilizes and the user navigates to the DEMONSTRATION menu, 0x8D38 transitions to values whose table entries contain valid state arrays.
However, as documented in the MAME investigation above, the actual Feature Demo activation in practice bypasses this table entirely: pressing DEMO in the FEATURE PRESENTATION sub-menu (state 0xE4) triggers GroupBoxProc_StartSSFPresentation directly through the soft-button event path, not through GroupBoxNotify_SendSSFEvent.
The gate table’s primary role appears to be gating hardware button events — when the event buffer dispatcher (0xFDB328) processes button presses from the key scanner, it invokes GroupBoxNotify_SendSSFEvent as part of widget handler chains. The table then determines whether those hardware button events should propagate as SSF trigger events based on the current UI mode.
State-Value Arrays
Each gate table entry points to a state-value array — a sequence of 16-bit little-endian values terminated by 0xFFFF. Each value encodes (chain_index << 8) | param, matching the packed panel selection state from DRAM bytes 0xC080 (chain index) and 0xC07D (param).
Known arrays in ROM range 0xE014CE–0xE01F7E:
| Label | Address | Entries | Content | Used by mode |
|---|---|---|---|---|
SSF_GateStates_Mode00 |
0xE014CE | 0 | {0xFFFF} — disabled |
0x00 (boot/init) |
SSF_GateStates_Mode01 |
0xE014D0 | ~215 | Chains 0x00–0x0C, 0x91 — many states | 0x01 (normal) |
SSF_GateStates_Mode02 |
0xE0157E | 0 | {0xFFFF} — disabled |
0x02 |
SSF_GateStates_Mode03 |
0xE01580 | ~215 | Chains 0x00–0x0C, 0x43, 0x48, 0x70, 0x80, 0x91, 0x98 | 0x03 |
SSF_GateStates_Mode04 |
0xE0174A | 1 | Chain 0x91, param 0x00 | 0x04 |
SSF_GateStates_Mode05 |
0xE0174E | 14 | Chain 0x92, params 0x00–0x0D | 0x05 |
SSF_GateStates_Mode06 |
0xE0176C | — | Binary data (incbin) | 0x06+ |
The SSF_GateStates_Mode05 array (0xE0174E) is a representative example: when the UI is in mode 5 (0x8D38 = 0x05), the SSF event fires only if the current panel state has chain index 0x92 and a param value between 0x00 and 0x0D. All other panel states are blocked.
Source Location
- Table definition:
maincpu/kn5000_v10_program.s, labelSSF_PresentationGateTable(0xE01F80) - Getter function:
SeMenu_LoadRawAddr— loads index from DRAM 0x8D38 - Consumer function:
GroupBoxNotify_SendSSFEvent(0xF98697) — reads and evaluates the table - State-value arrays: ROM range 0xE014CE–0xE01F7E (pointed to by table entries)
- Example array:
SSF_GateStates_Mode05(0xE0174E) — 14-entry filter for UI mode 5
Interpretation
The XML presentation system — with its EXEC tag, SONG playback, IMG display, rich tag vocabulary, and event-driven architecture — was almost certainly designed to be more flexible than its single current usage (a hardcoded Feature Demo in ROM).
The most likely explanation: Technics designed a general-purpose presentation engine, perhaps intended for:
- Dealer demonstration discs — scripted presentations showcasing the keyboard’s features at retail stores
- Educational materials — step-by-step tutorials loaded from floppy
- Trade show content — automated demonstrations for NAMM or music trade events
The infrastructure was built (XML parser, event system, tag vocabulary, EXEC command support), but the floppy loading path was either never completed or was removed before release. What shipped was a single ROM-resident Feature Demo using a fraction of the system’s capabilities.
The user’s memory of “feature presentation discs” may refer to this planned-but-unshipped feature, or to the commercially-sold style/song demo discs that showcased the keyboard’s capabilities using standard MIDI file playback.
Demo Timer State Machine
The Feature Demo uses a timer-driven state machine to sequence through demo items. The timer variable is at DRAM address 3375 (0xD2F).
Timer-Driven Entry Point
Demo_SelectEntry_TimerTick (0xF86D45) is called from the main loop timer. Each tick:
- Checks control panel state (
Demo_SelectEntry_CheckCPanel) - Decrements the countdown at address 3375
- At count=10 (
0x0A): loads the next song pattern and starts playback - At count=3: calls
Demo_SelectEntry_StartPlaybackto begin accompaniment - At count=1: updates display state
Song Processing Flow
Demo_SelectEntry_ProcessSongList (0xF86D86) manages song selection:
- Checks if song list at address 10420 is empty — if so, goes to countdown
- Checks bit 3 of address 10413 for auto-play vs manual mode
- In auto-play: compares current song index (10404) with target (4439)
- Calls
Demo_WaitForDisplayBit— busy-wait loop checking bit 2 of address 1056 (display ready flag) with 0xFFFFFF timeout - Calls
Banner_Loop_Check— sends note-off commands (status 0xD3) to all 16 channels, cleaning up active notes - Increments song index at address 4440 and loops
Display Wait
Demo_WaitForDisplayBit (0xF86F2C) is a timeout-protected busy-wait:
Loop 0xFFFFFF times:
If bit 2 of DRAM[0x420] == 0: return (display ready)
Return after timeout
This ensures display operations complete before the next slide loads. The timeout prevents a hard hang if the display never becomes ready.
Demo Timer Behavior in MAME
When the timer reaches 10, Demo_SelectEntry_PlaySong is called. This function calls SwbtWr_ReinitBothBanks, which runs the SwbtWr dispatch loop inline — processing 450+ buffered tone generator events. Each callback takes ~35ms, causing a ~16 second blocking period where the main loop is paused.
After SwbtWr processing completes (~960 frames), PlaySong returns (sets DRAM[0x8F4E] from 0x04 to 0x06), and the timer resumes counting down from 10. The tone generator hold timer (2 seconds per voice) then controls the pacing of subsequent song transitions.
Note: The 16-second initial delay may differ from real hardware where DSP writes are faster (direct hardware registers vs. HLE emulation). See research log for detailed investigation.
MAME Bug Found (March 9, 2026): The sequencer tick counters never increment because INTTR5 never fires. Two bugs in tmp94c241.cpp’s timer_16bits lambda: (1) TREG_HIGH match sets wrong interrupt flag (0x08/INTTR4 instead of 0x80/INTTR5), (2) T4FFCR bits incorrectly gate the entire match instead of just the flip-flop. Fix implemented, not yet tested. See timer bug research log.
Key DRAM Addresses
| Address | Description |
|---|---|
| 1056 (0x420) | Display status flags (bit 2 = display busy) |
| 3375 (0xD2F) | Demo timer countdown value |
| 3379 (0xD33) | Debounce counter |
| 4439 (0x1157) | Target song index |
| 4440 (0x1158) | Current song index |
| 10404 (0x28A4) | Active demo entry index |
| 10413 (0x28AD) | Demo control flags (bit 3 = auto-play) |
| 10420 (0x28B4) | Song list pointer |
| 36148 (0x8D34) | UI state byte |
| 36152 (0x8D38) | UI sub-state byte (0xE4 = Feature Presentation) |
| 36686 (0x8F4E) | Playback state flag |
Related Pages
- UI Framework — Widget system and event handling
- System Update Discs — Floppy disc format and update process
- Storage Subsystem — Storage architecture overview
- FDC Subsystem — Floppy disk controller handlers
- Image Gallery — Extracted graphics including FTBMP images
Last updated: March 14, 2026