Resident driver — joystick + keypad
Updated 2 Sep 2026
← All Voltmace Delta 14B Driver versions
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). |
|
| 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 #&81 ; 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. |
|
| 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 #&80 ; OSBYTE &80: read ADC channel X (ADVAL, Y=high byte)... |
| 0A55 | JSR osbyte ; call OSBYTE |
| 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 #&80 ; OSBYTE &80: read ADC channel X... | |
| 0A61 | JSR osbyte ; call OSBYTE |
| 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 ; ...onto port B |
| 0A87 | LDY user_via_orb ; 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 sensitivity threshold (BASIC line 1850: SL%) | |
| 0AFE | .threshold_hi←1← 0A64 CPY |
| EQUB &00 ; High sensitivity threshold (BASIC line 1850: SH%) | |
| 0AFF | .unused_b_end |
| EQUB &00 ; Unused | |
