Voltmace Delta 14B/1 KEYPAD loader and resident driver
Updated 2 Sep 2026
← All Voltmace Delta 14B Driver versions
This disassembly
Feedback
Install the resident keypad driverEnable the 50 Hz vertical-sync event and route it to the matrix scanner. Sets User VIA port B to strobe the keypad (DDRB = &F0: top nibble out, bottom nibble in) and hooks EVNTV to point at the event handler, saving the previous vector at saved_evntv. Called from the BASIC front-end via CALL &A00.
|
|||||||||
| Load | Exec | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 1900 | 0A00 | .install_driver | |||||||
| PHP ; Preserve the caller flags | |||||||||
| 1901 | 0A01 | PHA ; Preserve A | |||||||
| 1902 | 0A02 | TYA ; Preserve Y... | |||||||
| 1903 | 0A03 | PHA ; ...on the stack | |||||||
| 1904 | 0A04 | TXA ; Preserve X... | |||||||
| 1905 | 0A05 | PHA ; ...on the stack | |||||||
| 1906 | 0A06 | LDA #&0e ; OSBYTE 14: enable an event... | |||||||
| 1908 | 0A08 | LDX #4 ; ...event 4, the 50 Hz vertical sync | |||||||
| 190A | 0A0A | JSR osbyte ; call OSBYTE | |||||||
| 190D | 0A0D | LDA #&f0 ; DDRB = &F0: bits 4-7 drive the column strobes + 74LS157 select... | |||||||
| 190F | 0A0F | STA user_via_ddrb ; ...bits 0-3 sense the four row lines | |||||||
| 1912 | 0A12 | SEI ; Block IRQs while re-pointing the vector | |||||||
| 1913 | 0A13 | LDA evntv ; Save the current event vector low byte... | |||||||
| 1916 | 0A16 | STA saved_evntv ; ...at saved_evntv, to chain to on exit | |||||||
| 1919 | 0A19 | LDA evntv_hi ; Save its high byte... | |||||||
| 191C | 0A1C | STA saved_evntv_hi ; ...too | |||||||
| 191F | 0A1F | LDA #&31 ; Point EVNTV at vsync_event_handler: low byte &31... | |||||||
| 1921 | 0A21 | STA evntv ; ...store it | |||||||
| 1924 | 0A24 | LDA #&0a ; ...high byte &0A... | |||||||
| 1926 | 0A26 | STA evntv_hi ; ...store it | |||||||
| 1929 | 0A29 | CLI ; Re-enable IRQs | |||||||
| 192A | 0A2A | PLA ; Restore X... | |||||||
| 192B | 0A2B | TAX ; ...from the stack | |||||||
| 192C | 0A2C | PLA ; Restore Y... | |||||||
| 192D | 0A2D | TAY ; ...from the stack | |||||||
| 192E | 0A2E | PLA ; Restore A | |||||||
| 192F | 0A2F | PLP ; Restore the flags | |||||||
| 1930 | 0A30 | RTS ; Return to the BASIC caller | |||||||
Vertical-sync event handler: scan the keypadEntered from the MOS every 50 Hz vsync event. Strobes the 3x4 matrix through User VIA port B (bit 7 selects handset 0/1 via the 74LS157), debounces and auto-repeats via debounce_counter, and on a pressed key sounds a short key-click (OSWORD 7) and inserts the mapped character into the keyboard buffer (OSBYTE &99). Chains to the previous event vector on exit. |
||
| Load | Exec | |
|---|---|---|
| 1931 | 0A31 | .vsync_event_handler |
| PHP ; Preserve the interrupted flags | ||
| 1932 | 0A32 | PHA ; Preserve A |
| 1933 | 0A33 | TYA ; Preserve Y... |
| 1934 | 0A34 | PHA ; ...on the stack |
| 1935 | 0A35 | TXA ; Preserve X... |
| 1936 | 0A36 | PHA ; ...on the stack |
| 1937 | 0A37 | LDA #0 ; Quick probe: drive every column low on handset 0... |
| 1939 | 0A39 | STA user_via_orb ; ...write it |
| 193C | 0A3C | LDA user_via_orb ; ...and read the rows back |
| 193F | 0A3F | CMP #&0f ; All four rows high (&0F) => nothing down on handset 0 |
| 1941 | 0A41 | BNE check_held_key ; Something is down -> go locate it |
| 1943 | 0A43 | LDA #&80 ; Probe handset 1 (bit 7 selects it)... |
| 1945 | 0A45 | STA user_via_orb ; ...write it |
| 1948 | 0A48 | LDA user_via_orb ; ...read the rows |
| 194B | 0A4B | CMP #&8f ; &8F = handset-1 select set, all rows high = no key |
| 194D | 0A4D | BEQ no_key_down ; Nothing on either handset -> idle |
| 194F | 0A4F | .check_held_key←1← 0A41 BNE |
| LDY current_row ; Is a key already being held? (current_row 5 = none) | ||
| 1952 | 0A52 | CPY #5 ; against the "none" marker (5) |
| 1954 | 0A54 | BEQ scan_matrix ; No held key -> full matrix scan |
| 1956 | 0A56 | LDX current_col ; Re-test the held key: fetch its column strobe... |
| 1959 | 0A59 | LDA col_strobes,x ; from the strobe table |
| 195C | 0A5C | STA user_via_orb ; ...drive it |
| 195F | 0A5F | LDA user_via_orb ; ...read the rows... |
| 1962 | 0A62 | AND row_masks,y ; ...and mask its row bit |
| 1965 | 0A65 | BNE scan_matrix ; Released (bit high now) -> rescan for a new key |
| 1967 | 0A67 | DEC debounce_counter ; Still held: count down to the next auto-repeat (patched to LDA by the editor to disable auto-repeat) |
| 196A | 0A6A | BNE handler_exit ; Not time to repeat yet -> exit |
| 196C | 0A6C | LDA #4 ; Reload the repeat interval (4 frames)... |
| 196E | 0A6E | STA debounce_counter ; store it |
| 1971 | 0A71 | CLC ; clear carry for the branch |
| 1972 | 0A72 | BCC emit_key ; Re-emit the held key |
| 1974 | 0A74 | .scan_matrix←2← 0A54 BEQ← 0A65 BNE |
| LDX #0 ; Full scan: start at column 0 | ||
| 1976 | 0A76 | .scan_next_column←1← 0A92 BNE |
| LDY #0 ; Start at row 0 of this column | ||
| 1978 | 0A78 | .scan_next_row←1← 0A8D BNE |
| CPY #0 ; Only (re)strobe the column when starting at row 0... | ||
| 197A | 0A7A | BNE test_row ; ...otherwise the strobe already stands |
| 197C | 0A7C | LDA col_strobes,x ; Drive column X (its handset bit included)... |
| 197F | 0A7F | STA user_via_orb ; write it |
| 1982 | 0A82 | .test_row←1← 0A7A BNE |
| LDA user_via_orb ; Read the rows... | ||
| 1985 | 0A85 | AND row_masks,y ; ...test this row bit |
| 1988 | 0A88 | BEQ key_pressed ; Bit low -> key at (column X, row Y) is pressed |
| 198A | 0A8A | INY ; Next row... |
| 198B | 0A8B | CPY #4 ; all four rows done? |
| 198D | 0A8D | BNE scan_next_row ; ...until all 4 rows tested |
| 198F | 0A8F | INX ; Next column... |
| 1990 | 0A90 | CPX #6 ; ...6 columns = 3 per handset |
| 1992 | 0A92 | BNE scan_next_column ; loop back for the next column |
| 1994 | 0A94 | CLC ; clear carry for the branch |
| 1995 | 0A95 | BCC handler_exit ; Nothing pressed -> exit |
| 1997 | 0A97 | .key_pressed←1← 0A88 BEQ |
| LDA #&18 ; New press: set the initial auto-repeat delay (24 frames)... | ||
| 1999 | 0A99 | STA debounce_counter ; store it |
| 199C | 0A9C | STX current_col ; Remember which key is now held: column... |
| 199F | 0A9F | STY current_row ; ...and row |
| 19A2 | 0AA2 | .emit_key←1← 0A72 BCC |
| LDA #7 ; OSWORD 7: play the key-click sound... | ||
| 19A4 | 0AA4 | LDX #&f6 ; ...parameter block at sound_block (&0AF6)... |
| 19A6 | 0AA6 | LDY #&0a ; block high byte &0A |
| 19A8 | 0AA8 | JSR osword ; call OSWORD |
| 19AB | 0AAB | CLC ; Key-table index = col*4 + row: |
| 19AC | 0AAC | LDA current_col ; take the column... |
| 19AF | 0AAF | ASL ; ...times 4... |
| 19B0 | 0AB0 | ASL ; shifted twice = x4 |
| 19B1 | 0AB1 | ADC current_row ; ...plus the row |
| 19B4 | 0AB4 | TAX ; ...as an index |
| 19B5 | 0AB5 | LDY key_codes,x ; Fetch that cell character into Y |
| 19B8 | 0AB8 | LDA #&99 ; OSBYTE &99: insert Y into buffer 0 (keyboard)... |
| 19BA | 0ABA | LDX #0 ; X=0 selects the keyboard buffer |
| 19BC | 0ABC | JSR osbyte ; ...as if the key were typed |
| 19BF | 0ABF | CLC ; clear carry for the branch |
| 19C0 | 0AC0 | BCC handler_exit ; Done |
| 19C2 | 0AC2 | .no_key_down←1← 0A4D BEQ |
| LDA #5 ; Record 'no key held' (row 5)... | ||
| 19C4 | 0AC4 | STA current_row ; store it |
| 19C7 | 0AC7 | .handler_exit←3← 0A6A BNE← 0A95 BCC← 0AC0 BCC |
| PLA ; Restore X... | ||
| 19C8 | 0AC8 | TAX ; into X |
| 19C9 | 0AC9 | PLA ; Restore Y... |
| 19CA | 0ACA | TAY ; into Y |
| 19CB | 0ACB | PLA ; Restore A |
| 19CC | 0ACC | PLP ; Restore the flags |
| 19CD | 0ACD | JMP (saved_evntv) ; Chain to the event handler we displaced |
| 19D0 | 0AD0 | .debounce_counter←3← 0A67 DEC← 0A6E STA← 0A99 STA |
| EQUB &10 ; Frames until the next auto-repeat (0 = repeat this frame) | ||
| 19D1 | 0AD1 | .current_row←4← 0A4F LDY← 0A9F STY← 0AB1 ADC← 0AC4 STA |
| EQUB &04 ; Row of the held key (5 = none) | ||
| 19D2 | 0AD2 | .current_col←3← 0A56 LDX← 0A9C STX← 0AAC LDA |
| EQUB &00 ; Column of the held key | ||
| 19D3 | 0AD3 | .row_masks←2Used as index base by← 0A62 AND← 0A85 AND |
| EQUB &08, &04, &02, ; Port-B input bit for matrix &01 ; rows 0-3 | ||
| 19D7 | 0AD7 | .col_strobes←2Used as index base by← 0A59 LDA← 0A7C LDA |
| EQUB &60, &50, ; Port-B strobe per column: cols 0-2 = &30, &E0, ; handset 0 (&60,&50,&30), cols 3-5 = &D0, &B0 ; handset 1 (bit 7 set for the ; 74LS157) | ||
| 19DD | 0ADD | .key_codes←1Used as index base by← 0AB5 LDY |
| EQUB &7F ; Default character per cell, indexed col*4+row: cells 0-11 handset 0 (digits/DELETE/RETURN), cells 12-23 handset 1 (letters). The editor overwrites this table. | ||
| 19DE | 0ADE | EQUS "1470258" |
| 19E5 | 0AE5 | EQUB &0D |
| 19E6 | 0AE6 | EQUS "369ADGJBEHKCFIL" |
| 19F5 | 0AF5 | EQUB &00 ; Spare byte |
| 19F6 | 0AF6 | .sound_block |
| EQUW &0000, ; OSWORD 7 block: channel &0000, &FFF8, ; amplitude &FFF8 (patched by the editor &80, ; beep option), pitch &0080, duration &0001 ; &0001 | ||
| 19FE | 0AFE | .saved_evntv←2← 0A16 STA← 0ACD JMP |
| EQUW &0000 ; Previous EVNTV, chained to on exit | ||
| ; Image of page &0B (the MOS soft-key/function-key ; buffer). The relocator skips page &0B so the user keys ; survive, making these bytes inert filler. | ||
| 1A00 | EQUB &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10, &10 | |
| ; ROL-encoded tokenised BASIC (the keypad-definition ; editor); relocates to PAGE=&0C00 and is decrypted in ; place. Source: ; basic/voltmace-delta-14b-driver-keypad.bas. | ||
| 1B00 | ||
| ; Zero padding between the encrypted BASIC and the *RUN ; loader | ||
| 3869 | EQUB &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00, &00 | |
| ; Filler ahead of the loader entry; reads as a stub BASIC ; line (&0D, line 13, then RTS bytes) | ||
| 3900 | .loader_preamble | |
| EQUB &0D, &00, &0D, &60, &60, &60 | ||
*RUN entry: relocate, decode, and auto-runThe DFS execution address. Relocates the whole image down to &0A00 (installing the resident driver code and moving the encrypted BASIC to PAGE &0C00), decrypts the BASIC in place, then queues 'PAGE=&C00 / OLD / RUN' so the now-plain BASIC front-end starts. |
|
| 3906 | .main |
| PHA ; Preserve A | |
| 3907 | TXA ; Preserve X... |
| 3908 | PHA ; ...on the stack |
| 3909 | TYA ; Preserve Y... |
| 390A | PHA ; ...on the stack |
| 390B | JSR relocate_image ; Copy the image down (resident driver to &0A00, BASIC to PAGE &0C00) |
| 390E | JSR decode_basic ; Decrypt the relocated BASIC in place |
| 3911 | JSR patch_basic_header ; Repair the BASIC's first-line length byte |
| 3914 | JSR os_dependent_setup ; Queue the auto-run commands (OS-dependent) |
| 3917 | PLA ; Restore Y... |
| 3918 | TAY ; into Y |
| 3919 | PLA ; Restore X... |
| 391A | TAX ; into X |
| 391B | PLA ; Restore A |
| 391C | RTS ; Return to the MOS; the queued commands then run the BASIC |
Decrypt the relocated BASICRotate every byte of the relocated BASIC left one bit (the inverse of the ROL-1 storage protection), across pages &0C00-&2AFF, in place. |
|
| 391D | .decode_basic←1← 390E JSR |
| LDA decode_ptr ; Save the caller decode_ptr low byte... | |
| 391F | STA decode_ptr_save ; save it |
| 3922 | LDA decode_ptr_hi ; ...and high byte... |
| 3924 | STA decode_ptr_save_hi ; ...at decode_ptr_save |
| 3927 | LDX #0 ; Point decode_ptr at PAGE &0C00: low byte 0... |
| 3929 | STX decode_ptr ; set it |
| 392B | LDX #&0c ; ...high byte &0C... |
| 392D | LDY #0 ; byte index 0 |
| 392F | .decode_next_page←1← 3941 BNE |
| STX decode_ptr_hi ; Set the page being decrypted | |
| 3931 | .decode_next_byte←1← 393A BNE |
| LDA (decode_ptr),y ; Read an encrypted byte... | |
| 3933 | CLC ; clear carry for the rotate |
| 3934 | ASL ; ...rotate it left one bit (undo the ROL-1 protection)... |
| 3935 | ADC #0 ; ...carrying bit 7 into bit 0... |
| 3937 | STA (decode_ptr),y ; ...and store it back |
| 3939 | INY ; Next byte... |
| 393A | BNE decode_next_byte ; ...to the end of the page |
| 393C | LDX decode_ptr_hi ; Next page... |
| 393E | INX ; increment the page |
| 393F | CPX #&2b ; ...until page &2B (end of the BASIC region) |
| 3941 | BNE decode_next_page ; keep decrypting pages |
| 3943 | LDA decode_ptr_save ; Restore the caller decode_ptr... |
| 3946 | STA decode_ptr ; low byte |
| 3948 | LDA decode_ptr_save_hi ; high byte |
| 394B | STA decode_ptr_hi ; store it |
| 394D | RTS ; Done |
OS-version-dependent setupReads os_signature to pick how the auto-run commands are queued. |
|
| 394E | .os_dependent_setup←1← 3914 JSR |
| LDY #0 ; clear Y | |
| 3950 | LDA os_signature ; Read the OS ROM signature byte... |
| 3953 | CMP #&4f ; ...'O' identifies the supported OS |
| 3955 | BEQ use_direct_poke ; Supported OS -> poke the buffer directly |
| 3957 | JSR setup_keys ; Other OS -> insert via OSBYTE |
| 395A | RTS ; Done |
| 395B | .use_direct_poke←1← 3955 BEQ |
| JSR queue_autorun ; Queue by poking the keyboard buffer | |
| 395E | RTS ; Done |
| 395F | .decode_ptr_save←2← 391F STA← 3943 LDA |
| EQUW &EAEA ; Scratch: caller's decode_ptr saved across decode_basic (&EAEA at rest) | |
Repair the BASIC program's first lineWrite &16 (22) into basic_line10_len, the length byte of the line-10 REM at PAGE=&0C00. That byte is stored as 0 (part of the protection), so without this repair the relocated program cannot be LISTed or RUN. |
|
| 3961 | .patch_basic_header←1← 3911 JSR |
| LDA #&16 ; The first BASIC line is 22 bytes long... | |
| 3963 | STA basic_line10_len ; ...restore its length byte (stored as 0 by the protection) |
| 3966 | RTS ; Done |
Queue the auto-run commands (direct poke)Copy autorun_commands into the MOS keyboard buffer so the OS reads them as typed and runs the decoded BASIC. autorun_index wraps into the 32-byte buffer at &03E0-&03FF. |
|
| 3967 | .queue_autorun←1← 395B JSR |
| LDY #0 ; Start at the first command byte | |
| 3969 | .queue_next_byte←1← 3982 JMP |
| LDX autorun_index ; Current buffer fill position... | |
| 396C | LDA autorun_commands,y ; read a command byte... |
| 396F | BEQ queue_done ; ...&00 terminates |
| 3971 | STA kbd_buffer,x ; Poke it into the keyboard buffer |
| 3974 | INC autorun_index ; Advance the fill position... |
| 3977 | LDA autorun_index ; reload it |
| 397A | BNE queue_advance ; unless it wrapped to 0 |
| 397C | LDA #&e0 ; ...wrapping back to &E0 (buffer at &03E0)... |
| 397E | STA autorun_index ; store it |
| 3981 | .queue_advance←1← 397A BNE |
| INY ; Next command byte | |
| 3982 | JMP queue_next_byte ; loop |
| 3985 | .queue_done←1← 396F BEQ |
| RTS ; Done | |
Queue the auto-run commands (OSBYTE path)The non-&4F-OS variant of queue_autorun: set the BREAK/ESCAPE behaviour, then insert each autorun_commands byte with OSBYTE &8A. |
|
| 3986 | .setup_keys←1← 3957 JSR |
| LDA #&c8 ; OSBYTE 200: set the BREAK/ESCAPE behaviour... | |
| 3988 | LDX #3 ; value 3 |
| 398A | JSR osbyte ; call OSBYTE |
| 398D | LDX #0 ; Start at the first command byte |
| 398F | .insert_next_key←1← 39A5 JMP |
| LDA autorun_commands,x ; Read a command byte... | |
| 3992 | BEQ setup_keys_done ; ...&00 terminates |
| 3994 | TAY ; The character to insert |
| 3995 | TXA ; Save the loop index... |
| 3996 | STA setup_key_index ; save it |
| 3999 | LDX #0 ; OSBYTE &8A: insert Y into buffer 0 (keyboard)... |
| 399B | LDA #&8a ; A = &8A |
| 399D | JSR osbyte ; call OSBYTE |
| 39A0 | LDA setup_key_index ; Restore the loop index... |
| 39A3 | TAX ; into X |
| 39A4 | INX ; Next command byte |
| 39A5 | JMP insert_next_key ; loop |
| 39A8 | .setup_keys_done←1← 3992 BEQ |
| RTS ; Done | |
| 39A9 | .setup_key_index←2← 3996 STA← 39A0 LDA |
| EQUB &EA ; Scratch: saved loop index (&EA at rest) | |
Relocate the program image to &0A00OSCLI the startup command, then block-copy the image from &1900 down to &0A00 (copy_pages pages via copy_src -> copy_dst), skipping destination page &0B so the soft-key buffer survives. |
|
| 39AA | .relocate_image←1← 390B JSR |
| LDX #&f1 ; OSCLI the startup command at oscli_command (&39F1): low byte... | |
| 39AC | LDY #&39 ; ...high byte... |
| 39AE | JSR oscli ; ...call OSCLI |
| 39B1 | SEC ; (carry set; not used by the copy below) |
| 39B2 | LDA #0 ; Leftover byte count = 0 (whole pages only)... |
| 39B4 | STA copy_rem ; store it |
| 39B6 | LDA #&20 ; Copy &20 = 32 pages... |
| 39B8 | STA copy_pages ; store it |
| 39BA | LDA #0 ; Destination = &0A00: low byte... |
| 39BC | STA copy_dst ; store it |
| 39BE | LDA #&0a ; ...high byte... |
| 39C0 | STA copy_dst_hi ; store it |
| 39C2 | LDA #0 ; Source = &1900 (the loaded image): low byte... |
| 39C4 | STA copy_src ; store it |
| 39C6 | LDA #&19 ; ...high byte... |
| 39C8 | STA copy_src_hi ; store it |
| 39CA | LDY #0 ; Byte index within the page |
| 39CC | LDX copy_pages ; Page count... |
| 39CE | BEQ copy_remainder ; ...none -> just the remainder |
| 39D0 | .copy_page←2← 39DB BNE← 39E2 BNE |
| LDA copy_dst_hi ; Which destination page are we about to write? | |
| 39D2 | CMP #&0b ; Leave page &0B untouched so the user soft-key definitions survive the move |
| 39D4 | BEQ copy_next_byte ; ...skip the store for that page |
| 39D6 | LDA (copy_src),y ; Copy one byte... |
| 39D8 | STA (copy_dst),y ; to the destination |
| 39DA | .copy_next_byte←1← 39D4 BEQ |
| INY ; Next byte... | |
| 39DB | BNE copy_page ; ...to the end of the page |
| 39DD | INC copy_src_hi ; Next source page... |
| 39DF | INC copy_dst_hi ; ...and destination page |
| 39E1 | DEX ; one fewer page |
| 39E2 | BNE copy_page ; ...until all pages copied |
| 39E4 | .copy_remainder←1← 39CE BEQ |
| LDX copy_rem ; Any leftover bytes? (none here) | |
| 39E6 | BEQ copy_done ; none -> done |
| 39E8 | .copy_remainder_byte←1← 39EE BNE |
| LDA (copy_src),y ; Copy one byte... | |
| 39EA | STA (copy_dst),y ; to the destination |
| 39EC | INY ; next byte |
| 39ED | DEX ; one fewer |
| 39EE | BNE copy_remainder_byte ; loop |
| 39F0 | .copy_done←1← 39E6 BEQ |
| RTS ; Done | |
| 39F1 | .oscli_command |
| EQUS "T." ; Startup *-command "T." (*TAPE): select the cassette filing system, CR-terminated | |
| 39F3 | EQUB &0D |
| 39F4 | .autorun_commands←2Used as index base by← 396C LDA← 398F LDA |
| EQUB &15, &0D ; CTRL-U + CR: clear the input line | |
| 39F6 | EQUS "PA.=&C00" ; set PAGE to &0C00, where the BASIC now lives |
| 39FE | EQUB &0D ; submit |
| 39FF | EQUS "OLD" ; reinstate the decoded program (OLD) |
| 3A02 | EQUB &0D ; submit |
| 3A03 | EQUS "RUN" ; run it (RUN) |
| 3A06 | EQUB &0D, &00 ; submit; the &00 then stops the queue copy |
| ; Uninitialised tail of the saved image. The file is a memory ; dump &1900-&3A80 (the BASIC clears exactly TO &3A80); the ; loader's code and tables end ~120 bytes short, so this holds ; whatever occupied the memory at save time. It is referenced by ; nothing and decodes as neither 6502, tokenised BASIC (raw or ; bit-rotated), nor text; its byte statistics are those of ; noise. Inert: the BASIC memory-clear overwrites it at startup. | |
| 3A08 | .trailing_data |
| EQUB &0D, &83, &FC, &A6, &9A, &0E, &EF, &7C, &D1, &53, &48, &0A, &DF, &19, &6B, &A3, &C5, &A4, &06, &E7, &F8, &19, &48, &DA, &AC, &0D, &D7, &7F, &BB, &95, &91, &6B, &05, &C7, &E8, &5F, &61, &3B, &08, &08, &84, &CA, &16, &93, &A6, &D3, &CF, &15, &1A, &51, &34, &6F, &E4, &40, &F0, &19, &BA, &96, &05, &DF, &FD, &73, &81, &03, &CF, &D8, &09, &FC, &48, &31, &0A, &67, &EB, &BC, &3A, &0D, &29, &34, &D7, &11, &E9, &6B, &D0, &32, &A0, &34, &38, &0D, &5E, &8B, &9F, &63, &35, &41, &4B, &8C, &2B, &FC, &01, &19, &16, &2D, &E0, &39, &C7, &E0, &8B, &36, &53, &A2, &AC, &B4, &25, &A8, &21, &5D, &1C, &5D, &35, &2F | |
