Resident driver — joystick only

Updated 2 Sep 2026

← All Voltmace Delta 14B Driver versions

Install the resident joystick driver

Point 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 INKEY

Runs 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 input

For 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