KN7000 Event & Dispatch System

The KN7000’s user interface is event-driven, built on the same object-oriented “MILK” toolkit as the KN5000. Screens are trees of objects (windows, boxes, switches, list boxes…); the firmware delivers events to an object’s window procedure (…Proc), which updates state and redraws. This page documents the KN7000 implementation as recovered from the firmware itself — the event-code table and the dispatch functions are both decoded directly from kn7000_program.rom, and the named functions below are converted to readable, re-assemblable source in the disassembly project.

Event codes

The firmware embeds a 60-entry name table (program file 0x326BF0) enumerating every event type. The event code is the table index, so EV_NONE = 0, EV_SHOW = 1, and so on. These are identical in spirit to the KN5000’s event codes — the shared framework — with KN7000-specific additions (SD card, wallpaper preview, DSP sound names).

Code Name Code Name
0x00 EV_NONE 0x1E EV_AUTOINC
0x01 EV_SHOW 0x1F EV_SWIN_AIC
0x02 EV_HIDE 0x20 EV_RETURN_TITLE
0x03 EV_MOVE 0x21 EV_IAMSELECTED
0x04 EV_ACTION 0x22 EV_YOUARESELECTED
0x05 EV_SWIN 0x23 EV_SENDSWIN
0x06 EV_SWON 0x24 EV_CHANGEDIALFOCUS
0x07 EV_SWOFF 0x25 EV_TRSWPART
0x08 EV_ALLPAINT 0x26 EV_TRSWCOMMAND
0x09 EV_PAINT 0x27 EV_PARTSELECT
0x0A EV_RESET 0x28 EV_SWBOTH
0x0B EV_ACTIVATE 0x29 EV_INDEXSW_BOTH
0x0C EV_CHANGE_MODE 0x2A EV_SENDSWON
0x0D EV_CHANGE_TITLE 0x2B EV_SENDSWOFF
0x0E EV_INTERRUPT_TITLE 0x2C EV_SENDSWBOTH
0x0F EV_INDEXSW_UP 0x2D EV_PAGEINIT
0x10 EV_INDEXSW_DOWN 0x2E EV_UPDATESCREEN
0x11 EV_INDEXSW_UP_AIC 0x2F EV_DELIVERYEVENT
0x12 EV_INDEXSW_DOWN_AIC 0x30 EV_ASSSWB
0x13 EV_INDEXSELECT 0x31 EV_NEW_TITLE
0x14 EV_MMMDATA 0x32 EV_OLD_TITLE
0x15 EV_RAMDATA 0x33 EV_MEMDUMP
0x16 EV_PAGECHANGE 0x34 EV_TRANSDRAW
0x17 EV_DIAL 0x35 EV_INDEXSW_ON
0x18 EV_SOUNDNAME 0x36 EV_INDEXSW_OFF
0x19 EV_RHYTHMNAME 0x37 EV_SOUNDDSPNAME
0x1A EV_PMEMNAME 0x38 EV_LARGECHANGE
0x1B EV_SOUNDSWNO 0x39 EV_WALLPREVIEW
0x1C EV_BITDATA 0x3A EV_WAITDRAW
0x1D EV_MEMODRAW 0x3B EV_SWIN_MODE

The families are recognisable: lifecycle (EV_SHOW/EV_HIDE/EV_RESET), drawing (EV_PAINT/EV_ALLPAINT/EV_UPDATESCREEN), panel input (EV_INDEXSW_*, EV_DIAL, EV_SWON/EV_SWOFF), focus/selection (EV_IAMSELECTED, EV_CHANGEDIALFOCUS, EV_PARTSELECT), and data-changed notifications (EV_SOUNDNAME, EV_RHYTHMNAME, EV_MMMDATA, …).

The object table

Live UI objects are kept in a fixed table in work RAM at 0x5000757C, with 0x38 (56)-byte slots indexed by object id. Three functions compute a slot address the same way (base + id*0x38), which is how the geometry is known; GetCurrentTarget is the clearest:

GetCurrentTarget:                 # CPU 0x4842943B
    call    GetCurrentObjectId    # d0 <- id of the active object (see below)
    mov     0x38, d1
    mul     d1, d0                # id * 0x38
    add     0x5000757c, d0        # + table base  => &slot[id]
    mov     d0, a0
    movhu   (0x10, a0), d0        # slot + 0x10 = the object's current target
    ret

So offset +0x10 within a slot holds the object’s current target reference. InitializeEventQueue (0x484284B4) walks the same table to reset a slot. Other framework accessors (e.g. SetVisible, SetChange) instead reach their object through a linked-view pointer via the hot helper GetLinkView and test flag bits in it.

Two tasks: main and AP

The KN7000 runs two cooperative UI tasks — a main task and an AP (application) task — matching the SleepMainTask/SleepApTask / WakeUpMainTask/WakeUpApTask pair. The helper that every event call uses to find “the current object” selects between the two:

GetCurrentObjectId:               # CPU 0x48414A4F  (func_48414A4F)
    mov     (0x50380004), a2      # current-task handle
    mov     (0x5038002c), a1      # main-task handle
    cmp     a1, a2
    bne     .ap                   # not the main task -> AP path
    movhu   (0x500d3c60), d0      # main task's focused-object id
    mov     d0, (a0)
    bra     .done
.ap:
    movhu   (0x500d3c5c), d0      # AP task's focused-object id
    mov     d0, (a0)
.done:
    clr     d0
    retf    [a2], 4

i.e. the focused object id is kept per task (0x500D3C60 for main, 0x500D3C5C for AP), and is chosen by comparing the running task handle (0x50380004) against the main-task handle (0x5038002C).

Dispatch API

The public entry points, all recovered by name and converted to source:

Function CPU addr Role
InitializeEventQueue 0x484284B4 reset an object’s slot in the table
DispatchEvent 0x4842936F deliver the current object’s pending event now (no args)
SendEvent 0x48429388 deliver an event synchronously, with a param word
PostEvent 0x484293AD enqueue an event for later delivery
GetEvent 0x484293D2 fetch an event (obj, a0, a1) for processing
GetCurrentTarget 0x4842943B the active object’s current target (slot +0x10)

Most begin by resolving the current object via GetCurrentObjectId, then hand off to a lower-level queue/dispatch helper (func_48428…) that walks the object tree and invokes the target’s window procedure. (DispatchEvent is argument-less precisely because it acts on the current object rather than a passed-in one — the detail that finally disambiguated the reflection-table alignment, see below.) The window procedure itself is looked up through the toolkit’s MT_GetProcedure mechanism — the same reflection tables that let us recover 518 *Proc handlers by name (see the firmware page). A handler is therefore a function that switches on the event code and acts:

// shape of a MILK window procedure (Ac*/Vw*/Iv*/Ps*/Tt*Proc)
long SomeWidgetProc(object *self, int event, long param) {
    switch (event) {
        case EV_SHOW:   ... ; break;
        case EV_PAINT:  ... ; break;
        case EV_ACTION: ... ; break;   // the switch was pressed / activated
        ...
    }
}

A note on the recovered names

The names above are recovered by pairing each reflection table’s code-pointer array with its parallel name-pointer array. Getting the alignment right matters: a one-slot shift silently labels every function with a neighbour’s name. The alignment was pinned down behaviourally — under the correct pairing every Get* accessor reads its variable and every Set* writes it, with no exceptions, whereas the shifted pairing produces impossible “getters” that write memory. That check is what confirms, for instance, that the argument-less 0x4842936F is DispatchEvent (acting on the current object) rather than a similarly argument-less InitializeEventQueue.

Relationship to the KN5000

The event names, codes and dispatch shape are shared with the KN5000 — both firmwares are re-targets of one MILK-based source tree (see the Shared Codebase Map). What differs is machine- specific: the KN7000 adds SD-card, wallpaper and DSP-sound events, runs on the MN10300 rather than the TLCS-900, and keeps its object table at a different RAM address. The KN5000 Event Codes and UI Framework pages remain the best conceptual companions to this one.