Resident driver — joystick only
Updated 2 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). |
|
| 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 to INKEYRuns on every OSBYTE; only OSBYTE &81 (INKEY / read key) is intercepted. If the key being tested matches an entry in joystick_map, the matching analogue input is read and, when active, a "key pressed" result is returned; every other call chains to the previous BYTEV. |
|
| 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← 0A2B BNE |
| CMP joystick_map,y ; Does the tested key match this map entry? | |
| 0A1E | BNE scan_next ; no -> skip it |
| 0A20 | JSR read_joystick ; yes -> read the joystick input for it |
| 0A23 | .scan_next←1← 0A1E BNE |
| INY ; Step over the 2-byte entry... | |
| 0A24 | INY ; twice |
| 0A25 | CPY #8 ; first (button) block done? |
| 0A27 | BEQ skip_to_directions ; yes -> jump to the direction block |
| 0A29 | .scan_more←1← 0A49 BCC |
| CPY #&18 ; End of the map? | |
| 0A2B | BNE scan_map ; no -> keep scanning |
| 0A2D | LDA result_flag ; Did any mapped input read as active? |
| 0A30 | BEQ not_pressed ; no -> return the normal INKEY result |
| 0A32 | TAX ; yes: return "key pressed" (X=Y=&FF)... |
| 0A33 | TAY ; X and Y |
| 0A34 | PLA ; drop the saved X... |
| 0A35 | PLA ; ...Y... |
| 0A36 | PLA ; ...A |
| 0A37 | RTS ; and return, claiming the OSBYTE |
| 0A38 | .not_pressed←1← 0A30 BEQ |
| PLA ; Restore X... | |
| 0A39 | TAX ; into X |
| 0A3A | PLA ; Restore Y... |
| 0A3B | TAY ; into Y |
| 0A3C | PLA ; Restore A |
| 0A3D | .chain←1← 0A0F BNE |
| JMP chain_to_old_bytev ; Pass the call to the previous BYTEV (chain slot below) | |
| 0A40 | .skip_to_directions←1← 0A27 BEQ |
| INY ; skip the 8-byte button block | |
| 0A41 | INY ; skip the 8-byte button block |
| 0A42 | INY ; skip the 8-byte button block |
| 0A43 | INY ; skip the 8-byte button block |
| 0A44 | INY ; skip the 8-byte button block |
| 0A45 | INY ; skip the 8-byte button block |
| 0A46 | INY ; skip the 8-byte button block |
| 0A47 | INY ; skip the 8-byte button block |
| 0A48 | CLC ; clear carry for the branch |
| 0A49 | BCC scan_more ; resume scanning the direction entries |
| fall through ↓ | |
Test one analogue inputFor the matched entry, read its analogue channel with OSBYTE &80 (ADVAL) and compare the value against the sensitivity thresholds (a direction) or AND the fire-button bits (a button); flag a hit in result_flag. |
|
| 0A4B | .read_joystick←1← 0A20 JSR |
| PHA ; Preserve A... | |
| 0A4C | TYA ; Preserve Y... |
| 0A4D | PHA ; on the stack |
| 0A4E | INY ; Point at the entry parameter |
| 0A4F | LDA joystick_map,y ; top nibble = ADC channel... |
| 0A52 | AND #&f0 ; mask the top nibble |
| 0A54 | LSR ; ...shift it down |
| 0A55 | LSR ; ...shift it down |
| 0A56 | LSR ; ...shift it down |
| 0A57 | LSR ; ...shift it down |
| 0A58 | TAX ; ...into X for OSBYTE &80 |
| 0A59 | LDA joystick_map,y ; reload the parameter |
| 0A5C | CPY #&10 ; direction entry (>= &10) or a fire button? |
| 0A5E | BCS test_button ; >= &10 -> the fire-button path |
| 0A60 | AND #1 ; low bit picks which threshold (push vs pull) |
| 0A62 | BEQ test_high_threshold ; else test the high threshold |
| 0A64 | LDA #&80 ; OSBYTE &80: read ADC channel X (ADVAL, Y=high byte)... |
| 0A66 | JSR osbyte ; call OSBYTE |
| 0A69 | CPY threshold_lo ; past the low threshold? |
| 0A6C | BCC read_done ; no -> not active |
| 0A6E | BCS active ; yes -> active |
| 0A70 | .test_high_threshold←1← 0A62 BEQ |
| LDA #&80 ; OSBYTE &80: read ADC channel X... | |
| 0A72 | JSR osbyte ; call OSBYTE |
| 0A75 | CPY threshold_hi ; past the high threshold? |
| 0A78 | BCC active ; no -> active |
| 0A7A | BCS read_done ; yes -> not active |
| 0A7C | .test_button←1← 0A5E BCS |
| AND #3 ; fire-button mask (low 2 bits)... | |
| 0A7E | STA button_mask ; ...saved |
| 0A81 | LDA #&80 ; OSBYTE &80: read ADC channel X... |
| 0A83 | JSR osbyte ; call OSBYTE |
| 0A86 | TXA ; the returned button bits... |
| 0A87 | AND button_mask ; ...AND the mask |
| 0A8A | BEQ read_done ; none set -> not active |
| 0A8C | BNE active ; set -> active |
| 0A8E | .active←3← 0A6E BCS← 0A78 BCC← 0A8C BNE |
| LDA #&ff ; Flag a hit... | |
| 0A90 | STA result_flag ; ...in result_flag |
| 0A93 | .read_done←3← 0A6C BCC← 0A7A BCS← 0A8A BEQ |
| PLA ; Restore Y... | |
| 0A94 | TAY ; store it |
| 0A95 | PLA ; Restore A |
| 0A96 | RTS ; Done |
| 0A97 | .button_mask←2← 0A7E STA← 0A87 AND |
| EQUB &EA ; Scratch: fire-button mask (&EA at rest) | |
| 0A98 | .result_flag←3← 0A18 STY← 0A2D LDA← 0A90 STA |
| EQUB &EA ; Result byte (&EA at rest): the handler zeroes it, then sets it to &FF if a mapped input reads active | |
| ; Unused | |
| 0A99 | .unused_a |
| EQUB &00, &00, &00, &00, &00, &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←3Used as index base by← 0A1B CMP← 0A4F LDA← 0A59 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, &01 ; fire button: ADVAL(0), bit mask &1 |
| 0AC2 | EQUB &00, &01 ; fire button: ADVAL(0), bit mask &1 |
| 0AC4 | EQUB &00, &02 ; fire button: ADVAL(0), bit mask &2 |
| 0AC6 | EQUB &00, &02 ; fire button: ADVAL(0), bit mask &2 |
| 0AC8 | EQUB &00, ; keypad cell &61 (inert in this analogue-only &61 ; variant) |
| 0ACA | EQUB &00, ; keypad cell &32 (inert in this analogue-only &32 ; variant) |
| 0ACC | EQUB &00, ; keypad cell &52 (inert in this analogue-only &52 ; variant) |
| 0ACE | EQUB &00, ; keypad cell &62 (inert in this analogue-only &62 ; variant) |
| 0AD0 | EQUB &00, ; keypad cell &34 (inert in this analogue-only &34 ; variant) |
| 0AD2 | EQUB &00, ; keypad cell &54 (inert in this analogue-only &54 ; variant) |
| 0AD4 | EQUB &00, ; keypad cell &64 (inert in this analogue-only &64 ; variant) |
| 0AD6 | EQUB &00, ; keypad cell &38 (inert in this analogue-only &38 ; variant) |
| 0AD8 | EQUB &00, ; keypad cell &58 (inert in this analogue-only &58 ; variant) |
| 0ADA | EQUB &00, ; keypad cell &68 (inert in this analogue-only &68 ; variant) |
| 0ADC | EQUB &00, ; keypad cell &D1 (inert in this analogue-only &D1 ; variant) |
| 0ADE | EQUB &00, ; keypad cell &D1 (inert in this analogue-only &D1 ; variant) |
| 0AE0 | EQUB &00, ; keypad cell &D1 (inert in this analogue-only &D1 ; variant) |
| 0AE2 | EQUB &00, ; keypad cell &B1 (inert in this analogue-only &B1 ; variant) |
| 0AE4 | EQUB &00, ; keypad cell &E1 (inert in this analogue-only &E1 ; variant) |
| 0AE6 | EQUB &00, ; keypad cell &B2 (inert in this analogue-only &B2 ; variant) |
| 0AE8 | EQUB &00, ; keypad cell &D2 (inert in this analogue-only &D2 ; variant) |
| 0AEA | EQUB &00, ; keypad cell &E2 (inert in this analogue-only &E2 ; variant) |
| 0AEC | EQUB &00, ; keypad cell &B4 (inert in this analogue-only &B4 ; variant) |
| 0AEE | EQUB &00, ; keypad cell &D4 (inert in this analogue-only &D4 ; variant) |
| 0AF0 | EQUB &00, ; keypad cell &E4 (inert in this analogue-only &E4 ; variant) |
| 0AF2 | EQUB &00, ; keypad cell &B8 (inert in this analogue-only &B8 ; variant) |
| 0AF4 | EQUB &00, ; keypad cell &D8 (inert in this analogue-only &D8 ; variant) |
| 0AF6 | EQUB &00, ; keypad cell &E8 (inert in this analogue-only &E8 ; variant) |
| 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← 0A3D JMP |
| EQUB &60, &00, &00 | |
| 0AFD | .threshold_lo←1← 0A69 CPY |
| EQUB &00 ; Low sensitivity threshold (BASIC line 1850: SL%) | |
| 0AFE | .threshold_hi←1← 0A75 CPY |
| EQUB &00 ; High sensitivity threshold (BASIC line 1850: SH%) | |
| 0AFF | .unused_a_end |
| EQUB &00 ; Unused | |
