Resident driver template — joystick + keypad
Updated 7 Sep 2026
← All Voltmace Delta 14B Driver versions
This disassembly
Install the resident joystick driverPoint BYTEV at the OSBYTE intercept at &0A0D. Called from the BASIC via CALL &A00, after it has saved the previous BYTEV (line 50) and patched the chain slot (line 1840) and sensitivity thresholds (line 1850). Preserves A (saved and restored); X and Y are untouched.
|
||||||||
| 0A00 | .install | |||||||
| PHA ; Preserve A | ||||||||
| 0A01 | LDA #&0d ; BYTEV low byte -> &0D... | |||||||
| 0A03 | STA bytev ; store it | |||||||
| 0A06 | LDA #&0a ; ...high byte -> &0A... | |||||||
| 0A08 | STA bytev_hi ; ...so every OSBYTE now enters the handler at &0A0D | |||||||
| 0A0B | PLA ; Restore A | |||||||
| 0A0C | RTS ; Return to the BASIC caller | |||||||
BYTEV handler: map the joystick and keypad to INKEYAs variant A, but joystick_map entries >= &10 test the Delta 14B keypad matrix through the User VIA instead of an analogue channel. |
|
| 0A0D | .osbyte_intercept |
| CMP #osbyte_inkey ; Only intercept OSBYTE &81 (INKEY / read key) | |
| 0A0F | BNE chain ; other reason codes -> chain to the previous handler |
| 0A11 | PHA ; Preserve A (reason code)... |
| 0A12 | TYA ; Preserve Y (INKEY high byte)... |
| 0A13 | PHA ; on the stack |
| 0A14 | TXA ; A := X = the INKEY key number being tested |
| 0A15 | PHA ; ...(X also preserved) |
| 0A16 | LDY #0 ; No match yet... |
| 0A18 | STY result_flag ; ...clear the result flag |
| 0A1B | .scan_map←1← 0A27 BNE |
| CMP joystick_map,y ; Does the tested key match this map entry? | |
| 0A1E | BNE scan_next ; no -> skip it |
| 0A20 | JSR read_input ; yes -> read the mapped input for it |
| 0A23 | .scan_next←1← 0A1E BNE |
| INY ; Step over the 2-byte entry... | |
| 0A24 | INY ; twice |
| 0A25 | CPY #&48 ; end of the map? |
| 0A27 | BNE scan_map ; no -> keep scanning |
| 0A29 | LDA result_flag ; Did any mapped input read as active? |
| 0A2C | BEQ not_pressed ; no -> return the normal INKEY result |
| 0A2E | TAX ; yes: return "key pressed" (X=Y=&FF)... |
| 0A2F | TAY ; X and Y |
| 0A30 | PLA ; drop the saved X... |
| 0A31 | PLA ; ...Y... |
| 0A32 | PLA ; ...A |
| 0A33 | RTS ; and return, claiming the OSBYTE |
| 0A34 | .not_pressed←1← 0A2C BEQ |
| PLA ; Restore X... | |
| 0A35 | TAX ; into X |
| 0A36 | PLA ; Restore Y... |
| 0A37 | TAY ; into Y |
| 0A38 | PLA ; Restore A |
| 0A39 | .chain←1← 0A0F BNE |
| JMP chain_to_old_bytev ; Pass the call to the previous BYTEV (chain slot below) | |
Test one joystick or keypad inputFor the matched entry: entries below &10 read an analogue channel (OSBYTE &80 / ADVAL) against the thresholds; entries >= &10 strobe a keypad column through User VIA port B and test a row bit. A hit sets result_flag. Preserves A and Y (Y is the caller's map-scan index) but corrupts X.
|
||||||||
| 0A3C | .read_input←1← 0A20 JSR | |||||||
| PHA ; Preserve A... | ||||||||
| 0A3D | TYA ; Preserve Y... | |||||||
| 0A3E | PHA ; on the stack | |||||||
| 0A3F | CMP #&10 ; Analogue entry (< &10) or keypad entry? | |||||||
| 0A41 | BPL test_keypad ; >= &10 -> the keypad path | |||||||
| 0A43 | INY ; Point at the entry parameter | |||||||
| 0A44 | LDA joystick_map,y ; ADC channel in the top nibble... | |||||||
| 0A47 | LSR ; ...shift it down... | |||||||
| 0A48 | LSR ; ...shift it down... | |||||||
| 0A49 | LSR ; ...shift it down... | |||||||
| 0A4A | LSR ; ...shift it down... | |||||||
| 0A4B | TAX ; ...into X for OSBYTE &80 | |||||||
| 0A4C | LDA joystick_map,y ; reload the parameter | |||||||
| 0A4F | AND #1 ; low bit picks which threshold (push vs pull) | |||||||
| 0A51 | BEQ test_high_threshold ; else test the high threshold | |||||||
| 0A53 | LDA #osbyte_read_adc_or_get_buffer_status ; OSBYTE &80: read ADC channel X (ADVAL, Y=high byte)... | |||||||
| 0A55 | JSR osbyte ; call OSBYTE Read ADC channel X or buffer status | |||||||
| 0A58 | CPY threshold_lo ; past the low threshold? | |||||||
| 0A5B | BCC read_done ; no -> not active | |||||||
| 0A5D | BCS active ; yes -> active | |||||||
| 0A5F | .test_high_threshold←1← 0A51 BEQ | |||||||
| LDA #osbyte_read_adc_or_get_buffer_status ; OSBYTE &80: read ADC channel X... | ||||||||
| 0A61 | JSR osbyte ; call OSBYTE Read ADC channel X or buffer status | |||||||
| 0A64 | CPY threshold_hi ; past the high threshold? | |||||||
| 0A67 | BCC active ; no -> active | |||||||
| 0A69 | BCS read_done ; yes -> not active | |||||||
| 0A6B | .test_keypad←1← 0A41 BPL | |||||||
| INY ; Point at the entry parameter | ||||||||
| 0A6C | LDA joystick_map,y ; top nibble = the column strobe... | |||||||
| 0A6F | AND #&f0 ; mask the top nibble | |||||||
| 0A71 | STA col_strobe ; ...saved | |||||||
| 0A74 | LDA joystick_map,y ; low nibble = the row bit mask... | |||||||
| 0A77 | AND #&0f ; mask the low nibble | |||||||
| 0A79 | LDY #&f0 ; DDRB = &F0: strobes out, rows in... | |||||||
| 0A7B | STY user_via_ddrb ; set it | |||||||
| 0A7E | STA row_mask ; save the row mask | |||||||
| 0A81 | LDY col_strobe ; Drive the column strobe... | |||||||
| 0A84 | STY user_via_orb_irb ; ...onto port B | |||||||
| 0A87 | LDY user_via_orb_irb ; Read the rows back... | |||||||
| 0A8A | TYA ; into A | |||||||
| 0A8B | AND row_mask ; ...and mask this row bit | |||||||
| 0A8E | BEQ active ; low (pressed) -> not-active path... | |||||||
| 0A90 | BNE read_done ; high -> active | |||||||
| 0A92 | .active←3← 0A5D BCS← 0A67 BCC← 0A8E BEQ | |||||||
| LDA #&ff ; Flag a hit... | ||||||||
| 0A94 | STA result_flag ; ...in result_flag | |||||||
| 0A97 | .read_done←3← 0A5B BCC← 0A69 BCS← 0A90 BNE | |||||||
| PLA ; Restore Y... | ||||||||
| 0A98 | TAY ; into Y | |||||||
| 0A99 | PLA ; Restore A | |||||||
| 0A9A | RTS ; Done | |||||||
| 0A9B | .row_mask←2← 0A7E STA← 0A8B AND | |||||||
| EQUB &EA ; Scratch: keypad row bit mask (&EA at rest) | ||||||||
| 0A9C | .col_strobe←2← 0A71 STA← 0A81 LDY | |||||||
| EQUB &EA ; Scratch: keypad column strobe (&EA at rest) | ||||||||
| 0A9D | .result_flag←3← 0A18 STY← 0A29 LDA← 0A94 STA | |||||||
| EQUB &EA ; Result byte (&EA at rest): the handler zeroes it, then sets it to &FF if a mapped input reads active | ||||||||
| ; Unused | ||||||||
| 0A9E | .unused_b | |||||||
| EQUB &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00 | ||||||||
| ; 2-byte entries <INKEY key, input descriptor>. The key byte is ; 0 here; the BASIC (PROCASSEM, lines 1780-1830) pokes each with ; the user's chosen INKEY value. The fixed descriptor decodes ; as: | ||||||||
| 0AB0 | .joystick_map←5Used as index base by← 0A1B CMP← 0A44 LDA← 0A4C LDA← 0A6C LDA← 0A74 LDA | |||||||
| EQUB &00, ; joystick axis: ADC channel 1, high threshold &10 | ||||||||
| 0AB2 | EQUB &00, &11 ; joystick axis: ADC channel 1, low threshold | |||||||
| 0AB4 | EQUB &00, ; joystick axis: ADC channel 2, high threshold &20 | |||||||
| 0AB6 | EQUB &00, &21 ; joystick axis: ADC channel 2, low threshold | |||||||
| 0AB8 | EQUB &00, ; joystick axis: ADC channel 3, high threshold &30 | |||||||
| 0ABA | EQUB &00, &31 ; joystick axis: ADC channel 3, low threshold | |||||||
| 0ABC | EQUB &00, ; joystick axis: ADC channel 4, high threshold &40 | |||||||
| 0ABE | EQUB &00, &41 ; joystick axis: ADC channel 4, low threshold | |||||||
| 0AC0 | EQUB &00, &51 ; keypad: strobe column &50, test row bit &1 | |||||||
| 0AC2 | EQUB &00, &51 ; keypad: strobe column &50, test row bit &1 | |||||||
| 0AC4 | EQUB &00, &51 ; keypad: strobe column &50, test row bit &1 | |||||||
| 0AC6 | EQUB &00, &31 ; keypad: strobe column &30, test row bit &1 | |||||||
| 0AC8 | EQUB &00, &61 ; keypad: strobe column &60, test row bit &1 | |||||||
| 0ACA | EQUB &00, &32 ; keypad: strobe column &30, test row bit &2 | |||||||
| 0ACC | EQUB &00, &52 ; keypad: strobe column &50, test row bit &2 | |||||||
| 0ACE | EQUB &00, &62 ; keypad: strobe column &60, test row bit &2 | |||||||
| 0AD0 | EQUB &00, &34 ; keypad: strobe column &30, test row bit &4 | |||||||
| 0AD2 | EQUB &00, &54 ; keypad: strobe column &50, test row bit &4 | |||||||
| 0AD4 | EQUB &00, &64 ; keypad: strobe column &60, test row bit &4 | |||||||
| 0AD6 | EQUB &00, &38 ; keypad: strobe column &30, test row bit &8 | |||||||
| 0AD8 | EQUB &00, &58 ; keypad: strobe column &50, test row bit &8 | |||||||
| 0ADA | EQUB &00, &68 ; keypad: strobe column &60, test row bit &8 | |||||||
| 0ADC | EQUB &00, &D1 ; keypad: strobe column &D0, test row bit &1 | |||||||
| 0ADE | EQUB &00, &D1 ; keypad: strobe column &D0, test row bit &1 | |||||||
| 0AE0 | EQUB &00, &D1 ; keypad: strobe column &D0, test row bit &1 | |||||||
| 0AE2 | EQUB &00, &B1 ; keypad: strobe column &B0, test row bit &1 | |||||||
| 0AE4 | EQUB &00, &E1 ; keypad: strobe column &E0, test row bit &1 | |||||||
| 0AE6 | EQUB &00, &B2 ; keypad: strobe column &B0, test row bit &2 | |||||||
| 0AE8 | EQUB &00, &D2 ; keypad: strobe column &D0, test row bit &2 | |||||||
| 0AEA | EQUB &00, &E2 ; keypad: strobe column &E0, test row bit &2 | |||||||
| 0AEC | EQUB &00, &B4 ; keypad: strobe column &B0, test row bit &4 | |||||||
| 0AEE | EQUB &00, &D4 ; keypad: strobe column &D0, test row bit &4 | |||||||
| 0AF0 | EQUB &00, &E4 ; keypad: strobe column &E0, test row bit &4 | |||||||
| 0AF2 | EQUB &00, &B8 ; keypad: strobe column &B0, test row bit &8 | |||||||
| 0AF4 | EQUB &00, &D8 ; keypad: strobe column &D0, test row bit &8 | |||||||
| 0AF6 | EQUB &00, &E8 ; keypad: strobe column &E0, test row bit &8 | |||||||
| 0AF8 | EQUB &00, &00 ; end-of-map marker | |||||||
| ; RTS placeholder; the BASIC (line 1840) patches this to JMP ; <previous BYTEV> so non-INKEY OSBYTEs are chained. | ||||||||
| 0AFA | .chain_to_old_bytev←1← 0A39 JMP | |||||||
| EQUB &60, &00, &00 | ||||||||
| 0AFD | .threshold_lo←1← 0A58 CPY | |||||||
| EQUB &00 ; Low-threshold comparison value; BASIC line 1850 pokes SH% here | ||||||||
| 0AFE | .threshold_hi←1← 0A64 CPY | |||||||
| EQUB &00 ; High-threshold comparison value; BASIC line 1850 pokes SL% here | ||||||||
| 0AFF | .unused_b_end | |||||||
| EQUB &00 ; Unused | ||||||||
