Voltmace Delta 14B/1 JOYSTIK loader
Updated 2 Sep 2026
← All Voltmace Delta 14B Driver versions
This disassembly
Feedback
| ; Decoy stub BASIC line (&0D, line 13, RTS bytes) so a naive ; *LOAD/LIST at PAGE sees junk, not the loader | |
| 1900 | .decoy |
| EQUB &0D, &00, &0D, &60, &60, &60, &60, &60, &60 | |
*RUN entry: decrypt and auto-runThe DFS execution address. Decrypts the resident drivers and BASIC in place, repairs the BASIC's first line, then queues PAGE=&1C00 / OLD / RUN so the now-plain BASIC configuration program starts. |
|
| 1909 | .main |
| PHA ; Preserve A | |
| 190A | TXA ; Preserve X... |
| 190B | PHA ; ...on the stack |
| 190C | TYA ; Preserve Y... |
| 190D | PHA ; ...on the stack |
| 190E | JSR decode_basic ; Decrypt the resident drivers and BASIC in place |
| 1911 | JSR patch_header ; Repair the BASIC's first-line length byte |
| 1914 | JSR os_dependent_setup ; Queue the auto-run commands (OS-dependent) |
| 1917 | PLA ; Restore Y... |
| 1918 | TAY ; ... |
| 1919 | PLA ; Restore X... |
| 191A | TAX ; ... |
| 191B | PLA ; Restore A |
| 191C | RTS ; Return to the MOS; the queued commands then run the BASIC |
Decrypt the resident drivers and BASICRotate every byte of &1A00-&4AFF left one bit (the inverse of the ROL-1 storage protection), in place. That covers both resident driver variants (&1A00, &1B00) and the tokenised BASIC from PAGE=&1C00; the loop stops at page &4B, leaving the BASIC's raw tail untouched. |
|
| 191D | .decode_basic←1← 190E JSR |
| LDA decode_ptr ; Save the caller decode_ptr low byte... | |
| 191F | STA decode_ptr_save ; ... |
| 1922 | LDA decode_ptr_hi ; ...and high byte... |
| 1924 | STA decode_ptr_save_hi ; ...at decode_ptr_save |
| 1927 | LDX #0 ; Point decode_ptr at &1A00: low byte 0... |
| 1929 | STX decode_ptr ; ... |
| 192B | LDX #&1a ; ...high byte &1A... |
| 192D | LDY #0 ; byte index 0 |
| 192F | .decode_next_page←1← 1941 BNE |
| STX decode_ptr_hi ; Set the page being decrypted | |
| 1931 | .decode_next_byte←1← 193A BNE |
| LDA (decode_ptr),y ; Read an encrypted byte... | |
| 1933 | CLC ; clear carry for the rotate |
| 1934 | ASL ; ...rotate it left one bit (undo the ROL-1 protection)... |
| 1935 | ADC #0 ; ...carrying bit 7 into bit 0... |
| 1937 | STA (decode_ptr),y ; ...and store it back |
| 1939 | INY ; Next byte... |
| 193A | BNE decode_next_byte ; ...to the end of the page |
| 193C | LDX decode_ptr_hi ; Next page... |
| 193E | INX ; ... |
| 193F | CPX #&4b ; ...until page &4B (the BASIC raw tail is left alone) |
| 1941 | BNE decode_next_page ; ... |
| 1943 | LDA decode_ptr_save ; Restore the caller decode_ptr... |
| 1946 | STA decode_ptr ; ... |
| 1948 | LDA decode_ptr_save_hi ; ... |
| 194B | STA decode_ptr_hi ; ... |
| 194D | RTS ; Done |
OS-version-dependent setupReads the three-byte "OS " ROM signature to pick how the auto-run commands are queued. |
|
| 194E | .os_dependent_setup←1← 1914 JSR |
| LDY #0 ; Count matching signature bytes in Y | |
| 1950 | LDA os_signature ; First OS ROM byte... |
| 1953 | CMP #&4f ; ...'O'? |
| 1955 | BNE os_check_1 ; ... |
| 1957 | INY ; match: bump the count |
| 1958 | .os_check_1←1← 1955 BNE |
| LDA os_signature_1 ; Second OS ROM byte... | |
| 195B | CMP #&53 ; ...'S'? |
| 195D | BNE os_check_2 ; ... |
| 195F | INY ; match: bump the count |
| 1960 | .os_check_2←1← 195D BNE |
| LDA os_signature_2 ; Third OS ROM byte... | |
| 1963 | CMP #&20 ; ...' '? |
| 1965 | BNE os_check_done ; ... |
| 1967 | INY ; match: bump the count |
| 1968 | .os_check_done←1← 1965 BNE |
| CPY #3 ; All three bytes matched "OS "? | |
| 196A | BEQ use_direct_poke ; yes -> poke the buffer directly |
| 196C | JSR setup_keys ; no -> insert via OSBYTE |
| 196F | RTS ; Done |
| 1970 | .use_direct_poke←1← 196A BEQ |
| JSR queue_autorun ; Queue by poking the keyboard buffer | |
| 1973 | RTS ; Done |
| 1974 | .decode_ptr_save←2← 191F STA← 1943 LDA |
| EQUW &0000 ; Scratch: caller's decode_ptr saved across decode_basic | |
Repair the BASIC program's first lineWrite &17 (23) into basic_line10_len, the length byte of the line-10 REM at PAGE=&1C00, which is stored as 0 by the protection. |
|
| 1976 | .patch_header←1← 1911 JSR |
| LDA #&17 ; The first BASIC line is 23 bytes long... | |
| 1978 | STA basic_line10_len ; ...restore its length byte (stored as 0 by the protection) |
| 197B | RTS ; Done |
Queue the auto-run commands (direct poke)Copy autorun_commands into the MOS keyboard buffer so the OS reads them as typed. autorun_index wraps into the 32-byte buffer at &03E0-&03FF. |
|
| 197C | .queue_autorun←1← 1970 JSR |
| LDY #0 ; Start at the first command byte | |
| 197E | .queue_next_byte←1← 1997 JMP |
| LDX autorun_index ; Current buffer fill position... | |
| 1981 | LDA autorun_commands,y ; read a command byte... |
| 1984 | BEQ queue_done ; ...&00 terminates |
| 1986 | STA kbd_buffer,x ; Poke it into the keyboard buffer |
| 1989 | INC autorun_index ; Advance the fill position... |
| 198C | LDA autorun_index ; ... |
| 198F | BNE queue_advance ; ... |
| 1991 | LDA #&e0 ; ...wrapping back to &E0 (buffer at &03E0)... |
| 1993 | STA autorun_index ; ... |
| 1996 | .queue_advance←1← 198F BNE |
| INY ; Next command byte | |
| 1997 | JMP queue_next_byte ; ... |
| 199A | .queue_done←1← 1984 BEQ |
| RTS ; Done | |
Queue the auto-run commands (OSBYTE path)The non-"OS "-signature variant of queue_autorun: set the BREAK/ESCAPE behaviour, then insert each autorun_commands byte with OSBYTE &8A. |
|
| 199B | .setup_keys←1← 196C JSR |
| LDA #&c8 ; OSBYTE 200: set the BREAK/ESCAPE behaviour... | |
| 199D | LDX #3 ; ... |
| 199F | JSR osbyte ; call OSBYTE |
| 19A2 | LDX #0 ; Start at the first command byte |
| 19A4 | .insert_next_key←1← 19BA JMP |
| LDA autorun_commands,x ; Read a command byte... | |
| 19A7 | BEQ setup_keys_done ; ...&00 terminates |
| 19A9 | TAY ; The character to insert |
| 19AA | TXA ; Save the loop index... |
| 19AB | STA setup_key_index ; ... |
| 19AE | LDX #0 ; OSBYTE &8A: insert Y into buffer 0 (keyboard)... |
| 19B0 | LDA #&8a ; A = &8A |
| 19B2 | JSR osbyte ; call OSBYTE |
| 19B5 | LDA setup_key_index ; Restore the loop index... |
| 19B8 | TAX ; ... |
| 19B9 | INX ; Next command byte |
| 19BA | JMP insert_next_key ; ... |
| 19BD | .setup_keys_done←1← 19A7 BEQ |
| RTS ; Done | |
| 19BE | .setup_key_index←2← 19AB STA← 19B5 LDA |
| EQUB &EA ; Scratch: saved loop index (&EA at rest) | |
| 19BF | .autorun_commands←2Used as index base by← 1981 LDA← 19A4 LDA |
| EQUB &15, &0D ; CTRL-U + CR: clear the input line | |
| 19C1 | EQUS "PA.=&1C00" ; set PAGE to &1C00, where the BASIC lives |
| 19CA | EQUB &0D ; submit |
| 19CB | EQUS "OLD" ; reinstate the decoded program (OLD) |
| 19CE | EQUB &0D ; submit |
| 19CF | EQUS "RUN" ; run it (RUN) |
| 19D2 | EQUB &0D, &00 ; submit; the &00 then stops the queue copy |
| ; Unused bytes after the command list, up to the resident driver ; at &1A00; referenced by nothing and inert. | |
| 19D4 | .loader_tail |
| EQUB &0D, &54, &67, &42, &CC, &6D, &F6, &B0, &C0, &FF, &38, &25, &4D, &05, &55, &06, &B7, &84, &20, &D6, &88, &B0, &40, &BD, &D3, &FA, &C8, &10, &F8, &F1, &EB, &44, &1B, &4C, &01, &63, &F6, &50, &74, &21, &39, &71, &C0, &86 | |
| ; ROL-encoded: resident driver variant A (&1A00), resident ; driver variant B (&1B00), then the tokenised BASIC from ; PAGE=&1C00 (raw past &4B00). Resident drivers: see ; driver_a.asm/driver_b.asm; BASIC: ; basic/voltmace-delta-14b-driver-joystik.bas. | |
| 1A00 | .encoded_region |
| ; BBC BASIC's variable heap as it stood when the author saved ; the &1900-&4D00 image: variable-name/value records for the ; program globals (e.g. REV$="Rev 2.0", and LR%, R1%, EV$, LVL%, ; LVH%, S% ...). Not used by the loader; PAGE=&1C00 sits below ; it, so BASIC rebuilds its own variables on RUN. | |
| 4C93 | .basic_variables |
| EQUB &E5, &E5, &E5, &E5, &E5, &A1, &4C, &52, &25, &00, &00, &00, &00, &00, &E5, &00, &52, &31, &25, &00, &00, &00, &00, &00, &E5, &00, &45, &56, &24, &00, &B5, &4C, &07, &07, &52, &65, &76, &20, &32, &2E, &30, &C6, &4C, &56, &4C, &25, &00, &72, &00, &00, &00, &D0, &4C, &56, &48, &25, &00, &E7, &00, &00, &00, &E5, &00, &53, &25, &00, &01, &00, &00, &00, &E5, &00, &25, &28, &00, &03, &49, &00, &21, &00, &00, &00, &72, &00, &00, &00, &73, &00, &00, &00, &74, &00, &00, &00, &15, &00, &00, &00, &75, &00, &00, &00, &76, &00, &00, &00, &17, &00, &00 | |
