MIDI Serial I/O
External MIDI Serial I/O
The KN5000 provides external MIDI communication through Serial Channel 0 (SC0) of the TMP94C241F Main CPU. The rear panel features three 5-pin DIN connectors (MIDI IN, MIDI OUT, MIDI THRU) and a COM SELECT switch that configures the serial port for different communication modes.
Hardware Configuration
Serial Channel 0 Registers
| Register | Address | Name | Purpose |
|---|---|---|---|
| SC0BUF | 0xD0 |
Serial Buffer | TX/RX data register |
| SC0CR | 0xD1 |
Control Register | Serial control/status |
| SC0MOD | 0xD2 |
Mode Register | Async mode, parity, stop bits |
| BR0CR | 0xD3 |
Baud Rate Control | Clock divider for baud rate |
| INTES0 | 0xEA |
Interrupt Enable | TX/RX interrupt priority |
Initialization (SC0Init_EnableRegisters)
SC0MOD (0xD2) = 0x29 ; 8N1, async mode
SC0CR (0xD1) = 0x00 ; Clear errors/status
BR0CR (0xD3) = 0x08 ; Standard: 31,250 baud (MIDI standard)
INTES0 (0xEA) = 0x5D ; Enable TX/RX interrupts, priority 5
SC0BUF (0xD0) = 0xFE ; Prime TX with Active Sense byte
The baud rate generator uses fc/4/N where fc = 16 MHz and N = BR0CR value. Standard mode: 16MHz / 4 / 8 = 500 kHz internal clock, yielding the MIDI-standard 31,250 baud rate. An alternate variant (hardware revision dependent) uses BR0CR=6 for a 666.6 kHz clock.
COM SELECT Switch
The KN5000 rear panel has a rotary switch read from Port G bits 7-4 (register 0x68, shifted right by 4). The switch selects between four communication modes:
| Switch Position | Value | Mode | Protocol |
|---|---|---|---|
| MIDI | 0x00 |
Standard MIDI | 31,250 baud, 8N1 |
| MAC | 0x01 |
Apple Macintosh serial | (different baud/protocol) |
| PC1 | 0x02 |
PC serial mode 1 | (different baud/protocol) |
| PC2 | 0x03 |
PC serial mode 2 | (different baud/protocol) |
READ_COM_SELECT_SWITCH (0xFCF8F6) reads the switch value via a 16-entry lookup table that handles debouncing (invalid switch states default to MIDI mode). The result is stored at DRAM address 47072.
The TX dispatch (MIDI_SC0_TX_DISPATCH) checks this value: 0x00 uses the MIDI-specific TX path (MIDI_SC0_ENABLE_TX), all other values use an alternate serial handler.
Baud Rate Tables
Two sets of timing constants are computed during initialization based on hardware revision:
| Entry | Standard Mode | Alternate Mode | Purpose |
|---|---|---|---|
| DRAM 47060 | 31,250 | 23,437 | Base clock rate |
| DRAM 47062 | 10,416 | 7,812 | MIDI clock timing |
| DRAM 47064 | 4,166 | 3,125 | Fine timing |
| DRAM 47066 | 1,000 | 750 | Sub-timing |
| DRAM 47068 | 8 | 6 | BR0CR register value |
MIDI Receive Path
Interrupt Handler (INTRX0_HANDLER at 0xFCF1F0)
When a byte arrives on SC0, the hardware triggers the RX interrupt:
-
Error check: Read SC0CR (0xD1) bits 4-2 (framing/parity/overrun errors). If any error bit is set, jump to
INTRX0_CLEAR_ERROR_STATE(clear error flags, increment error counter at DRAM 47070, return from interrupt). -
Full context save: Push all 7 extended registers (XWA, XBC, XDE, XHL, XIX, XIY, XIZ) to stack.
-
Read byte: Read SC0BUF (0xD0) — the received byte.
-
Dispatch: Call
MidiSeq_ReceiveAndForwardwith the received byte as parameter. This routes toMIDI_RX_BYTE_DISPATCHER. -
Context restore and return from interrupt.
Byte Dispatcher (MIDI_RX_BYTE_DISPATCHER)
The dispatcher classifies each incoming byte:
Received byte
|
v
Bit 7 set? ──no──> MIDI_CHANNEL_MESSAGE_DISPATCHER (data byte)
|yes
v
Is it <= 0xF7? ──no──> MIDI_SYSTEM_MESSAGE_HANDLER (real-time)
|yes
v
Store as running status (DRAM 1059)
|
v
SysEx in progress? ──yes──> Handle SysEx end (0xF7) or error
|no
v
Return (wait for data bytes)
Register Context Save/Restore
The MIDI RX handler maintains a full saved register context at DRAM 1080-1104. This allows MIDI processing to be interrupted and resumed without losing state:
| DRAM Address | Register | Size |
|---|---|---|
| 1080 | XWA | 4 bytes |
| 1084 | XBC | 4 bytes |
| 1088 | XDE | 4 bytes |
| 1092 | XHL | 4 bytes |
| 1096 | XIX | 4 bytes |
| 1100 | XIY | 4 bytes |
| 1104 | XIZ | 4 bytes |
MIDI_RX_CONTEXT_RESTORE loads all 7 registers at entry; MIDI_RX_CONTEXT_SAVE stores them on exit. This design allows the MIDI parser to maintain multi-byte message parsing state across interrupts.
Channel Message Routing
MIDI_CHANNEL_MESSAGE_DISPATCHER (0xFCF746) routes incoming data bytes based on the running status byte:
-
Extract the status type:
(running_status >> 4) & 0x7→ index into 8-entry jump table atMIDI_CHANNEL_HANDLER_JUMP_TABLE(0xFCF761). -
Dispatch to the appropriate handler.
Handler Jump Table
| Status | Type | Handler |
|---|---|---|
| 0x80 | Note Off | Default handler |
| 0x90 | Note On | Channel message handler |
| 0xA0 | Poly Aftertouch | Default handler |
| 0xB0 | Control Change | Channel message handler |
| 0xC0 | Program Change | Channel message handler |
| 0xD0 | Channel Pressure | Channel message handler |
| 0xE0 | Pitch Bend | Default handler |
| 0xF0 | System | System Exclusive handler |
Channel Message Queueing
Channel messages are queued to the sequencer ring buffer at 0x1F37B via MIDI_QUEUE_EVENT_TO_SEQUENCER:
- Checks buffer capacity (minimum 3 entries free)
- Writes status byte, then data byte(s)
- If buffer overflows: sets overflow flag (DRAM 1063 bit 2), increments error counter (DRAM 47069)
For Note On with velocity 0 in a full buffer, the event is silently dropped (effectively converting it to a Note Off that’s already implied by the zero velocity).
SysEx Message Handling
MIDI_SYSTEM_EXCLUSIVE_HANDLER (0xFCF7A4) handles system messages:
| Status | Message | Action |
|---|---|---|
0xF0 |
SysEx Start | Set SysEx-in-progress flag; check manufacturer ID |
0xF2 |
Song Position | Set running status, capture LSB |
0xF3 |
Song Select | Queue to sequencer |
SysEx manufacturer ID filtering: After 0xF0, the next byte is checked against three accepted manufacturer IDs:
0x50(Technics/Panasonic)0x41(Roland)0x7E(Universal Non-Realtime)
If matched: set SysEx capture flag (DRAM 1074 bit 1), write both bytes to SeqBuf2. Subsequent data bytes are accumulated until 0xF7 terminates the message. Unrecognized manufacturer IDs cause the SysEx to be silently ignored.
MIDI Transmit Path
Interrupt Handler (INTTX0_HANDLER at 0xFCF15B)
The TX interrupt fires when SC0BUF is ready for the next byte:
- Check flag byte (DRAM 1065) for pending system messages:
- Bit 0: Send
0xF8(MIDI Clock) — then clear flag - Bit 1: Send
0xFA(Start) — then clear flag - Bit 2: Send
0xFB(Continue) — then clear flag - Bit 3: Send
0xFC(Stop) — then clear flag - Bit 4: Send
0xFE(Active Sense) — then clear flag
- Bit 0: Send
-
Dequeue next byte from the TX queue via
SeqAlt1. If the queue is empty, disable TX interrupt (INTES0 = 0xFD). - Write to SC0BUF (register 0xD0) to transmit the byte.
TX Enable (MIDI_SC0_ENABLE_TX)
Called when new data is available for transmission:
- If MIDI is active (DRAM 1140 == 0x55): reinitialize
SeqAlt1queue and clear flags. - Otherwise: set TX interrupt enable (INTES0 = 0xDD) to trigger the TX interrupt handler.
System Real-Time Messages
MIDI_SYSTEM_MESSAGE_HANDLER handles incoming real-time messages:
| Byte | Message | Action |
|---|---|---|
0xF8 |
Timing Clock | Process MIDI clock tick |
0xFA |
Start | Reset playback position, arm sync |
0xFB |
Continue | Resume from current position |
0xFC |
Stop | Halt playback, save position |
0xFE |
Active Sense | Set alive flag (DRAM 1063 bit 7) |
0xFF |
System Reset | Ignored (in MIDI mode) |
MIDI Clock Synchronization
The KN5000 supports full external MIDI clock sync with three independent clock sources:
Source 1 (Primary Clock):
- DRAM 1047: Sub-beat counter (increments by 4 per clock tick)
- DRAM 1048: Beat counter (increments when sub-beat reaches 96)
- DRAM 1056: Source state flags
Source 2 (Secondary/Error-Corrected):
- DRAM 1045/1046: Sub-beat and beat counters
- DRAM 1111: Previous sub-beat (for delta computation)
- DRAM 1120-1124: Error accumulator (phase-locked loop correction)
- DRAM 1076/1077: Coarse counters with overflow detection
Source 3 (Alternate Sync):
- DRAM 1051/1052: Sub-beat and beat counters
- DRAM 1057: Source state flags
- DRAM 1071/1072: Sync threshold comparison values
All sources use 96 PPQN (pulses per quarter note) resolution, matching standard MIDI clock resolution (24 PPQN with 4x subdivision).
The error correction in Source 2 computes a running delta between clock ticks and applies a correction factor (DRAM 1124 accumulates the error, wrapping at 96), effectively implementing a software PLL that smooths out jitter in the incoming MIDI clock.
MIDI Transport Events
When external Start/Stop/Continue messages change the transport state, the firmware queues events to the internal sequencer:
| Event | Code | Trigger |
|---|---|---|
0x85 |
Start/Reset | External Start (0xFA) received |
0x86 |
Stop | External Stop (0xFC) received |
Events are queued via MIDI_QUEUE_EVENT_PAIR which writes both the event code and the current sub-beat position to the sequencer FIFO (ring buffer at 0x1E753, capacity checked against 2 entries minimum).
High-Level Processing (MidiSerial_ProcessInput)
Above the interrupt-driven byte-level I/O, MidiSerial_ProcessInput (0xFCFA39) runs in the main task loop:
- Check if MIDI processing is enabled (DRAM 64848 bit 4).
- Compare read/write pointers of the ring buffer at
0x1F37B. - For each buffered message:
- Extract the status type:
(status >> 4) & 0x7 - Look up handler address from
MidiSerial_CmdJumpTable(16-entry table at0xFCFA5D) - Call the handler
- Extract the status type:
- After processing all messages: call
MidiStream_LoadAllPresets.
Command Jump Table
The 16-entry jump table at MidiSerial_CmdJumpTable dispatches to:
| Index | Handler | Purpose |
|---|---|---|
| 0-1 | MidiSerial_HandleDefault_Data |
Default (pass-through) |
| 2 | MidiSerial_HandleSysReset_Data |
System Reset handling |
| 3 | MidiSerial_HandleSysCommon_Data |
System Common (Song Position, etc.) |
| 4-15 | MidiSerial_HandleDefault_Data |
Default (pass-through) |
MIDI THRU
The MIDI THRU port is a hardware echo of the MIDI IN signal. It does not pass through software processing — the electrical signal from MIDI IN is directly routed to MIDI THRU via optocoupler output.
DRAM State Variables
Key MIDI state variables maintained in DRAM:
| Address | Size | Purpose |
|---|---|---|
| 1059 | 1 | Running status byte |
| 1063 | 1 | Status flags (bit 2: overflow, bit 6: SysEx active, bit 7: Active Sense) |
| 1065 | 1 | TX pending flags (bits 0-4: F8/FA/FB/FC/FE) |
| 1066 | 1 | Clock tempo accumulator |
| 1074 | 1 | SysEx state (bit 0: in progress, bit 1: capturing, bit 5: error) |
| 1080-1104 | 28 | RX context save area (7 registers x 4 bytes) |
| 1140 | 1 | MIDI active flag (0x55 = active) |
| 32523 | 1 | External sync mode |
| 47060-47068 | 10 | Baud rate configuration table |
| 47069 | 1 | Queue overflow counter |
| 47070 | 1 | Serial error counter |
| 47071 | 1 | Last received byte |
| 47072 | 1 | COM SELECT switch value (0=MIDI, 1=MAC, 2=PC1, 3=PC2) |
| 47074 | 1 | Transport flags |
| 64848 | 1 | MIDI control flags (bit 2: ext clock enable, bit 4: MIDI disable) |
Code References
| Symbol | Address | Purpose |
|---|---|---|
SC0Init_Entry |
0xFCF890 |
Serial port initialization |
SC0Init_EnableRegisters |
0xFCF924 |
Configure SC0 registers |
READ_COM_SELECT_SWITCH |
0xFCF8F6 |
Read rear panel switch |
INTRX0_HANDLER |
0xFCF1F0 |
MIDI RX interrupt handler |
INTTX0_HANDLER |
0xFCF15B |
MIDI TX interrupt handler |
INTRX0_CLEAR_ERROR_STATE |
0xFCF139 |
RX error recovery |
MIDI_RX_BYTE_DISPATCHER |
0xFCF22E |
Classify and route RX bytes |
MIDI_CHANNEL_MESSAGE_DISPATCHER |
0xFCF746 |
Route channel messages |
MIDI_SYSTEM_MESSAGE_HANDLER |
0xFCF2A9 |
Process real-time messages |
MIDI_SYSTEM_EXCLUSIVE_HANDLER |
0xFCF7A4 |
SysEx start/end handling |
MIDI_RX_CONTEXT_SAVE |
0xFCF610 |
Save 7 registers to DRAM |
MIDI_RX_CONTEXT_RESTORE |
0xFCF5F8 |
Restore 7 registers from DRAM |
MIDI_SC0_TX_DISPATCH |
0xFCF940 |
TX path dispatch (MIDI vs serial) |
MIDI_SC0_ENABLE_TX |
0xFCF960 |
Enable TX interrupt |
MidiSerial_ProcessInput |
0xFCFA39 |
Main-loop MIDI processing |
MidiSerial_WaitForData |
0xFCFA6C |
Wait for RX buffer data |
MIDI_QUEUE_EVENT_TO_SEQUENCER |
0xFCF780 |
Queue MIDI to sequencer buffer |
MIDI_START_PLAYBACK_REQUEST |
0xFCF62F |
External Start handler |
MIDI_RESET_PLAYBACK_STATE |
0xFCF64D |
Reset all clock counters |
MIDI_QUEUE_TRACK_EVENT |
0xFCF697 |
Queue beat event to FIFO |
MIDI_QUEUE_EVENT_PAIR |
0xFCF6C5 |
Queue event + position pair |
References
- MIDI Subsystem — Internal MIDI processing (Sub CPU)
- SysEx Messages — System Exclusive message formats
- Audio Subsystem — Sound generation
- Sequencer — MIDI recording and playback
- CPU Subsystem — TMP94C241F serial channel details
Last updated: March 2026