Keybed Scanning
Keybed Scanning
The KN5000’s 61-key velocity-sensitive keyboard connects directly to the tone generator IC303 (TC183C230002), which performs hardware key scanning internally. The Sub CPU reads completed note events from IC303’s keyboard output interface — the CPU does not scan the keyboard matrix itself.
Status: Note encoding and voice slot management fully reverse-engineered from SubCPU firmware. HLE keybed device implemented in MAME driver.
Correction (2026). The disassembly has since renamed the two routines this page describes and corrected their behaviour:
ToneGen_Read_Voice_DataisKeybed_Read_Event(same address, 0x03D0C5) andToneGen_Calc_PitchisKeybed_Decode_Event(same address, 0x03D11F) — the old names were wrong, because neither routine touches tone-generator voice state. More importantly, the high byte at 0x110000 is not a linear velocity. It is a raw touch/key-travel-time reading that indexes a lookup-table curve; a small raw value means a hard, loud strike. The sections below have been corrected to describe the real curve instead of a direct pass-through.
Architecture
The note flow is bidirectional — keybed events travel through the Sub CPU to the Main CPU (for display and MIDI output), then note-on commands travel back from the Main CPU through the Sub CPU to IC303 (for sound generation):
┌──────────────────────────────────────────────────────────────────┐
│ NOTE EVENT FLOW │
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ KEYBED │───>│ IC303 │───>│ SUB CPU │───>│ MAIN CPU │ │
│ │ (keys) │ │ ToneGen │ │ firmware │ │ firmware │ │
│ └─────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ 0x110000 DMA latch Display, │
│ (HW scan) @ 0x120000 MIDI out │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ IC303 │<───│ SUB CPU │<───│ MAIN CPU │ │
│ │ ToneGen │ │ firmware │ │ firmware │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ 0x100000 DMA latch Sound select, │
│ (voice cfg) @ 0x120000 note routing │
└──────────────────────────────────────────────────────────────────┘
Forward path (keybed to display):
- Physical key press detected by IC303 hardware scanner
- IC303 presents the key index and a raw touch reading at 0x110000, sets status bit at 0x110002
Keybed_Read_Event(formerly mis-namedToneGen_Read_Voice_Data) reads the event, callsKeybed_Decode_Eventto curve-map the touch reading into a MIDI velocity, and allocates a voice slotInterCPU_DMA_Sendtransmits[0x90, note, velocity]to Main CPU via latch- Main CPU updates display (key indicators, voice allocation) and emits MIDI
Return path (sound generation):
- Main CPU sends note-on command via latch to Sub CPU
- Sub CPU
Voice_NoteOnconfigures IC303 voice parameters at 0x100000 - IC303 begins waveform playback from ROMs IC304-IC307
Hardware Interface
| Address | Width | Direction | Purpose |
|---|---|---|---|
| 0x110002 | 16-bit | Read | Status register |
| 0x110000 | 16-bit | Read | Keybed event data (key index + raw touch reading) |
Status Register (0x110002)
| Bit | Name | Description |
|---|---|---|
| 0 | DATA_READY | 1 = an event is queued; 0 = FIFO empty, data port left untouched |
| 1 | RELEASE_QUALIFIER | 1 = treat this event the same as a 0xFF touch byte (release path), even though the touch byte itself may not be 0xFF |
| 15:2 | — | Never tested by either firmware copy |
Data Register (0x110000)
| Bits | Name | Description |
|---|---|---|
| 6:0 | KEY_INDEX | Key index |
| 7 | KEY_STATE | SET = key DOWN, CLEAR = key UP (not a “has velocity” or release flag) |
| 15:8 | RAW_TOUCH | Raw touch/key-travel-time reading — not a velocity. It indexes a lookup-table curve (Keybed_Decode_Event) that produces the actual MIDI velocity; a small value here means a hard strike. 0xFF means “no touch value for this event” and routes to the release/note-off arm. |
Note Encoding
Raw Note to MIDI Note
Keybed_Decode_Event (at 0x03D11F, formerly mis-named ToneGen_Calc_Pitch) adds a fixed offset of 0x24 (36) to the low 7 bits of the key byte to get the MIDI note:
MIDI_note = (key_byte & 0x7F) + 0x24
| Raw Note | MIDI Note | Name | Octave |
|---|---|---|---|
| 0 | 36 | C2 | 2 |
| 12 | 48 | C3 | 3 |
| 24 | 60 | C4 (Middle C) | 4 |
| 36 | 72 | C5 | 5 |
| 48 | 84 | C6 | 6 |
| 60 | 96 | C7 | 7 |
The KN5000 has 61 keys: C2 (raw 0) through C7 (raw 60).
Touch-to-Velocity Curve
Correction: earlier revisions of this page described the high byte as a linear velocity
carried straight through to the DMA packet. It is not — Keybed_Decode_Event runs it through
a touch-sensitivity curve (the same curve family as the sub-CPU boot ROM’s
NOTE_VELOCITY_LOOKUP_CALCULATE, see Sub-CPU Boot ROM):
raw = high byte of the 0x110000 word ; 0xFF = "no touch value" (release)
x = ToneGen_Velocity_Input_Curve[raw] ; 256-byte LUT @ 0x01F43E, MONOTONICALLY DECREASING
y = (x - pivot) * gain / divisor + pivot_out ; pivot/divisor @ 0x01F418/0x01F41A; gain/pivot_out
; from the 3-byte-per-curve table @ 0x01F420
y -= black_key_trim if note % 12 in {1,3,6,8,10} ; the five black keys (table @ 0x01F422)
clamp y to 0..255
velocity = ToneGen_Velocity_Output_Curve[y] ; 256-byte LUT @ 0x01F53E, MONOTONICALLY INCREASING, max 0x7F (127)
curve (the touch-sensitivity mode, 0-9) is the byte at RAM 0x4A48, initialised to 6
by ToneGen_Init and changeable only by Audio_CmdHandler_A0_BF. Curve 0 has zero gain: with
touch sensitivity off, every note lands at a fixed level (MIDI velocity 80). The input curve
is monotonically decreasing — a small raw touch reading means a hard, loud strike
(consistent with the raw value being a key-travel time, not a force). The black-key trim is
a real mechanical compensation: the five black keys (note % 12 in {1, 3, 6, 8, 10}) sit higher
and travel further, so the same physical force reads as a smaller raw value and needs an
explicit subtraction to land at the same delivered velocity as a white key.
Note-On / Note-Off Event Format
data_word (0x110000) = (raw_touch << 8) | (key_index | key_state_bit7)
status (0x110002): bit 0 = 1 (data ready), bit 1 = release qualifier
- Bit 7 of the low byte is the key state: SET = key DOWN, CLEAR = key UP.
- A raw touch byte of
0xFF, or status bit 1 set, routes the event to the release/note-off arm regardless of the key-state bit;Keybed_Decode_Eventstill runs (for the note number) but the delivered velocity is forced to 0.
Voice Slot Table
The Sub CPU maintains a 16-entry voice slot table at RAM address 0x4A4C:
| Address | Size | Description |
|---|---|---|
| 0x4A4A | 2 bytes | DMA enable flag (non-zero = DMA relay active) |
| 0x4A4C | 16 bytes | Voice slot states (one byte per slot) |
Each slot byte:
- 0xFF = note active (slot in use)
- 0x00 = slot available
When a note-on event arrives, Keybed_Read_Event scans the 16 slots for an available one. When a note-off arrives, the corresponding slot is freed.
DMA Packet Format
After processing a note event, the Sub CPU relays it to the Main CPU via the inter-CPU latch using InterCPU_DMA_Send:
Note-On Packet
Byte 0: 0x90 (MIDI Note On status)
Byte 1: MIDI note number ((key_index & 0x7F) + 0x24)
Byte 2: velocity — the curve-mapped output of Keybed_Decode_Event (see Touch-to-Velocity
Curve above), not the raw touch byte; the output curve's range is 0x01-0x7F (1-127)
Note-Off Packet
Byte 0: 0x90 (MIDI Note On status — velocity 0 = note off per MIDI convention)
Byte 1: MIDI note number ((key_index & 0x7F) + 0x24)
Byte 2: 0x00 (zero velocity = note off)
Key Firmware Routines
| Routine | Address | Description |
|---|---|---|
ToneGen_Init |
0x03D016 | Set tone generator mode to 6, begin polling |
ToneGen_Process_Notes |
0x03D01E | Main loop: read and process all pending events |
Keybed_Read_Event |
0x03D0C5 | Read one event from 0x110000, manage voice slots (formerly mis-named ToneGen_Read_Voice_Data — it does not touch tone-generator voice state) |
Keybed_Decode_Event |
0x03D11F | Convert raw key byte + touch reading to MIDI note and curve-mapped velocity (formerly mis-named ToneGen_Calc_Pitch) |
ToneGen_Poll_Init |
0x03D1FB | Initial polling: read 16 slots with delay loops (ToneGen_Poll_Delay, the inner busy-wait sub-block, is at 0x03D227) |
ToneGen_Config_Init |
0x02DFCF | Configure all 64 IC303 voices and global registers |
InterCPU_DMA_Send |
(varies) | Send 3-byte note packet to Main CPU via latch |
MAME HLE Implementation
Since IC303 is a custom ASIC with no public documentation, the MAME driver uses an HLE (High-Level Emulation) device to inject keybed events:
- 6 input ports (KEY0-KEY5) map PC keyboard keys to 61 piano notes
- A 1ms scan timer compares current key states against previous states
- Key press/release transitions generate events in the IC303 output format
- Events are queued and served when the SubCPU reads 0x110000/0x110002
The HLE produces no sound (IC303 voice parameter writes at 0x100000 are discarded), but the full note event pipeline works end-to-end.
PC Keyboard Mapping
The keybed notes are available as MAME input entries (visible in the Tab menu under “Input (This Machine)”) but have no default PC keyboard assignments because all candidate keys conflict with control panel button mappings.
To play notes, use MAME’s input configuration UI (Tab menu) to assign keys. A suggested piano layout:
Lower octave (Z row — assign to KEY2 / C4-B4):
| Suggested Key | Note | MAME Input Name |
|---|---|---|
| Z | C4 | C4 |
| S | C#4 | C#4 |
| X | D4 | D4 |
| D | D#4 | D#4 |
| C | E4 | E4 |
| V | F4 | F4 |
| G | F#4 | F#4 |
| B | G4 | G4 |
| H | G#4 | G#4 |
| N | A4 | A4 |
| J | A#4 | A#4 |
| M | B4 | B4 |
Upper octave (Q row — assign to KEY3 / C5-B5):
| Suggested Key | Note | MAME Input Name |
|---|---|---|
| Q | C5 | C5 |
| 2 | C#5 | C#5 |
| W | D5 | D5 |
| 3 | D#5 | D#5 |
| E | E5 | E5 |
| R | F5 | F5 |
| 5 | F#5 | F#5 |
| T | G5 | G5 |
| 6 | G#5 | G#5 |
| Y | A5 | A5 |
| 7 | A#5 | A#5 |
| U | B5 | B5 |
Note: Assigning these keys will also trigger control panel buttons that share the same keys. This is a known limitation — the control panel and keybed share the same physical keyboard namespace.
Fixed velocity of 100 for all key presses (PC keyboards have no velocity sensitivity).
Related Pages
- Tone Generator — IC303 register map and voice configuration
- Audio Subsystem — Overall audio architecture and DSP details
- Inter-CPU Protocol — Latch communication for note relay
- Hardware Architecture — Physical keyboard and PCB layout