Acorn ANFS 4.08.53
Updated 31 Mar 2026
← All Acorn NFS and Acorn ANFS versions
- Acorn ANFS 4.08.53 in The BBC Micro ROM Library
- Disassembly source on GitHub
- Discuss this disassembly on the Stardot Forums thread: Acorn ANFS - A new annotated disassembly
- Changes from NFS 3.65
- Found a mistake or a comment that could be clearer? Report an issue.
| ; Sideways ROM header | |
| ; ANFS ROM 4.08.53 disassembly (Acorn Advanced Network Filing ; System) | |
| ; ============================================================== ; ===== | |
| 8000 | .rom_header←1← 04E6 LDA |
| .language_entry←1← 04E6 LDA | |
| .pydis_start←1← 04E6 LDA | |
| EQUB &00, &42, &43 | |
| 8003 | .service_entry←1← 04F5 LDY |
| JMP service_handler ; JMP service_handler | |
| 8006 | .rom_type←1← 04DA AND |
| EQUB &82 ; ROM type: service + language | |
| 8007 | .copyright_offset←1← 04E2 LDX |
| EQUB copyright - rom_header | |
| 8008 | .binary_version |
| EQUB &04 | |
| 8009 | .title |
| EQUS "Acorn ANFS" | |
| 8013 | .version |
| EQUB &00 | |
| 8014 | .copyright |
| EQUB &00 ; Null terminator before copyright | |
| 8015 | .copyright_string |
| EQUS "(C)1985 Acorn." | |
Service 5: unrecognised interrupt (SR dispatch)Tests IFR bit 2 (SR complete) to check for a shift register transfer complete. If SR is not set, returns A=5 to pass the service call on. If SR is set, saves registers, reads the VIA ACR, clears and restores the SR mode bits from ws_0d64, then dispatches the TX completion callback via the operation type stored in tx_op_type. The indexed handler performs the completion action (e.g. resuming background print spooling) before returning with A=0 to claim the service call.
|
||||||||
| 8023 | .svc5_irq_check | |||||||
| LDA #4 ; A=4: SR bit mask for IFR test | ||||||||
| 8025 | BIT system_via_ifr ; Test IFR bit 2: SR complete | |||||||
| 8028 | BNE save_registers ; SR set: shift register complete | |||||||
| 802A | LDA #5 ; A=5: not our interrupt, pass on | |||||||
| 802C | RTS ; Return service code 5 to MOS | |||||||
| 802D | .save_registers←1← 8028 BNE | |||||||
| TXA ; Save X on stack | ||||||||
| 802E | PHA ; Push saved X | |||||||
| 802F | TYA ; Save Y on stack | |||||||
| 8030 | PHA ; Push saved Y | |||||||
| 8031 | LDA system_via_acr ; Read ACR for shift register restore | |||||||
| 8034 | AND #&e3 ; Clear SR mode bits (2-4) | |||||||
| 8036 | ORA ws_0d64 ; Restore saved SR mode from ws_0d64 | |||||||
| 8039 | STA system_via_acr ; Write restored ACR to system VIA | |||||||
| 803C | LDA system_via_sr ; Read SR to clear shift register IRQ | |||||||
| 803F | LDA #4 ; A=4: SR bit mask | |||||||
| 8041 | STA system_via_ifr ; Clear SR interrupt flag in IFR | |||||||
| 8044 | STA system_via_ier ; Disable SR interrupt in IER | |||||||
| 8047 | LDY tx_op_type ; Load TX operation type for dispatch | |||||||
| 804A | TYA ; Copy to A for sign test | |||||||
| 804B | BMI set_jsr_protection ; Bit 7 set: dispatch via table | |||||||
| 804D | LDY #&fe ; Y=&FE: Econet receive event | |||||||
| 804F | JMP tx_done_fire_event ; Fire event (enable: *FX52,150) | |||||||
Set JSR protection and dispatch via tableValidates the TX operation type in Y against the dispatch table range, saves the current JSR protection mask, sets protection bits 2-4, then dispatches through the PHA/RTS trampoline using the table at set_rx_buf_len_hi. If Y >= &86, skips the protection setup and dispatches directly.
|
||||
| 8052 | .set_jsr_protection←1← 804B BMI | |||
| CPY #&86 ; Y >= &86: above dispatch range | ||||
| 8054 | BCS dispatch_svc5 ; Out of range: skip protection | |||
| 8056 | LDA ws_0d68 ; Save current JSR protection mask | |||
| 8059 | STA ws_0d69 ; Backup to saved_jsr_mask | |||
| 805C | ORA #&1c ; Set protection bits 2-4 | |||
| 805E | STA ws_0d68 ; Apply protection during dispatch | |||
| 8061 | .dispatch_svc5←1← 8054 BCS | |||
| LDA #&85 ; Push return addr high (&85) | ||||
| 8063 | PHA ; High byte on stack for RTS | |||
| 8064 | LDA set_rx_buf_len_hi,y ; Load dispatch target low byte | |||
| 8067 | PHA ; Low byte on stack for RTS | |||
| 8068 | .svc_5_unknown_irq | |||
| RTS ; RTS = dispatch to PHA'd address | ||||
ADLC initialisationInitialise ADLC hardware and Econet workspace. Reads station ID via &FE18 (INTOFF side effect), performs a full ADLC reset (adlc_full_reset), then checks for Tube co-processor via OSBYTE &EA and stores the result in l0d63. Issues NMI claim service request (OSBYTE &8F, X=&0C). Falls through to init_nmi_workspace to copy the NMI shim to RAM. |
|
| 8069 | .adlc_init←1← 8F40 JSR |
| BIT station_id_disable_net_nmis ; INTOFF: read station ID, disable NMIs | |
| 806C | JSR adlc_full_reset ; Full ADLC hardware reset |
| 806F | LDA #&ea ; OSBYTE &EA: check Tube co-processor |
| 8071 | LDX #0 ; X=0 for OSBYTE |
| 8073 | STX ws_0d62 ; Clear Econet init flag before setup |
| 8076 | JSR osbyte_x0 ; Check Tube presence via OSBYTE &EA |
| 8079 | STX tube_present ; Store Tube presence flag from OSBYTE &EA |
| 807C | LDA #&8f ; OSBYTE &8F: issue service request |
| 807E | LDX #&0c ; X=&0C: NMI claim service |
| 8080 | JSR osbyte_yff ; Issue NMI claim service request |
| 8083 | LDY #5 ; Y=5: NMI claim service number |
| 8085 | .econet_restore |
| CPY #5 ; Check if NMI service was claimed (Y changed) | |
| 8087 | BNE adlc_init_done ; Service claimed by other ROM: skip init |
| fall through ↓ | |
Initialise NMI workspace (skip service request)Copies 32 bytes of NMI shim code from ROM (listen_jmp_hi) to &0D00, then patches the current ROM bank number into the self-modifying code at &0D07. Clears tx_src_net, need_release_tube, and tx_op_type to zero. Reads station ID into tx_src_stn (&0D22). Sets ws_0d60 and ws_0d62 to &80 to mark TX complete and Econet initialised. Finally re-enables NMIs via INTON (&FE20 read). |
|
| 8089 | .init_nmi_workspace |
| LDY #&20 ; Copy 32 bytes of NMI shim from ROM to &0D00 | |
| 808B | .copy_nmi_shim←1← 8092 BNE |
| LDA listen_jmp_hi,y ; Read byte from NMI shim ROM source | |
| 808E | STA nmi_code_base,y ; Write to NMI shim RAM at &0D00 |
| 8091 | DEY ; Next byte (descending) |
| 8092 | BNE copy_nmi_shim ; Loop until all 32 bytes copied |
| 8094 | LDA romsel_copy ; Patch current ROM bank into NMI shim |
| 8096 | STA nmi_romsel ; Self-modifying code: ROM bank at &0D07 |
| 8099 | STY tx_src_net ; Clear source network (Y=0 from copy loop) |
| 809C | STY need_release_tube ; Clear Tube release flag |
| 809E | STY tx_op_type ; Clear TX operation type |
| 80A1 | LDY station_id_disable_net_nmis ; Read station ID (and disable NMIs) |
| 80A4 | STY tx_src_stn ; Set own station as TX source |
| 80A7 | LDA #&80 ; &80 = Econet initialised |
| 80A9 | STA ws_0d60 ; Mark TX as complete (ready) |
| 80AC | STA ws_0d62 ; Mark Econet as initialised |
| 80AF | BIT video_ula_control ; INTON: re-enable NMIs (&FE20 read side effect) |
| 80B2 | .adlc_init_done←1← 8087 BNE |
| RTS ; Return | |
NMI RX scout handler (initial byte)Default NMI handler for incoming scout frames. Checks if the frame is addressed to us or is a broadcast. Installed as the NMI target during idle RX listen mode. Tests SR2 bit0 (AP = Address Present) to detect incoming data. Reads the first byte (destination station) from the RX FIFO and compares against our station ID. Reading &FE18 also disables NMIs (INTOFF side effect). |
|
| 80B3 | .nmi_rx_scout←1← 89A8 JMP |
| LDA #1 ; A=&01: mask for SR2 bit0 (AP = Address Present) | |
| 80B5 | BIT econet_control23_or_status2 ; BIT SR2: Z = A AND SR2 -- tests if AP is set |
| 80B8 | BEQ scout_error ; AP not set, no incoming data -- check for errors |
| 80BA | LDA econet_data_continue_frame ; Read first RX byte (destination station address) |
| 80BD | CMP station_id_disable_net_nmis ; Compare to our station ID (&FE18 read = INTOFF, disables NMIs) |
| 80C0 | BEQ accept_frame ; Match -- accept frame |
| 80C2 | CMP #&ff ; Check for broadcast address (&FF) |
| 80C4 | BNE scout_reject ; Neither our address nor broadcast -- reject frame |
| 80C6 | LDA #&40 ; Flag &40 = broadcast frame |
| 80C8 | STA rx_src_net ; Store broadcast flag in rx_src_net |
| 80CB | .accept_frame←1← 80C0 BEQ |
| LDA #&d0 ; Install nmi_rx_scout_net NMI handler | |
| 80CD | JMP install_nmi_handler ; Install next handler and RTI |
RX scout second byte handlerReads the second byte of an incoming scout (destination network). Checks for network match: 0 = local network (accept), &FF = broadcast (accept and flag), anything else = reject. Installs the scout data reading loop handler at &8102. |
|
| 80D0 | .nmi_rx_scout_net |
| BIT econet_control23_or_status2 ; BIT SR2: test for RDA (bit7 = data available) | |
| 80D3 | BPL scout_error ; No RDA -- check errors |
| 80D5 | LDA econet_data_continue_frame ; Read destination network byte |
| 80D8 | BEQ accept_local_net ; Network = 0 -- local network, accept |
| 80DA | EOR #&ff ; EOR &FF: test if network = &FF (broadcast) |
| 80DC | BEQ accept_scout_net ; Broadcast network -- accept |
| 80DE | .scout_reject←1← 80C4 BNE |
| LDA #&a2 ; Reject: wrong network. CR1=&A2: RIE|RX_DISCONTINUE | |
| 80E0 | STA econet_control1_or_status1 ; Write CR1 to discontinue RX |
| 80E3 | JMP set_nmi_rx_scout ; Return to idle scout listening |
| 80E6 | .accept_local_net←1← 80D8 BEQ |
| STA rx_src_net ; Network = 0 (local): clear tx_flags | |
| 80E9 | .accept_scout_net←1← 80DC BEQ |
| STA port_buf_len ; Store Y offset for scout data buffer | |
| 80EB | LDA #2 ; Install scout data handler (&8102) |
| 80ED | LDY #&81 ; High byte of scout data handler |
| 80EF | JMP set_nmi_vector ; Install scout data loop and RTI |
Scout error/discard handlerHandles scout reception errors and end-of-frame conditions. Reads SR2 and tests AP|RDA (bits 0|7): if neither set, the frame ended cleanly and is simply discarded. If unexpected data is present, performs a full ADLC reset. Also serves as the common discard path for address/network mismatches from nmi_rx_scout and scout_complete -- reached by 5 branch sites across the scout reception chain. |
|
| 80F2 | .scout_error←5← 80B8 BEQ← 80D3 BPL← 8107 BPL← 813B BEQ← 813D BPL |
| LDA econet_control23_or_status2 ; Read SR2 | |
| 80F5 | AND #&81 ; Test AP (b0) | RDA (b7) |
| 80F7 | BEQ scout_discard ; Neither set -- clean end, discard frame |
| 80F9 | JSR adlc_full_reset ; Unexpected data/status: full ADLC reset |
| 80FC | JMP set_nmi_rx_scout ; Discard and return to idle |
| 80FF | .scout_discard←1← 80F7 BEQ |
| JMP reset_adlc_rx_listen ; Gentle discard: RX_DISCONTINUE | |
| 8102 | LDY port_buf_len ; Y = buffer offset |
| 8104 | LDA econet_control23_or_status2 ; Read SR2 |
| 8107 | .scout_loop_rda←1← 8127 BNE |
| BPL scout_error ; No RDA -- error handler | |
| 8109 | LDA econet_data_continue_frame ; Read data byte from RX FIFO |
| 810C | STA scout_buf,y ; Store at &0D3D+Y (scout buffer) |
| 810F | INY ; Advance buffer index |
| 8110 | LDA econet_control23_or_status2 ; Read SR2 again (FV detection point) |
| 8113 | BMI scout_loop_second ; RDA set -- more data, read second byte |
| 8115 | BNE scout_complete ; SR2 non-zero (FV or other) -- scout completion |
| 8117 | .scout_loop_second←1← 8113 BMI |
| LDA econet_data_continue_frame ; Read second byte of pair | |
| 811A | STA scout_buf,y ; Store at &0D3D+Y |
| 811D | INY ; Advance and check buffer limit |
| 811E | CPY #&0c ; Copied all 12 scout bytes? |
| 8120 | BEQ scout_complete ; Buffer full (Y=12) -- force completion |
| 8122 | STY port_buf_len ; Save final buffer offset |
| 8124 | LDA econet_control23_or_status2 ; Read SR2 for next pair |
| 8127 | BNE scout_loop_rda ; SR2 non-zero -- loop back for more bytes |
| 8129 | JMP nmi_rti ; SR2 = 0 -- RTI, wait for next NMI |
Scout completion handlerProcesses a completed scout frame. Writes CR1=&00 and CR2=&84 to disable PSE and suppress FV, then tests SR2 for FV (frame valid). If FV is set with RDA, reads the remaining scout data bytes in pairs into the buffer at &0D3D. Matches the port byte (&0D40) against open receive control blocks to find a listener. On match, calculates the transfer size via tx_calc_transfer, sets up the data RX handler chain, and sends a scout ACK. On no match or error, discards the frame via scout_error. |
|
| 812C | .scout_complete←2← 8115 BNE← 8120 BEQ |
| LDA #0 ; Save Y for next iteration | |
| 812E | STA econet_control1_or_status1 ; Write CR1 |
| 8131 | LDA #&84 ; CR2=&84: disable PSE, enable RDA_SUPPRESS_FV |
| 8133 | STA econet_control23_or_status2 ; Write CR2 |
| 8136 | LDA #2 ; A=&02: FV mask for SR2 bit1 |
| 8138 | BIT econet_control23_or_status2 ; BIT SR2: test FV (Z) and RDA (N) |
| 813B | BEQ scout_error ; No FV -- not a valid frame end, error |
| 813D | BPL scout_error ; FV set but no RDA -- missing last byte, error |
| 813F | LDA econet_data_continue_frame ; Read last byte from RX FIFO |
| 8142 | STA scout_buf,y ; Store last byte at &0D3D+Y |
| 8145 | LDA #&44 ; CR1=&44: RX_RESET | TIE (switch to TX for ACK) |
| 8147 | STA econet_control1_or_status1 ; Write CR1: switch to TX mode |
| 814A | SEC ; Set bit7 of need_release_tube flag |
| 814B | ROR need_release_tube ; Rotate C=1 into bit7: mark Tube release needed |
| 814D | LDA scout_port ; Check port byte: 0 = immediate op, non-zero = data transfer |
| 8150 | BNE scout_match_port ; Port non-zero -- look for matching receive block |
| 8152 | .scout_no_match |
| JMP immediate_op ; Port = 0 -- immediate operation handler | |
| 8155 | .scout_match_port←1← 8150 BNE |
| BIT rx_src_net ; Check if broadcast (bit6 of tx_flags) | |
| 8158 | BVC scan_port_list ; Not broadcast -- skip CR2 setup |
| 815A | LDA #7 ; CR2=&07: broadcast prep |
| 815C | STA econet_control23_or_status2 ; Write CR2: broadcast frame prep |
| 815F | .scan_port_list←1← 8158 BVC |
| BIT econet_flags ; Check if RX port list active (bit7) | |
| 8162 | BPL try_nfs_port_list ; No active ports -- try NFS workspace |
| 8164 | LDA #&c0 ; Start scanning port list at page &C0 |
| 8166 | LDY #0 ; Y=0: start offset within each port slot |
| 8168 | .scan_nfs_port_list←1← 81AD BNE |
| STA port_ws_offset ; Store page to workspace pointer low | |
| 816A | STY rx_buf_offset ; Store page high byte for slot scanning |
| 816C | .check_port_slot←1← 819F BCC |
| LDY #0 ; Y=0: read control byte from start of slot | |
| 816E | .scout_ctrl_check |
| LDA (port_ws_offset),y ; Read port control byte from slot | |
| 8170 | BEQ discard_no_match ; Zero = end of port list, no match |
| 8172 | CMP #&7f ; &7F = any-port wildcard |
| 8174 | BNE next_port_slot ; Not wildcard -- check specific port match |
| 8176 | INY ; Y=1: advance to port byte in slot |
| 8177 | LDA (port_ws_offset),y ; Read port number from slot (offset 1) |
| 8179 | BEQ check_station_filter ; Zero port in slot = match any port |
| 817B | CMP scout_port ; Check if port matches this slot |
| 817E | BNE next_port_slot ; Port mismatch -- try next slot |
| 8180 | .check_station_filter←1← 8179 BEQ |
| INY ; Y=2: advance to station byte | |
| 8181 | LDA (port_ws_offset),y ; Read station filter from slot (offset 2) |
| 8183 | BEQ port_match_found ; Zero station = match any station, accept |
| 8185 | CMP scout_buf ; Check if source station matches |
| 8188 | BNE next_port_slot ; Station mismatch -- try next slot |
| 818A | .scout_port_match |
| INY ; Y=3: advance to network byte | |
| 818B | LDA (port_ws_offset),y ; Read network filter from slot (offset 3) |
| 818D | BEQ port_match_found ; Zero = accept any network |
| 818F | CMP scout_src_net ; Check if source network matches |
| 8192 | BEQ port_match_found ; Network matches or zero = accept |
| 8194 | .next_port_slot←3← 8174 BNE← 817E BNE← 8188 BNE |
| LDA rx_buf_offset ; Check if NFS workspace search pending | |
| 8196 | BEQ try_nfs_port_list ; No NFS workspace -- try fallback path |
| 8198 | LDA port_ws_offset ; Load current slot base address |
| 819A | CLC ; CLC for 12-byte slot advance |
| 819B | ADC #&0c ; Advance to next 12-byte port slot |
| 819D | STA port_ws_offset ; Update workspace pointer to next slot |
| 819F | BCC check_port_slot ; Always branches (page &C0 won't overflow) |
| 81A1 | .discard_no_match←2← 8170 BEQ← 81A7 BVC |
| JMP nmi_error_dispatch ; No match found -- discard frame | |
| 81A4 | .try_nfs_port_list←2← 8162 BPL← 8196 BEQ |
| BIT econet_flags ; Try NFS workspace if paged list exhausted | |
| 81A7 | BVC discard_no_match ; No NFS workspace RX (bit6 clear) -- discard |
| 81A9 | LDA #0 ; NFS workspace starts at offset 0 in page |
| 81AB | LDY nfs_workspace_hi ; NFS workspace high byte for port list |
| 81AD | BNE scan_nfs_port_list ; Scan NFS workspace port list |
| 81AF | .port_match_found←4← 8183 BEQ← 818D BEQ← 8192 BEQ← 84AC JMP |
| LDA #3 ; Match found: set scout_status = 3 | |
| 81B1 | STA rx_port ; Record match for completion handler |
| 81B4 | JSR tx_calc_transfer ; Calculate transfer parameters |
| 81B7 | BCC nmi_error_dispatch ; C=0: no Tube claimed -- discard |
| 81B9 | BIT rx_src_net ; Check broadcast flag for ACK path |
| 81BC | BVC send_data_rx_ack ; Not broadcast -- normal ACK path |
| 81BE | JMP copy_scout_to_buffer ; Broadcast: different completion path |
| 81C1 | .send_data_rx_ack←2← 81BC BVC← 84A1 JMP |
| LDA #&44 ; CR1=&44: RX_RESET | TIE | |
| 81C3 | STA econet_control1_or_status1 ; Write CR1: TX mode for ACK |
| 81C6 | LDA #&a7 ; CR2=&A7: RTS | CLR_TX_ST | FC_TDRA | PSE |
| 81C8 | STA econet_control23_or_status2 ; Write CR2: enable TX with PSE |
| 81CB | LDA #&d2 ; Install data_rx_setup at &81D2 |
| 81CD | LDY #&81 ; High byte of data_rx_setup handler |
| 81CF | JMP ack_tx_write_dest ; Send ACK with data_rx_setup as next NMI |
| 81D2 | .data_rx_setup |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (switch to RX for data frame) | |
| 81D4 | STA econet_control1_or_status1 ; Write CR1: switch to RX for data frame |
| 81D7 | LDA #&dc ; Install nmi_data_rx at &81DC |
| 81D9 | JMP install_nmi_handler ; Install nmi_data_rx and return from NMI |
Data frame RX handler (four-way handshake)Receives the data frame after the scout ACK has been sent. First checks AP (Address Present) for the start of the data frame. Reads and validates the first two address bytes (dest_stn, dest_net) against our station address, then installs continuation handlers to read the remaining data payload into the open port buffer. Handler chain: &81DC (AP+addr check) -> &81F0 (net=0 check) -> &8206 (skip ctrl+port) -> &8239 (bulk data read) -> &826D (completion) |
|
| 81DC | .nmi_data_rx |
| LDA #1 ; A=1: AP mask for SR2 bit test | |
| 81DE | BIT econet_control23_or_status2 ; BIT SR2: test AP bit |
| 81E1 | BEQ nmi_error_dispatch ; No AP: wrong frame or error |
| 81E3 | LDA econet_data_continue_frame ; Read first byte (dest station) |
| 81E6 | CMP station_id_disable_net_nmis ; Compare to our station ID (INTOFF) |
| 81E9 | BNE nmi_error_dispatch ; Not for us: error path |
| 81EB | LDA #&f0 ; Install net check handler at &81F0 |
| 81ED | JMP install_nmi_handler ; Set NMI vector via RAM shim |
| 81F0 | .nmi_data_rx_net |
| BIT econet_control23_or_status2 ; Validate source network = 0 | |
| 81F3 | BPL nmi_error_dispatch ; SR2 bit7 clear: no data ready -- error |
| 81F5 | LDA econet_data_continue_frame ; Read dest network byte |
| 81F8 | BNE nmi_error_dispatch ; Network != 0: wrong network -- error |
| 81FA | LDA #6 ; Install skip handler at &8206 |
| 81FC | LDY #&82 ; High byte of &8206 handler |
| 81FE | BIT econet_control1_or_status1 ; SR1 bit7: IRQ, data already waiting |
| 8201 | BMI nmi_data_rx_skip ; Data ready: skip directly, no RTI |
| 8203 | JMP set_nmi_vector ; Install handler and return via RTI |
| 8206 | .nmi_data_rx_skip←1← 8201 BMI |
| BIT econet_control23_or_status2 ; Skip control and port bytes (already known from scout) | |
| 8209 | BPL nmi_error_dispatch ; SR2 bit7 clear: error |
| 820B | LDA econet_data_continue_frame ; Discard control byte |
| 820E | LDA econet_data_continue_frame ; Discard port byte |
| fall through ↓ | |
Install data RX bulk or Tube handlerSelects between the normal bulk RX handler (&8239) and the Tube RX handler based on bit 1 of rx_src_net (tx_flags). If normal mode, loads the handler address &8239 and checks SR1 bit 7: if IRQ is already asserted (more data waiting), jumps directly to nmi_data_rx_bulk to avoid NMI re-entry overhead. Otherwise installs the handler via set_nmi_vector and returns via RTI. |
|
| 8211 | .install_data_rx_handler←1← 88BC JMP |
| LDA #2 ; A=2: Tube transfer flag mask | |
| 8213 | BIT rx_src_net ; Check if Tube transfer active |
| 8216 | BNE install_tube_rx ; Tube active: use Tube RX path |
| 8218 | LDA #&39 ; Install bulk read at &8239 |
| 821A | LDY #&82 ; High byte of &8239 handler |
| 821C | BIT econet_control1_or_status1 ; SR1 bit7: more data already waiting? |
| 821F | BMI nmi_data_rx_bulk ; Yes: enter bulk read directly |
| 8221 | JMP set_nmi_vector ; No: install handler and RTI |
| 8224 | .install_tube_rx←1← 8216 BNE |
| LDA #&96 ; Tube: install Tube RX at &8296 | |
| 8226 | LDY #&82 ; High byte of &8296 handler |
| 8228 | JMP set_nmi_vector ; Install Tube handler and RTI |
NMI error handler dispatchCommon error/abort entry used by 12 call sites. Checks tx_flags bit 7: if clear, does a full ADLC reset and returns to idle listen (RX error path); if set, jumps to tx_result_fail (TX not-listening path). |
|
| 822B | .nmi_error_dispatch←12← 81A1 JMP← 81B7 BCC← 81E1 BEQ← 81E9 BNE← 81F3 BPL← 81F8 BNE← 8209 BPL← 824C BEQ← 827E BEQ← 8284 BEQ← 8341 JMP← 847B JMP |
| LDA rx_src_net ; Check tx_flags for error path | |
| 822E | BPL rx_error_reset ; Bit7 clear: RX error path |
| 8230 | JMP tx_result_fail ; Bit7 set: TX result = not listening |
| 8233 | .rx_error_reset←1← 822E BPL |
| JSR adlc_full_reset ; Full ADLC reset on RX error | |
| 8236 | JMP discard_reset_rx ; Discard and return to idle listen |
Data frame bulk read loopReads data payload bytes from the RX FIFO and stores them into the open port buffer at (open_port_buf),Y. Reads bytes in pairs (like the scout data loop), checking SR2 between each pair. SR2 non-zero (FV or other) -> frame completion at &826D. SR2 = 0 -> RTI, wait for next NMI to continue. |
|
| 8239 | .nmi_data_rx_bulk←1← 821F BMI |
| LDY port_buf_len ; Y = buffer offset, resume from last position | |
| 823B | LDA econet_control23_or_status2 ; Read SR2 for next pair |
| 823E | .data_rx_loop←1← 8268 BNE |
| BPL data_rx_complete ; SR2 bit7 clear: frame complete (FV) | |
| 8240 | LDA econet_data_continue_frame ; Read first byte of pair from RX FIFO |
| 8243 | STA (open_port_buf),y ; Store byte to buffer |
| 8245 | INY ; Advance buffer offset |
| 8246 | BNE read_sr2_between_pairs ; Y != 0: no page boundary crossing |
| 8248 | INC open_port_buf_hi ; Crossed page: increment buffer high byte |
| 824A | DEC port_buf_len_hi ; Decrement remaining page count |
| 824C | BEQ nmi_error_dispatch ; No pages left: handle as complete |
| 824E | .read_sr2_between_pairs←1← 8246 BNE |
| LDA econet_control23_or_status2 ; Read SR2 between byte pairs | |
| 8251 | BMI read_second_rx_byte ; SR2 bit7 set: more data available |
| 8253 | BNE data_rx_complete ; SR2 non-zero, bit7 clear: frame done |
| 8255 | .read_second_rx_byte←1← 8251 BMI |
| LDA econet_data_continue_frame ; Read second byte of pair from RX FIFO | |
| 8258 | STA (open_port_buf),y ; Store byte to buffer |
| 825A | INY ; Advance buffer offset |
| 825B | STY port_buf_len ; Save updated buffer position |
| 825D | BNE check_sr2_loop_again ; Y != 0: no page boundary crossing |
| 825F | INC open_port_buf_hi ; Crossed page: increment buffer high byte |
| 8261 | DEC port_buf_len_hi ; Decrement remaining page count |
| 8263 | BEQ data_rx_complete ; No pages left: frame complete |
| 8265 | .check_sr2_loop_again←1← 825D BNE |
| LDA econet_control23_or_status2 ; Read SR2 for next iteration | |
| 8268 | BNE data_rx_loop ; SR2 non-zero: more data, loop back |
| 826A | JMP nmi_rti ; SR2=0: no more data yet, wait for NMI |
Data frame completion |
|
| 826D | .data_rx_complete←3← 823E BPL← 8253 BNE← 8263 BEQ |
| LDA #&84 ; CR1=&00: disable all interrupts | |
| 826F | STA econet_control23_or_status2 ; Write CR2: disable PSE for bit testing |
| 8272 | LDA #0 ; CR2=&84: disable PSE for individual bit testing |
| 8274 | STA econet_control1_or_status1 ; Write CR1: disable all interrupts |
| 8277 | STY port_buf_len ; Save Y (byte count from data RX loop) |
| 8279 | LDA #2 ; A=&02: FV mask |
| 827B | BIT econet_control23_or_status2 ; BIT SR2: test FV (Z) and RDA (N) |
| 827E | BEQ nmi_error_dispatch ; No FV -- error |
| 8280 | BPL send_ack ; FV set, no RDA -- proceed to ACK |
| 8282 | LDA port_buf_len_hi ; Check if buffer space remains |
| 8284 | .read_last_rx_byte←3← 82A1 BEQ← 82C8 BEQ← 82D4 BEQ |
| BEQ nmi_error_dispatch ; No buffer space: error/discard frame | |
| 8286 | LDA econet_data_continue_frame ; FV+RDA: read and store last data byte |
| 8289 | LDY port_buf_len ; Y = current buffer write offset |
| 828B | STA (open_port_buf),y ; Store last byte in port receive buffer |
| 828D | INC port_buf_len ; Advance buffer write offset |
| 828F | BNE send_ack ; No page wrap: proceed to send ACK |
| 8291 | INC open_port_buf_hi ; Page boundary: advance buffer page |
| 8293 | .send_ack←2← 8280 BPL← 828F BNE |
| JMP ack_tx ; Send ACK frame to complete handshake | |
| 8296 | .nmi_data_rx_tube |
| LDA econet_control23_or_status2 ; Read SR2 for Tube data receive path | |
| 8299 | .rx_tube_data←1← 82B4 BNE |
| BPL data_rx_tube_complete ; RDA clear: no more data, frame complete | |
| 829B | LDA econet_data_continue_frame ; Read data byte from ADLC RX FIFO |
| 829E | JSR advance_buffer_ptr ; Check buffer limits and transfer size |
| 82A1 | BEQ read_last_rx_byte ; Zero: buffer full, handle as error |
| 82A3 | STA tube_data_register_3 ; Send byte to Tube data register 3 |
| 82A6 | LDA econet_data_continue_frame ; Read second data byte (paired transfer) |
| 82A9 | STA tube_data_register_3 ; Send second byte to Tube |
| 82AC | JSR advance_buffer_ptr ; Check limits after byte pair |
| 82AF | BEQ data_rx_tube_complete ; Zero: Tube transfer complete |
| 82B1 | LDA econet_control23_or_status2 ; Re-read SR2 for next byte pair |
| 82B4 | BNE rx_tube_data ; More data available: continue loop |
| 82B6 | .data_rx_tube_error |
| JMP nmi_rti ; Unexpected end: return from NMI | |
| 82B9 | .data_rx_tube_complete←2← 8299 BPL← 82AF BEQ |
| LDA #0 ; CR1=&00: disable all interrupts | |
| 82BB | STA econet_control1_or_status1 ; Write CR1 for individual bit testing |
| 82BE | LDA #&84 ; CR2=&84: disable PSE |
| 82C0 | STA econet_control23_or_status2 ; Write CR2: same pattern as main path |
| 82C3 | LDA #2 ; A=&02: FV mask for Tube completion |
| 82C5 | BIT econet_control23_or_status2 ; BIT SR2: test FV (Z) and RDA (N) |
| 82C8 | BEQ read_last_rx_byte ; No FV: incomplete frame, error |
| 82CA | BPL ack_tx ; FV set, no RDA: proceed to ACK |
| 82CC | LDA port_buf_len ; Check if any buffer was allocated |
| 82CE | ORA port_buf_len_hi ; OR all 4 buffer pointer bytes together |
| 82D0 | ORA open_port_buf ; Check buffer low byte |
| 82D2 | ORA open_port_buf_hi ; Check buffer high byte |
| 82D4 | BEQ read_last_rx_byte ; All zero (null buffer): error |
| 82D6 | LDA econet_data_continue_frame ; Read extra trailing byte from FIFO |
| 82D9 | STA rx_extra_byte ; Save extra byte at &0D5D for later use |
| 82DC | LDA #&20 ; Bit5 = extra data byte available flag |
| 82DE | ORA rx_src_net ; Set extra byte flag in tx_flags |
| 82E1 | STA rx_src_net ; Store updated flags |
| fall through ↓ | |
ACK transmissionSends a scout ACK or final ACK frame as part of the four-way handshake. If bit7 of &0D4A is set, this is a final ACK -> completion (&88C6). Otherwise, configures for TX (CR1=&44, CR2=&A7) and sends the ACK frame (dst_stn, dst_net from &0D3D, src_stn from &FE18, src_net=0). The ACK frame has no data payload -- just address bytes. After writing the address bytes to the TX FIFO, installs the next NMI handler from &0D4B/&0D4C (saved by the scout/data RX handler) and sends TX_LAST_DATA (CR2=&3F) to close the frame. |
|
| 82E4 | .ack_tx←2← 8293 JMP← 82CA BPL |
| LDA rx_src_net ; Load TX flags to check ACK type | |
| 82E7 | BPL ack_tx_configure ; Bit7 clear: normal scout ACK |
| 82E9 | JSR advance_rx_buffer_ptr ; Final ACK: call completion handler |
| 82EC | JMP tx_result_ok ; Jump to TX success result |
| 82EF | .ack_tx_configure←1← 82E7 BPL |
| LDA #&44 ; CR1=&44: RX_RESET | TIE (switch to TX mode) | |
| 82F1 | STA econet_control1_or_status1 ; Write CR1: switch to TX mode |
| 82F4 | LDA #&a7 ; CR2=&A7: RTS|CLR_TX_ST|FC_TDRA|2_1_BYTE|PSE |
| 82F6 | STA econet_control23_or_status2 ; Write CR2: enable TX with status clear |
| 82F9 | LDA #&8b ; Install saved next handler (&838B for scout ACK) |
| 82FB | LDY #&83 ; High byte of post-ACK handler |
| 82FD | .ack_tx_write_dest←2← 81CF JMP← 84E9 JMP |
| STA saved_nmi_lo ; Store next handler low byte | |
| 8300 | STY saved_nmi_hi ; Store next handler high byte |
| 8303 | LDA scout_buf ; Load dest station from RX scout buffer |
| 8306 | BIT econet_control1_or_status1 ; BIT SR1: test TDRA (V=bit6) |
| 8309 | BVC dispatch_nmi_error ; TDRA not ready -- error |
| 830B | STA econet_data_continue_frame ; Write dest station to TX FIFO |
| 830E | LDA scout_src_net ; Write dest network to TX FIFO |
| 8311 | STA econet_data_continue_frame ; Write dest net byte to FIFO |
| 8314 | LDA #&1b ; Install handler at &831B (write src addr) |
| 8316 | LDY #&83 ; High byte of nmi_ack_tx_src |
| 8318 | JMP set_nmi_vector ; Set NMI vector to ack_tx_src handler |
ACK TX continuationContinuation of ACK frame transmission. Reads our station ID from &FE18 (INTOFF side effect), tests TDRA via SR1, and writes station + network=0 to the TX FIFO, completing the 4-byte ACK address header. Then checks rx_src_net bit 7: if set, branches to start_data_tx to begin the data phase. Otherwise writes CR2=&3F (TX_LAST_DATA) and falls through to post_ack_scout for scout processing. |
|
| 831B | .nmi_ack_tx_src |
| LDA station_id_disable_net_nmis ; Load our station ID (also INTOFF) | |
| 831E | BIT econet_control1_or_status1 ; BIT SR1: test TDRA |
| 8321 | BVC dispatch_nmi_error ; TDRA not ready -- error |
| 8323 | STA econet_data_continue_frame ; Write our station to TX FIFO |
| 8326 | LDA #0 ; Write network=0 to TX FIFO |
| 8328 | STA econet_data_continue_frame ; Write network=0 (local) to TX FIFO |
| 832B | LDA rx_src_net ; Check tx_flags for data phase |
| 832E | BMI start_data_tx ; bit7 set: start data TX phase |
| 8330 | LDA #&3f ; CR2=&3F: TX_LAST_DATA | CLR_RX_ST | FLAG_IDLE | FC_TDRA | 2_1_BYTE | PSE |
| fall through ↓ | |
Post-ACK scout processingCalled after the scout ACK has been transmitted. Processes the received scout data stored in the buffer at &0D3D-&0D48. Checks the port byte (&0D40) against open receive blocks to find a matching listener. If a match is found, sets up the data RX handler chain for the four-way handshake data phase. If no match, discards the frame. |
|
| 8332 | .post_ack_scout |
| STA econet_control23_or_status2 ; Write CR2 to clear status after ACK TX | |
| 8335 | LDA saved_nmi_lo ; Install saved handler from &0D4B/&0D4C |
| 8338 | LDY saved_nmi_hi ; Load saved next handler high byte |
| 833B | JMP set_nmi_vector ; Install next NMI handler |
| 833E | .start_data_tx←1← 832E BMI |
| JMP data_tx_begin ; Jump to start data TX phase | |
| 8341 | .dispatch_nmi_error←2← 8309 BVC← 8321 BVC |
| JMP nmi_error_dispatch ; Jump to error handler | |
Advance RX buffer pointer after transferAdds the transfer count to the RXCB buffer pointer (4-byte addition). If a Tube transfer is active, re-claims the Tube address and sends the extra RX byte via R3, incrementing the Tube pointer by 1. |
|
| 8344 | .advance_rx_buffer_ptr←2← 82E9 JSR← 839A JSR |
| LDA #2 ; A=2: test bit1 of tx_flags | |
| 8346 | BIT rx_src_net ; BIT tx_flags: check data transfer bit |
| 8349 | BEQ return_rx_complete ; Bit1 clear: no transfer -- return |
| 834B | CLC ; CLC: init carry for 4-byte add |
| 834C | PHP ; Save carry on stack for loop |
| 834D | LDY #8 ; Y=8: RXCB high pointer offset |
| 834F | .add_rxcb_ptr←1← 835B BCC |
| LDA (port_ws_offset),y ; Load RXCB[Y] (buffer pointer byte) | |
| 8351 | PLP ; Restore carry from stack |
| 8352 | ADC net_tx_ptr,y ; Add transfer count byte |
| 8355 | STA (port_ws_offset),y ; Store updated pointer back to RXCB |
| 8357 | INY ; Next byte |
| 8358 | PHP ; Save carry for next iteration |
| 8359 | CPY #&0c ; Done 4 bytes? (Y reaches &0C) |
| 835B | BCC add_rxcb_ptr ; No: continue adding |
| 835D | PLP ; Discard final carry |
| 835E | LDA #&20 ; A=&20: test bit5 of tx_flags |
| 8360 | BIT rx_src_net ; BIT tx_flags: check Tube bit |
| 8363 | BEQ skip_tube_update ; No Tube: skip Tube update |
| 8365 | TXA ; Save X on stack |
| 8366 | PHA ; Push X |
| 8367 | LDA #8 ; A=8: offset for Tube address |
| 8369 | CLC ; CLC for address calculation |
| 836A | ADC port_ws_offset ; Add workspace base offset |
| 836C | TAX ; X = address low for Tube claim |
| 836D | LDY rx_buf_offset ; Y = address high for Tube claim |
| 836F | LDA #1 ; A=1: Tube claim type (read) |
| 8371 | JSR tube_addr_data_dispatch ; Claim Tube address for transfer |
| 8374 | LDA rx_extra_byte ; Load extra RX data byte |
| 8377 | STA tube_data_register_3 ; Send to Tube via R3 |
| 837A | SEC ; SEC: init carry for increment |
| 837B | LDY #8 ; Y=8: start at high pointer |
| 837D | .inc_rxcb_ptr←1← 8384 BCS |
| LDA #0 ; A=0: add carry only (increment) | |
| 837F | ADC (port_ws_offset),y ; Add carry to pointer byte |
| 8381 | STA (port_ws_offset),y ; Store back to RXCB |
| 8383 | INY ; Next byte |
| 8384 | BCS inc_rxcb_ptr ; Keep going while carry propagates |
| 8386 | PLA ; Restore X from stack |
| 8387 | TAX ; Transfer to X register |
| 8388 | .skip_tube_update←1← 8363 BEQ |
| LDA #&ff ; A=&FF: return value (transfer done) | |
| 838A | .return_rx_complete←1← 8349 BEQ |
| RTS ; Return | |
| 838B | LDA scout_port ; Load received port byte |
| 838E | BNE rx_complete_update_rxcb ; Port != 0: data transfer frame |
| 8390 | LDY scout_ctrl ; Port=0: load control byte |
| 8393 | CPY #&82 ; Ctrl = &82 (POKE)? |
| 8395 | BEQ rx_complete_update_rxcb ; Yes: POKE also needs data transfer |
| 8397 | JMP imm_op_build_reply ; Other port-0 ops: immediate dispatch |
Complete RX and update RXCBFinalises a received data transfer. Calls advance_rx_buffer_ptr to update the 4-byte buffer pointer with the transfer count (and handle Tube re-claim if needed). Adds the buffer bytes remaining to the base address, then subtracts 8 from the RXCB buffer length to account for the scout overhead. Clears the RXCB flag byte and sends the final ACK via ack_tx. On Tube transfers, releases the Tube claim before resetting to idle listen. |
|
| 839A | .rx_complete_update_rxcb←3← 838E BNE← 8395 BEQ← 842A JMP |
| JSR advance_rx_buffer_ptr ; Update buffer pointer and check for Tube | |
| 839D | BNE skip_buf_ptr_update ; Transfer not done: skip buffer update |
| 839F | .add_buf_to_base |
| LDA port_buf_len ; Load buffer bytes remaining | |
| 83A1 | CLC ; CLC for address add |
| 83A2 | ADC open_port_buf ; Add to buffer base address |
| 83A4 | BCC store_buf_ptr_lo ; No carry: skip high byte increment |
| 83A6 | .inc_rxcb_buf_hi |
| INC open_port_buf_hi ; Carry: increment buffer high byte | |
| 83A8 | .store_buf_ptr_lo←1← 83A4 BCC |
| LDY #8 ; Y=8: store updated buffer position | |
| 83AA | .store_rxcb_buf_ptr |
| STA (port_ws_offset),y ; Store updated low byte to RXCB | |
| 83AC | INY ; Y=9: buffer high byte offset |
| 83AD | LDA open_port_buf_hi ; Load updated buffer high byte |
| 83AF | .store_rxcb_buf_hi |
| STA (port_ws_offset),y ; Store high byte to RXCB | |
| 83B1 | .skip_buf_ptr_update←1← 839D BNE |
| LDA scout_port ; Check port byte again | |
| 83B4 | BEQ discard_reset_rx ; Port=0: immediate op, discard+listen |
| 83B6 | LDA scout_src_net ; Load source network from scout buffer |
| 83B9 | LDY #3 ; Y=3: RXCB source network offset |
| 83BB | STA (port_ws_offset),y ; Store source network to RXCB |
| 83BD | DEY ; Y=2: source station offset Y=&02 |
| 83BE | LDA scout_buf ; Load source station from scout buffer |
| 83C1 | STA (port_ws_offset),y ; Store source station to RXCB |
| 83C3 | DEY ; Y=1: port byte offset Y=&01 |
| 83C4 | LDA scout_port ; Load port byte |
| 83C7 | STA (port_ws_offset),y ; Store port to RXCB |
| 83C9 | DEY ; Y=0: control/flag byte offset Y=&00 |
| 83CA | LDA scout_ctrl ; Load control byte from scout |
| 83CD | ORA #&80 ; Set bit7 = reception complete flag |
| 83CF | STA (port_ws_offset),y ; Store to RXCB (marks CB as complete) |
| 83D1 | LDA fs_flags ; Load callback event flags |
| 83D4 | ROR ; Shift bit 0 into carry |
| 83D5 | BCC discard_reset_rx ; Bit 0 clear: no callback, skip to reset |
| 83D7 | SEC ; Set carry for subtraction |
| 83D8 | LDA port_ws_offset ; Load RXCB workspace pointer low byte |
| 83DA | .loop_count_rxcb_slot←1← 83DD BCS |
| INY ; Count slots | |
| 83DB | SBC #&0c ; Subtract 12 bytes per RXCB slot |
| 83DD | BCS loop_count_rxcb_slot ; Loop until pointer exhausted |
| 83DF | DEY ; Adjust for off-by-one |
| 83E0 | CPY #3 ; Check slot index >= 3 |
| 83E2 | BCC discard_reset_rx ; Slot < 3: no callback, skip to reset |
| 83E4 | JSR discard_reset_listen ; Discard scout and reset listen state |
| 83E7 | TYA ; Pass slot index as callback parameter |
| 83E8 | JMP setup_sr_tx ; Jump to TX completion with slot index |
| 83EB | .discard_reset_rx←6← 8236 JMP← 83B4 BEQ← 83D5 BCC← 83E2 BCC← 886B JMP← 88D5 JMP |
| JSR discard_reset_listen ; Discard scout and reset RX listen | |
| 83EE | .reset_adlc_rx_listen←3← 80FF JMP← 8466 BCS← 8522 JMP |
| JSR adlc_rx_listen ; Reset ADLC and return to RX listen | |
| 83F1 | .set_nmi_rx_scout←2← 80E3 JMP← 80FC JMP |
| LDA #&b3 ; A=&B3: low byte of nmi_rx_scout | |
| 83F3 | LDY #&80 ; Y=&80: high byte of nmi_rx_scout |
| 83F5 | JMP set_nmi_vector ; Install nmi_rx_scout as NMI handler |
Discard with Tube releaseChecks whether a Tube transfer is active by ANDing bit 1 of l0d63 with rx_src_net (tx_flags). If a Tube claim is held, calls release_tube to free it before returning. Used as the clean-up path after RXCB completion and after ADLC reset to ensure no stale Tube claims persist. |
|
| 83F8 | .discard_reset_listen←2← 83E4 JSR← 83EB JSR |
| LDA #2 ; Tube flag bit 1 AND tx_flags bit 1 | |
| 83FA | AND tube_present ; Check if Tube transfer active |
| 83FD | BIT rx_src_net ; Test tx_flags for Tube transfer |
| 8400 | BEQ return_from_discard_reset ; No Tube transfer active -- skip release |
| 8402 | JSR release_tube ; Release Tube claim before discarding |
| 8405 | .return_from_discard_reset←1← 8400 BEQ |
| RTS ; Return | |
Copy scout data to port bufferCopies scout data bytes (offsets 4-11) from the RX scout buffer at &0D3D into the open port buffer. Checks bit 1 of rx_src_net (tx_flags) to select the write path: direct memory store via (open_port_buf),Y for normal transfers, or Tube data register 3 write for Tube transfers. Calls advance_buffer_ptr after each byte. Falls through to release_tube on completion. Handles page overflow (Y wrap) by branching to scout_page_overflow. |
|
| 8406 | .copy_scout_to_buffer←1← 81BE JMP |
| TXA ; Save X on stack | |
| 8407 | PHA ; Push X |
| 8408 | LDX #4 ; X=4: start at scout byte offset 4 |
| 840A | LDA #2 ; A=2: Tube transfer check mask |
| 840C | .copy_scout_select |
| BIT rx_src_net ; BIT tx_flags: check Tube bit | |
| 840F | BNE copy_scout_via_tube ; Tube active: use R3 write path |
| 8411 | LDY port_buf_len ; Y = current buffer position |
| 8413 | .copy_scout_bytes←1← 8426 BNE |
| LDA scout_buf,x ; Load scout data byte | |
| 8416 | STA (open_port_buf),y ; Store to port buffer |
| 8418 | INY ; Advance buffer pointer |
| 8419 | BNE next_scout_byte ; No page crossing |
| 841B | INC open_port_buf_hi ; Page crossing: inc buffer high byte |
| 841D | DEC port_buf_len_hi ; Decrement remaining page count |
| 841F | BEQ scout_page_overflow ; No pages left: overflow |
| 8421 | .next_scout_byte←1← 8419 BNE |
| INX ; Next scout data byte | |
| 8422 | STY port_buf_len ; Save updated buffer position |
| 8424 | CPX #&0c ; Done all scout data? (X reaches &0C) |
| 8426 | BNE copy_scout_bytes ; No: continue copying |
| 8428 | .scout_copy_done←2← 843D BEQ← 8477 BEQ |
| PLA ; Restore X from stack | |
| 8429 | TAX ; Transfer to X register |
| 842A | JMP rx_complete_update_rxcb ; Jump to completion handler |
| 842D | .copy_scout_via_tube←2← 840F BNE← 843B BNE |
| LDA scout_buf,x ; Tube path: load scout data byte | |
| 8430 | STA tube_data_register_3 ; Send byte to Tube via R3 |
| 8433 | JSR advance_buffer_ptr ; Increment buffer position counters |
| 8436 | BEQ check_scout_done ; Counter overflow: handle end of buffer |
| 8438 | INX ; Next scout data byte |
| 8439 | CPX #&0c ; Done all scout data? |
| 843B | BNE copy_scout_via_tube ; No: continue Tube writes |
| 843D | BEQ scout_copy_done ; ALWAYS branch |
Release Tube co-processor claimTests need_release_tube (&98) bit 7: if set, the Tube has already been released and the subroutine just clears the flag. If clear (Tube claim held), calls tube_addr_data_dispatch with A=&82 to release the claim, then clears the release flag via LSR (which shifts bit 7 to 0). Called after completed RX transfers and during discard paths to ensure no stale Tube claims persist. |
|
| 843F | .release_tube←2← 8402 JSR← 8934 JSR |
| BIT need_release_tube ; Check if Tube needs releasing | |
| 8441 | BMI clear_release_flag ; Bit7 set: already released |
| 8443 | LDA #&82 ; A=&82: Tube release claim type |
| 8445 | JSR tube_addr_data_dispatch ; Release Tube address claim |
| 8448 | .clear_release_flag←1← 8441 BMI |
| LSR need_release_tube ; Clear release flag (LSR clears bit7) | |
| 844A | RTS ; Return |
Immediate operation handler (port = 0)Checks the control byte at l0d30 for immediate operation codes (&81-&88). Codes below &81 or above &88 are out of range and discarded. Codes &87-&88 (HALT/CONTINUE) bypass the protection mask check. For &81-&86, converts to a 0-based index and tests against the immediate operation mask at &0D61 to determine if this station accepts the operation. If accepted, dispatches via the immediate operation table. Builds the reply by storing data length, station/network, and control byte into the RX buffer. |
|
| 844B | .immediate_op←1← 8152 JMP |
| LDY scout_ctrl ; Control byte &81-&88 range check | |
| 844E | CPY #&81 ; Below &81: not an immediate op |
| 8450 | BCC imm_op_out_of_range ; Out of range low: jump to discard |
| 8452 | CPY #&89 ; Above &88: not an immediate op |
| 8454 | BCS imm_op_out_of_range ; Out of range high: jump to discard |
| 8456 | CPY #&87 ; HALT(&87)/CONTINUE(&88) skip protection |
| 8458 | BCS dispatch_imm_op ; Ctrl >= &87: dispatch without mask check |
| 845A | TYA ; Convert ctrl byte to 0-based index for mask |
| 845B | SEC ; SEC for subtract |
| 845C | SBC #&81 ; A = ctrl - &81 (0-based operation index) |
| 845E | TAY ; Y = index for mask rotation count |
| 845F | LDA ws_0d68 ; Load protection mask from LSTAT |
| 8462 | .rotate_prot_mask←1← 8464 BPL |
| ROR ; Rotate mask right by control byte index | |
| 8463 | DEY ; Decrement rotation counter |
| 8464 | BPL rotate_prot_mask ; Loop until bit aligned |
| 8466 | BCS reset_adlc_rx_listen ; Bit set = operation disabled, discard |
| 8468 | .dispatch_imm_op←1← 8458 BCS |
| LDY scout_ctrl ; Reload ctrl byte for dispatch table | |
| 846B | LDA #&84 ; Hi byte: all handlers are in page &84 |
| 846D | PHA ; Push hi byte for PHA/PHA/RTS dispatch |
| 846E | LDA imm_op_dispatch_lo-&81,y ; Load handler low byte from jump table |
| 8471 | PHA ; Push handler low byte |
| 8472 | RTS ; RTS dispatches to handler |
| 8473 | .scout_page_overflow←1← 841F BEQ |
| INC port_buf_len ; Increment port buffer length | |
| 8475 | .check_scout_done←1← 8436 BEQ |
| CPX #&0b ; Check if scout data index reached 11 | |
| 8477 | BEQ scout_copy_done ; Yes: loop back to continue reading |
| 8479 | PLA ; Restore A from stack |
| 847A | TAX ; Transfer to X |
| 847B | .imm_op_out_of_range←2← 8450 BCC← 8454 BCS |
| JMP nmi_error_dispatch ; Jump to discard handler | |
| 847E | .imm_op_dispatch_lo |
| EQUB <(rx_imm_peek-1) ; Ctrl &81: PEEK | |
| 847F | EQUB <(rx_imm_poke-1) ; Ctrl &82: POKE |
| 8480 | EQUB <(rx_imm_exec-1) ; Ctrl &83: JSR |
| 8481 | EQUB <(rx_imm_exec-1) ; Ctrl &84: UserProc |
| 8482 | EQUB <(rx_imm_exec-1) ; Ctrl &85: OSProc |
| 8483 | EQUB <(rx_imm_halt_cont-1) ; Ctrl &86: HALT |
| 8484 | EQUB <(rx_imm_halt_cont-1) ; Ctrl &87: CONTINUE |
| 8485 | EQUB <(rx_imm_machine_type-1) ; Ctrl &88: machine type query |
RX immediate: JSR/UserProc/OSProc setup |
|
| 8486 | .rx_imm_exec |
| LDA #0 ; A=0: port buffer lo at page boundary | |
| 8488 | STA open_port_buf ; Set port buffer lo |
| 848A | LDA #&82 ; Buffer length lo = &82 |
| 848C | STA port_buf_len ; Set buffer length lo |
| 848E | LDA #1 ; Buffer length hi = 1 |
| 8490 | STA port_buf_len_hi ; Set buffer length hi |
| 8492 | LDA net_rx_ptr_hi ; Load RX page hi for buffer |
| 8494 | STA open_port_buf_hi ; Set port buffer hi |
| 8496 | LDY #1 ; Y=1: copy 2 bytes (1 down to 0) |
| 8498 | .copy_addr_loop←1← 849F BPL |
| LDA scout_data,y ; Load remote address byte | |
| 849B | STA exec_addr_lo,y ; Store to exec address workspace |
| 849E | DEY ; Next byte (descending) |
| 849F | BPL copy_addr_loop ; Loop until all 4 bytes copied |
| 84A1 | .jmp_send_data_rx_ack |
| JMP send_data_rx_ack ; Enter common data-receive path Svc 5 dispatch table low bytes | |
RX immediate: POKE setupSets up workspace offsets for receiving POKE data. port_ws_offset=&2E, rx_buf_offset=&0D, then jumps to the common data-receive path at c81af. |
|
| 84A4 | .rx_imm_poke |
| LDA #&2e ; Port workspace offset = &3D | |
| 84A6 | STA port_ws_offset ; Store workspace offset lo |
| 84A8 | LDA #&0d ; RX buffer page = &0D |
| 84AA | STA rx_buf_offset ; Store workspace offset hi |
| 84AC | JMP port_match_found ; Enter POKE data-receive path |
RX immediate: machine type querySets up a buffer at &88C1 (length #&01FC) for the machine type query response. Falls through to set_rx_buf_len_hi to configure buffer dimensions, then branches to set_tx_reply_flag. |
|
| 84AF | .rx_imm_machine_type |
| LDA #1 ; Buffer length hi = 1 | |
| 84B1 | .set_rx_buf_len_hi←1← 8064 LDA |
| STA port_buf_len_hi ; Set buffer length hi | |
| 84B3 | LDA #&fc ; Buffer length lo = &FC |
| 84B5 | STA port_buf_len ; Set buffer length lo |
| 84B7 | LDA #&c1 ; Buffer start lo = &25 |
| 84B9 | STA open_port_buf ; Set port buffer lo |
| 84BB | LDA #&88 ; Buffer hi = &7F (below screen) |
| 84BD | STA open_port_buf_hi ; Set port buffer hi |
| 84BF | BNE set_tx_reply_flag ; ALWAYS branch |
RX immediate: PEEK setupWrites &0D2E to port_ws_offset/rx_buf_offset, sets scout_status=2, then calls tx_calc_transfer to send the PEEK response data back to the requesting station. |
|
| 84C1 | .rx_imm_peek |
| LDA #&2e ; Port workspace offset = &3D | |
| 84C3 | STA port_ws_offset ; Store workspace offset lo |
| 84C5 | LDA #&0d ; RX buffer page = &0D |
| 84C7 | STA rx_buf_offset ; Store workspace offset hi |
| 84C9 | LDA #2 ; Scout status = 2 (PEEK response) |
| 84CB | STA rx_port ; Store scout status |
| 84CE | JSR tx_calc_transfer ; Calculate transfer size for response |
| 84D1 | BCC imm_op_discard ; C=0: transfer not set up, discard |
| 84D3 | .set_tx_reply_flag←1← 84BF BNE |
| LDA rx_src_net ; Mark TX flags bit 7 (reply pending) | |
| 84D6 | ORA #&80 ; Set reply pending flag |
| 84D8 | STA rx_src_net ; Store updated TX flags |
| 84DB | .rx_imm_halt_cont |
| LDA #&44 ; CR1=&44: TIE | TX_LAST_DATA | |
| 84DD | STA econet_control1_or_status1 ; Write CR1: enable TX interrupts |
| 84E0 | .tx_cr2_setup |
| LDA #&a7 ; CR2=&A7: RTS|CLR_RX_ST|FC_TDRA|PSE | |
| 84E2 | STA econet_control23_or_status2 ; Write CR2 for TX setup |
| 84E5 | .tx_nmi_setup |
| LDA #2 ; NMI handler lo byte (self-modifying) | |
| 84E7 | LDY #&85 ; Y=&85: NMI handler high byte |
| 84E9 | JMP ack_tx_write_dest ; Acknowledge and write TX dest |
Build immediate operation reply headerStores data length, source station/network, and control byte into the RX buffer header area for port-0 immediate operations. Then disables SR interrupts and configures the VIA shift register for shift-in mode before returning to idle listen. |
|
| 84EC | .imm_op_build_reply←1← 8397 JMP |
| LDA port_buf_len ; Get buffer position for reply header | |
| 84EE | CLC ; Clear carry for offset addition |
| 84EF | ADC #&80 ; Data offset = buf_len + &80 (past header) |
| 84F1 | LDY #&7f ; Y=&7F: reply data length slot |
| 84F3 | STA (net_rx_ptr),y ; Store reply data length in RX buffer |
| 84F5 | LDY #&80 ; Y=&80: source station slot |
| 84F7 | LDA scout_buf ; Load requesting station number |
| 84FA | STA (net_rx_ptr),y ; Store source station in reply header |
| 84FC | INY ; Y=&81 |
| 84FD | LDA scout_src_net ; Load requesting network number |
| 8500 | STA (net_rx_ptr),y ; Store source network in reply header |
| 8502 | LDA scout_ctrl ; Load control byte from received frame |
| 8505 | .setup_sr_tx←1← 83E8 JMP |
| STA tx_op_type ; Save TX operation type for SR dispatch | |
| 8508 | LDA #&84 ; IER bit 2: disable SR interrupt |
| 850A | STA system_via_ier ; Write IER to disable SR |
| 850D | LDA system_via_acr ; Read ACR for shift register config |
| 8510 | AND #&1c ; Isolate shift register mode bits (2-4) |
| 8512 | STA ws_0d64 ; Save original SR mode for later restore |
| 8515 | LDA system_via_acr ; Reload ACR for modification |
| 8518 | AND #&e3 ; Clear SR mode bits (keep other bits) |
| 851A | ORA #8 ; SR mode 2: shift in under φ2 |
| 851C | STA system_via_acr ; Apply new shift register mode |
| 851F | BIT system_via_sr ; Read SR to clear pending interrupt |
| 8522 | .imm_op_discard←1← 84D1 BCC |
| JMP reset_adlc_rx_listen ; Return to idle listen mode | |
Increment 4-byte receive buffer pointerAdds one to the counter at &A2-&A5 (port_buf_len low/high, open_port_buf low/high), cascading overflow through all four bytes. Called after each byte is stored during scout data copy and data frame reception to track the current write position in the receive buffer. |
|
| 8525 | .advance_buffer_ptr←3← 829E JSR← 82AC JSR← 8433 JSR |
| INC port_buf_len ; Increment buffer length low byte | |
| 8527 | BNE return_from_advance_buf ; No overflow: done |
| 8529 | INC port_buf_len_hi ; Increment buffer length high byte |
| 852B | BNE return_from_advance_buf ; No overflow: done |
| 852D | INC open_port_buf ; Increment buffer pointer low byte |
| 852F | BNE return_from_advance_buf ; No overflow: done |
| 8531 | INC open_port_buf_hi ; Increment buffer pointer high byte |
| 8533 | .return_from_advance_buf←3← 8527 BNE← 852B BNE← 852F BNE |
| RTS ; Return | |
| ; TX done dispatch table (lo bytes) | |
| ; Low bytes of PHA/PHA/RTS dispatch targets for TX | |
| ; operation types &83-&87. Read by the dispatch at | |
| ; &8064 via LDA set_rx_buf_len_hi,Y (base &84B1 | |
| ; + Y). High byte is always &85, so targets are | |
| ; &85xx+1. Entries for Y < &83 read from preceding | |
| ; code bytes and are not valid operation types. | |
| 8534 | .tx_done_dispatch_lo |
| EQUB <(tx_done_jsr-1) ; Y=&83: lo &38 -> tx_done_jsr (&8539) | |
| 8535 | EQUB <(tx_done_econet_event-1) ; Y=&84: lo &41 -> tx_done_econet_event |
| 8536 | EQUB <(tx_done_os_proc-1) ; Y=&85: lo &4F -> tx_done_os_proc |
| 8537 | EQUB <(tx_done_halt-1) ; Y=&86: lo &5B -> tx_done_halt |
| 8538 | EQUB <(tx_done_continue-1) ; Y=&87: lo &72 -> tx_done_continue |
TX done: remote JSR executionPushes (tx_done_exit - 1) on the stack so RTS returns to tx_done_exit, then does JMP (l0d66) to call the remote JSR target routine. When that routine returns via RTS, control resumes at tx_done_exit. |
|
| 8539 | .tx_done_jsr |
| LDA #&85 ; Hi byte of tx_done_exit-1 | |
| 853B | PHA ; Push hi byte on stack |
| 853C | LDA #&7a ; Push lo of (tx_done_exit-1) |
| 853E | PHA ; Push lo byte on stack |
| 853F | JMP (exec_addr_lo) ; Call remote JSR; RTS to tx_done_exit |
TX done: fire Econet eventHandler for TX operation type &84. Loads the remote address from l0d66/l0d67 into X/A and sets Y=8 (Econet event number), then falls through to tx_done_fire_event to call OSEVEN. |
|
| 8542 | .tx_done_econet_event |
| LDX exec_addr_lo ; X = remote address lo from l0d66 | |
| 8545 | LDA exec_addr_hi ; A = remote address hi from l0d67 |
| 8548 | LDY #event_network_error ; Y = 8: Econet event number |
| 854A | .tx_done_fire_event←1← 804F JMP |
| JSR oseven ; Generate event Y='Network error' | |
| 854D | JMP tx_done_exit ; Exit TX done handler |
TX done: OSProc callCalls the ROM service entry point with X=l0d66, Y=l0d67. This invokes an OS-level procedure on behalf of the remote station, then exits via tx_done_exit. |
|
| 8550 | .tx_done_os_proc |
| LDX exec_addr_lo ; X = remote address lo | |
| 8553 | LDY exec_addr_hi ; Y = remote address hi |
| 8556 | JSR dir_op_dispatch ; Call ROM entry point at &8000 |
| 8559 | JMP tx_done_exit ; Exit TX done handler |
TX done: HALTSets bit 2 of rx_flags (&0D61), enables interrupts, and spin-waits until bit 2 is cleared (by a CONTINUE from the remote station). If bit 2 is already set, skips to exit. |
|
| 855C | .tx_done_halt |
| LDA #4 ; A=&04: bit 2 mask for rx_flags | |
| 855E | BIT econet_flags ; Test if already halted |
| 8561 | BNE tx_done_exit ; Already halted: skip to exit |
| 8563 | ORA econet_flags ; Set bit 2 in rx_flags |
| 8566 | STA econet_flags ; Store halt flag |
| 8569 | LDA #4 ; A=4: re-load halt bit mask |
| 856B | CLI ; Enable interrupts during halt wait |
| 856C | .halt_spin_loop←1← 856F BNE |
| BIT econet_flags ; Test halt flag | |
| 856F | BNE halt_spin_loop ; Still halted: keep spinning |
| 8571 | BEQ tx_done_exit ; ALWAYS branch |
TX done: CONTINUEClears bit 2 of rx_flags (&0D61), releasing any station that is halted and spinning in tx_done_halt. |
|
| 8573 | .tx_done_continue |
| LDA econet_flags ; Load current RX flags | |
| 8576 | AND #&fb ; Clear bit 2: release halted station |
| 8578 | STA econet_flags ; Store updated flags |
| 857B | .tx_done_exit←4← 854D JMP← 8559 JMP← 8561 BNE← 8571 BEQ |
| PLA ; Restore Y from stack | |
| 857C | TAY ; Transfer to Y register |
| 857D | PLA ; Restore X from stack |
| 857E | TAX ; Transfer to X register |
| 857F | LDA #0 ; A=0: success status |
| 8581 | RTS ; Return with A=0 (success) |
Begin TX operationMain TX initiation entry point (called via trampoline at &06CE). Copies dest station/network from the TXCB to the scout buffer, dispatches to immediate op setup (ctrl >= &81) or normal data transfer, calculates transfer sizes, copies extra parameters, then enters the INACTIVE polling loop. |
|
| 8582 | .tx_begin←3← 98C1 JSR← A5A5 JMP← A89D JSR |
| TXA ; Save X on stack | |
| 8583 | PHA ; Push X |
| 8584 | LDY #2 ; Y=2: TXCB offset for dest station |
| 8586 | LDA (nmi_tx_block),y ; Load dest station from TX control block |
| 8588 | STA tx_dst_stn ; Store to TX scout buffer |
| 858B | INY ; Y=&03 |
| 858C | LDA (nmi_tx_block),y ; Load dest network from TX control block |
| 858E | STA tx_dst_net ; Store to TX scout buffer |
| 8591 | LDY #0 ; Y=0: first byte of TX control block |
| 8593 | LDA (nmi_tx_block),y ; Load control/flag byte |
| 8595 | BMI tx_imm_op_setup ; Bit7 set: immediate operation ctrl byte |
| 8597 | JMP tx_active_start ; Bit7 clear: normal data transfer |
| 859A | .tx_imm_op_setup←1← 8595 BMI |
| STA tx_ctrl_byte ; Store control byte to TX scout buffer | |
| 859D | TAX ; X = control byte for range checks |
| 859E | INY ; Y=1: port byte offset |
| 859F | LDA (nmi_tx_block),y ; Load port byte from TX control block |
| 85A1 | STA tx_port ; Store port byte to TX scout buffer |
| 85A4 | BNE tx_line_idle_check ; Port != 0: skip immediate op setup |
| 85A6 | CPX #&83 ; Ctrl < &83: PEEK/POKE need address calc |
| 85A8 | BCS tx_ctrl_range_check ; Ctrl >= &83: skip to range check |
| 85AA | SEC ; SEC: init borrow for 4-byte subtract |
| 85AB | PHP ; Save carry on stack for loop |
| 85AC | LDY #8 ; Y=8: high pointer offset in TXCB |
| 85AE | .calc_peek_poke_size←1← 85C2 BCC |
| LDA (nmi_tx_block),y ; Load TXCB[Y] (end addr byte) | |
| 85B0 | DEY ; Y -= 4: back to start addr offset |
| 85B1 | DEY ; (continued) |
| 85B2 | DEY ; (continued) |
| 85B3 | DEY ; (continued) |
| 85B4 | PLP ; Restore borrow from stack |
| 85B5 | SBC (nmi_tx_block),y ; end - start = transfer size byte |
| 85B7 | STA tx_data_start,y ; Store result to tx_data_start |
| 85BA | INY ; Y += 5: advance to next end byte |
| 85BB | INY ; (continued) |
| 85BC | INY ; (continued) |
| 85BD | INY ; (continued) |
| 85BE | INY ; (continued) |
| 85BF | PHP ; Save borrow for next byte |
| 85C0 | CPY #&0c ; Done all 4 bytes? (Y reaches &0C) |
| 85C2 | BCC calc_peek_poke_size ; No: next byte pair |
| 85C4 | PLP ; Discard final borrow |
| 85C5 | .tx_ctrl_range_check←1← 85A8 BCS |
| CPX #&81 ; Ctrl < &81: not an immediate op | |
| 85C7 | BCC tx_active_start ; Below range: normal data transfer |
| 85C9 | .check_imm_range |
| CPX #&89 ; Ctrl >= &89: out of immediate range | |
| 85CB | BCS tx_active_start ; Above range: normal data transfer |
| 85CD | LDY #&0c ; Y=&0C: start of extra data in TXCB |
| 85CF | .copy_imm_params←1← 85D7 BCC |
| LDA (nmi_tx_block),y ; Load extra parameter byte from TXCB | |
| 85D1 | STA imm_param_base,y ; Copy to NMI shim workspace at &0D1A+Y |
| 85D4 | INY ; Next byte |
| 85D5 | CPY #&10 ; Done 4 bytes? (Y reaches &10) |
| 85D7 | BCC copy_imm_params ; No: continue copying |
| 85D9 | .tx_line_idle_check←1← 85A4 BNE |
| LDA #&20 ; A=&20: mask for SR2 INACTIVE bit | |
| 85DB | BIT econet_control23_or_status2 ; BIT SR2: test if line is idle |
| 85DE | BNE tx_no_clock_error ; Line not idle: handle as line jammed |
| 85E0 | LDA #&fd ; A=&FD: high byte of timeout counter |
| 85E2 | PHA ; Push timeout high byte to stack |
| 85E3 | LDA #6 ; Scout frame = 6 address+ctrl bytes |
| 85E5 | STA rx_ctrl ; Store scout frame length |
| 85E8 | LDA #0 ; A=0: init low byte of timeout counter |
| fall through ↓ | |
INACTIVE polling loopEntry point for the Econet line idle detection loop. Saves the TX index in rx_remote_addr, pushes two timeout counter bytes onto the stack, and loads Y=&E7 (CR2 value for TX preparation). Loads the INACTIVE bit mask (&04) into A and falls through to intoff_test_inactive to begin polling SR2 with interrupts disabled. |
|
| 85EA | .inactive_poll |
| STA rx_remote_addr ; Save TX index | |
| 85ED | PHA ; Push timeout byte 1 on stack |
| 85EE | PHA ; Push timeout byte 2 on stack |
| 85EF | LDY #&e7 ; Y=&E7: CR2 value for TX prep (RTS|CLR_TX_ST|CLR_RX_ST|FC_TDRA|2_1_ BYTE|PSE) |
| 85F1 | .reload_inactive_mask←3← 8617 BNE← 861C BNE← 8621 BNE |
| LDA #4 ; A=&04: INACTIVE bit mask for SR2 test | |
| 85F3 | .test_inactive_retry |
| PHP ; Save interrupt state | |
| 85F4 | SEI ; Disable interrupts for ADLC access |
| fall through ↓ | |
Disable NMIs and test INACTIVEDisables NMIs via two reads of &FE18 (INTOFF), then polls SR2 for the INACTIVE bit (bit 2). If INACTIVE is detected, reads SR1 and writes CR2=&67 to clear status, then tests CTS (SR1 bit 4): if CTS is present, branches to tx_prepare to begin transmission. If INACTIVE is not set, re-enables NMIs via &FE20 (INTON) and decrements the 3-byte timeout counter on the stack. On timeout, falls through to tx_line_jammed. |
|
| 85F5 | .intoff_test_inactive |
| BIT station_id_disable_net_nmis ; INTOFF -- disable NMIs | |
| 85F8 | BIT station_id_disable_net_nmis ; INTOFF again (belt-and-braces) |
| 85FB | .test_line_idle |
| BIT econet_control23_or_status2 ; BIT SR2: Z = &04 AND SR2 -- tests INACTIVE | |
| 85FE | BEQ inactive_retry ; INACTIVE not set -- re-enable NMIs and loop |
| 8600 | LDA econet_control1_or_status1 ; Read SR1 (acknowledge pending interrupt) |
| 8603 | LDA #&67 ; CR2=&67: CLR_TX_ST|CLR_RX_ST|FC_ TDRA|2_1_BYTE|PSE |
| 8605 | STA econet_control23_or_status2 ; Write CR2: clear status, prepare TX |
| 8608 | LDA #&10 ; A=&10: CTS mask for SR1 bit4 |
| 860A | BIT econet_control1_or_status1 ; BIT SR1: tests CTS present |
| 860D | BNE tx_prepare ; CTS set -- clock hardware detected, start TX |
| 860F | .inactive_retry←1← 85FE BEQ |
| BIT video_ula_control ; INTON -- re-enable NMIs (&FE20 read) | |
| 8612 | PLP ; Restore interrupt state |
| 8613 | TSX ; 3-byte timeout counter on stack |
| 8614 | INC error_text,x ; Increment timeout counter byte 1 |
| 8617 | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test |
| 8619 | INC stack_page_2,x ; Increment timeout counter byte 2 |
| 861C | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test |
| 861E | INC stack_page_3,x ; Increment timeout counter byte 3 |
| 8621 | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test |
| 8623 | BEQ tx_line_jammed ; ALWAYS branch |
| 8625 | .tx_active_start←3← 8597 JMP← 85C7 BCC← 85CB BCS |
| LDA #&44 ; CR1=&44: TIE | TX_LAST_DATA | |
| 8627 | BNE store_tx_error ; ALWAYS branch |
TX timeout error handler (Line Jammed)Reached when the INACTIVE polling loop times out without detecting a quiet line. Writes CR2=&07 (FC_TDRA|2_1_BYTE|PSE) to abort the TX attempt, pulls the 3-byte timeout state from the stack, and stores error code &40 ('Line Jammed') in the TX control block via store_tx_error. |
|
| 8629 | .tx_line_jammed←1← 8623 BEQ |
| LDA #7 ; CR2=&07: FC_TDRA | 2_1_BYTE | PSE (abort TX) | |
| 862B | STA econet_control23_or_status2 ; Write CR2 to abort TX |
| 862E | PLA ; Clean 3 bytes of timeout loop state |
| 862F | PLA ; Pop saved register |
| 8630 | PLA ; Pop saved register |
| 8631 | LDA #&40 ; Error &40 = 'Line Jammed' |
| 8633 | BNE store_tx_error ; ALWAYS branch to shared error handler ALWAYS branch |
| 8635 | .tx_no_clock_error←1← 85DE BNE |
| LDA #&43 ; Error &43 = 'No Clock' | |
| 8637 | .store_tx_error←2← 8627 BNE← 8633 BNE |
| LDY #0 ; Offset 0 = error byte in TX control block | |
| 8639 | STA (nmi_tx_block),y ; Store error code in TX CB byte 0 |
| 863B | LDA #&80 ; &80 = TX complete flag |
| 863D | STA ws_0d60 ; Signal TX operation complete |
| 8640 | PLA ; Restore X saved by caller |
| 8641 | TAX ; Move to X register |
| 8642 | RTS ; Return to TX caller |
TX preparationConfigures the ADLC for frame transmission. Writes CR2=Y (&E7: RTS|CLR_TX_ST|CLR_RX_ST|FC_TDRA| 2_1_BYTE|PSE) and CR1=&44 (RX_RESET|TIE) to enable TX with interrupts. Installs the nmi_tx_data handler at &86E0. Sets need_release_tube flag via SEC/ROR. Writes the 4-byte destination address (dst_stn, dst_net, src_stn, src_net=0) to the TX FIFO. For Tube transfers, claims the Tube address; for direct transfers, sets up the buffer pointer from the TXCB. |
|
| 8643 | .tx_prepare←1← 860D BNE |
| STY econet_control23_or_status2 ; Write CR2 = Y (&E7: RTS|CLR_TX_ST|CLR_RX_ST| FC_TDRA|2_1_BYTE|PSE) | |
| 8646 | LDX #&44 ; CR1=&44: RX_RESET | TIE (TX active, TX interrupts enabled) |
| 8648 | STX econet_control1_or_status1 ; Write to ADLC CR1 |
| 864B | LDX #&e0 ; Install NMI handler at &86E0 (TX data handler) |
| 864D | LDY #&86 ; High byte of NMI handler address |
| 864F | STX nmi_jmp_lo ; Write NMI vector low byte directly |
| 8652 | STY nmi_jmp_hi ; Write NMI vector high byte directly |
| 8655 | SEC ; Set need_release_tube flag (SEC/ROR = bit7) |
| 8656 | ROR need_release_tube ; Rotate carry into bit 7 of flag |
| 8658 | BIT video_ula_control ; INTON -- NMIs now fire for TDRA (&FE20 read) |
| 865B | LDA tx_port ; Load destination port number |
| 865E | BNE setup_data_xfer ; Port != 0: standard data transfer |
| 8660 | LDY tx_ctrl_byte ; Port 0: load control byte for table lookup |
| 8663 | LDA tube_tx_sr1_operand,y ; Look up tx_flags from table |
| 8666 | STA rx_src_net ; Store operation flags |
| 8669 | LDA tube_tx_inc_operand,y ; Look up tx_length from table |
| 866C | STA rx_ctrl ; Store expected transfer length |
| 866F | LDA #&86 ; Push high byte of return address (&9C) |
| 8671 | PHA ; Push high byte for PHA/PHA/RTS dispatch |
| 8672 | LDA intoff_disable_nmi_op,y ; Look up handler address low from table |
| 8675 | PHA ; Push low byte for PHA/PHA/RTS dispatch |
| 8676 | RTS ; RTS dispatches to control-byte handler |
| ; TX ctrl dispatch table (lo bytes) | |
| ; Low bytes of PHA/PHA/RTS dispatch targets for TX | |
| ; control byte types &81-&88. Read by the dispatch | |
| ; at &8672 via LDA intoff_disable_nmi_op,Y (base | |
| ; intoff_test_inactive+1). High byte is always &86, | |
| ; so targets are &86xx+1. Last entry dispatches to | |
| ; tx_ctrl_machine_type at &867F, immediately after | |
| ; the table. | |
| 8677 | .tx_ctrl_dispatch_lo |
| EQUB <(tx_ctrl_peek-1) ; Ctrl &81 PEEK: tx_ctrl_peek | |
| 8678 | EQUB <(tx_ctrl_poke-1) ; Ctrl &82 POKE: tx_ctrl_poke |
| 8679 | EQUB <(proc_op_status2-1) ; Ctrl &83 JSR: proc_op_status2 |
| 867A | EQUB <(proc_op_status2-1) ; Ctrl &84 UserProc: proc_op_status2 |
| 867B | EQUB <(proc_op_status2-1) ; Ctrl &85 OSProc: proc_op_status2 |
| 867C | EQUB <(tx_ctrl_exit-1) ; Ctrl &86 HALT: tx_ctrl_exit |
| 867D | EQUB <(tx_ctrl_exit-1) ; Ctrl &87 CONTINUE: tx_ctrl_exit |
| 867E | EQUB <(tx_ctrl_machine_type-1) ; Ctrl &88 MachType: tx_ctrl_machine_type |
TX ctrl: machine type query setupHandler for control byte &88. Sets scout_status=3 and branches to store_status_copy_ptr, skipping the 4-byte address addition (no address parameters needed for a machine type query). |
|
| 867F | .tx_ctrl_machine_type |
| LDA #3 ; scout_status=3 (machine type query) | |
| 8681 | BNE store_status_copy_ptr ; Skip address addition, store status ALWAYS branch |
TX ctrl: PEEK transfer setupSets A=3 (scout_status for PEEK) and branches to tx_ctrl_store_and_add to store the status and perform the 4-byte transfer address addition. |
|
| 8683 | .tx_ctrl_peek |
| LDA #3 ; A=3: scout_status for PEEK op | |
| 8685 | BNE tx_ctrl_store_and_add ; ALWAYS branch |
TX ctrl: POKE transfer setupSets A=2 (scout_status for POKE) and falls through to tx_ctrl_store_and_add to store the status and perform the 4-byte transfer address addition. |
|
| 8687 | .tx_ctrl_poke |
| LDA #2 ; Scout status = 2 (POKE transfer) | |
| fall through ↓ | |
TX ctrl: store status and add transfer addressShared path for PEEK (A=3) and POKE (A=2). Stores A as the scout status byte at rx_port (&0D40), then performs a 4-byte addition with carry propagation, adding bytes from the TXCB (nmi_tx_block+&0C to +&0F) into the transfer address workspace at &0D1E-&0D21. Falls through to tx_ctrl_proc which checks the loop boundary, then continues to tx_calc_transfer and tx_ctrl_exit.
|
||||
| 8689 | .tx_ctrl_store_and_add←1← 8685 BNE | |||
| STA rx_port ; Store scout status | ||||
| 868C | CLC ; Clear carry for 4-byte addition | |||
| 868D | PHP ; Save carry on stack | |||
| 868E | LDY #&0c ; Y=&0C: start at offset 12 | |||
| 8690 | .add_bytes_loop←1← 869D BCC | |||
| LDA tx_addr_base,y ; Load workspace address byte | ||||
| 8693 | PLP ; Restore carry from previous byte | |||
| 8694 | ADC (nmi_tx_block),y ; Add TXCB address byte | |||
| 8696 | STA tx_addr_base,y ; Store updated address byte | |||
| 8699 | INY ; Next byte | |||
| 869A | PHP ; Save carry for next addition | |||
| fall through ↓ | ||||
TX ctrl: JSR/UserProc/OSProc setupSets scout_status=2 and calls tx_calc_transfer directly (no 4-byte address addition needed for procedure calls). Shared by operation types &83-&85. |
|
| 869B | .tx_ctrl_proc |
| CPY #&10 ; Compare Y with 16-byte boundary | |
| 869D | BCC add_bytes_loop ; Below boundary: continue addition |
| 869F | PLP ; Restore processor flags |
| 86A0 | BNE skip_buf_setup ; Skip buffer setup if transfer size is zero |
| 86A2 | .setup_data_xfer←1← 865E BNE |
| LDA tx_dst_stn ; Load dest station for broadcast check | |
| 86A5 | AND tx_dst_net ; AND with dest network |
| 86A8 | CMP #&ff ; Both &FF = broadcast address? |
| 86AA | BNE setup_unicast_xfer ; Not broadcast: unicast path |
| 86AC | LDA #&0e ; Broadcast scout: 14 bytes total |
| 86AE | STA rx_ctrl ; Store broadcast scout length |
| 86B1 | LDA #&40 ; A=&40: broadcast flag |
| 86B3 | STA rx_src_net ; Set broadcast flag in tx_flags |
| 86B6 | LDY #4 ; Y=4: start of address data in TXCB |
| 86B8 | .copy_bcast_addr←1← 86C0 BCC |
| LDA (nmi_tx_block),y ; Copy TXCB address bytes to scout buffer | |
| 86BA | STA tx_src_stn,y ; Store to TX source/data area |
| 86BD | INY ; Next byte |
| 86BE | CPY #&0c ; Done 8 bytes? (Y reaches &0C) |
| 86C0 | BCC copy_bcast_addr ; No: continue copying |
| 86C2 | BCS tx_ctrl_exit ; ALWAYS branch |
| 86C4 | .setup_unicast_xfer←1← 86AA BNE |
| LDA #0 ; A=0: clear flags for unicast | |
| 86C6 | STA rx_src_net ; Clear tx_flags |
| 86C9 | .proc_op_status2 |
| LDA #2 ; scout_status=2: data transfer pending | |
| 86CB | .store_status_copy_ptr←1← 8681 BNE |
| STA rx_port ; Store scout status | |
| 86CE | .skip_buf_setup←1← 86A0 BNE |
| LDA nmi_tx_block ; Copy TX block pointer to workspace ptr | |
| 86D0 | STA port_ws_offset ; Store low byte |
| 86D2 | LDA nmi_tx_block_hi ; Copy TX block pointer high byte |
| 86D4 | STA rx_buf_offset ; Store high byte |
| 86D6 | JSR tx_calc_transfer ; Calculate transfer size from RXCB |
| 86D9 | .tx_ctrl_exit←1← 86C2 BCS |
| PLP ; Restore processor status from stack | |
| 86DA | PLA ; Restore stacked registers (4 PLAs) |
| 86DB | PLA ; Second PLA |
| 86DC | PLA ; Third PLA |
| 86DD | PLA ; Fourth PLA |
| 86DE | TAX ; Restore X from A |
| 86DF | RTS ; Return to caller |
NMI TX data handlerWrites 2 bytes per NMI invocation to the TX FIFO at &FEA2. Uses the BIT instruction on SR1 to test TDRA (V flag = bit6) and IRQ (N flag = bit7). After writing 2 bytes, checks if the frame is complete. If more data, tests SR1 bit7 (IRQ) via BMI -- if IRQ still asserted, writes 2 more bytes without returning from NMI (tight loop). Otherwise returns via RTI. |
|
| 86E0 | .nmi_tx_data |
| LDY rx_remote_addr ; Load TX buffer index | |
| 86E3 | BIT econet_control1_or_status1 ; BIT SR1: V=bit6(TDRA), N=bit7(IRQ) |
| 86E6 | .tx_fifo_write←1← 8701 BMI |
| BVC tx_fifo_not_ready ; TDRA not set -- TX error | |
| 86E8 | LDA tx_dst_stn,y ; Load byte from TX buffer |
| 86EB | STA econet_data_continue_frame ; Write to TX_DATA (continue frame) |
| 86EE | INY ; Next TX buffer byte |
| 86EF | LDA tx_dst_stn,y ; Load second byte from TX buffer |
| 86F2 | INY ; Advance TX index past second byte |
| 86F3 | STY rx_remote_addr ; Save updated TX buffer index |
| 86F6 | STA econet_data_continue_frame ; Write second byte to TX_DATA |
| 86F9 | CPY rx_ctrl ; Compare index to TX length |
| 86FC | BCS tx_last_data ; Frame complete -- go to TX_LAST_DATA |
| 86FE | BIT econet_control1_or_status1 ; Check if we can send another pair |
| 8701 | BMI tx_fifo_write ; IRQ set -- send 2 more bytes (tight loop) |
| 8703 | JMP nmi_rti ; RTI -- wait for next NMI |
| 8706 | .tx_error←1← 8749 BEQ |
| LDA #&42 ; Error &42 | |
| 8708 | BNE tx_store_error ; ALWAYS branch |
| 870A | .tx_fifo_not_ready←1← 86E6 BVC |
| LDA #&67 ; CR2=&67: clear status, return to listen | |
| 870C | STA econet_control23_or_status2 ; Write CR2: clear status, idle listen |
| 870F | LDA #&41 ; Error &41 (TDRA not ready) |
| 8711 | .tx_store_error←1← 8708 BNE |
| LDY station_id_disable_net_nmis ; INTOFF (also loads station ID) | |
| 8714 | .delay_nmi_disable←1← 8717 BNE |
| PHA ; PHA/PLA delay loop (256 iterations for NMI disable) | |
| 8715 | PLA ; PHA/PLA delay (~7 cycles each) |
| 8716 | INY ; Increment delay counter |
| 8717 | BNE delay_nmi_disable ; Loop 256 times for NMI disable |
| 8719 | JMP tx_store_result ; Store error and return to idle |
TX_LAST_DATA and frame completionSignals end of TX frame by writing CR2=&3F (TX_LAST_DATA). Then installs the TX completion NMI handler at &8728 (nmi_tx_complete). CR2=&3F = 0011_1111: bit5: CLR_RX_ST -- clears fv_stored_ (prepares for RX of reply) bit4: TX_LAST_DATA -- tells ADLC this is the final data byte bit3: FLAG_IDLE -- send flags/idle after frame bit2: FC_TDRA -- force clear TDRA bit1: 2_1_BYTE -- two-byte transfer mode bit0: PSE -- prioritised status enable Note: NO CLR_TX_ST (bit6=0), NO RTS (bit7=0 -- drops RTS after frame) |
|
| 871C | .tx_last_data←1← 86FC BCS |
| LDA #&3f ; CR2=&3F: TX_LAST_DATA | CLR_RX_ST | FLAG_IDLE | FC_TDRA | 2_1_BYTE | PSE | |
| 871E | STA econet_control23_or_status2 ; Write to ADLC CR2 |
| 8721 | LDA #&28 ; Install NMI handler at &8728 (TX completion) |
| 8723 | LDY #&87 ; High byte of handler address |
| 8725 | JMP set_nmi_vector ; Install and return via set_nmi_vector |
TX completion: switch to RX modeCalled via NMI after the frame (including CRC and closing flag) has been fully transmitted. Switches from TX mode to RX mode by writing CR1=&82. CR1=&82 = 1000_0010: TX_RESET | RIE (listen for reply). Checks workspace flags to decide next action: - bit6 set at &0D4A -> tx_result_ok at &88C6 - bit0 set at &0D4A -> handshake_await_ack at &886E - Otherwise -> install nmi_reply_scout at &8744 |
|
| 8728 | .nmi_tx_complete |
| LDA #&82 ; Jump to error handler | |
| 872A | STA econet_control1_or_status1 ; Write CR1 to switch from TX to RX |
| 872D | BIT rx_src_net ; Test workspace flags |
| 8730 | BVC check_handshake_bit ; bit6 not set -- check bit0 |
| 8732 | JMP tx_result_ok ; bit6 set -- TX completion |
| 8735 | .check_handshake_bit←1← 8730 BVC |
| LDA #1 ; A=1: mask for bit0 test | |
| 8737 | BIT rx_src_net ; Test tx_flags bit0 (handshake) |
| 873A | BEQ install_reply_scout ; bit0 clear: install reply handler |
| 873C | JMP handshake_await_ack ; bit0 set -- four-way handshake data phase |
| 873F | .install_reply_scout←1← 873A BEQ |
| LDA #&44 ; Install RX reply handler at &8744 | |
| 8741 | JMP install_nmi_handler ; Install handler and RTI |
RX reply scout handlerHandles reception of the reply scout frame after transmission. Checks SR2 bit0 (AP) for incoming data, reads the first byte (destination station) and compares to our station ID via &FE18 (which also disables NMIs as a side effect). |
|
| 8744 | .nmi_reply_scout |
| LDA #1 ; A=&01: AP mask for SR2 | |
| 8746 | BIT econet_control23_or_status2 ; BIT SR2: test AP (Address Present) |
| 8749 | BEQ tx_error ; No AP -- error |
| 874B | LDA econet_data_continue_frame ; Read first RX byte (destination station) |
| 874E | CMP station_id_disable_net_nmis ; Compare to our station ID (INTOFF side effect) |
| 8751 | BNE reject_reply ; Not our station -- error/reject |
| 8753 | LDA #&58 ; Install next handler at &8758 (reply continuation) |
| 8755 | JMP install_nmi_handler ; Install continuation handler |
RX reply continuation handlerReads the second byte of the reply scout (destination network) and validates it is zero (local network). Installs nmi_reply_validate (&876F) for the remaining two bytes (source station and network). Optimisation: checks SR1 bit7 (IRQ still asserted) via BMI at &8767. If IRQ is still set, falls through directly to &876F without an RTI, avoiding NMI re-entry overhead for short frames where all bytes arrive in quick succession. |
|
| 8758 | .nmi_reply_cont |
| BIT econet_control23_or_status2 ; Read RX byte (destination station) | |
| 875B | BPL reject_reply ; No RDA -- error |
| 875D | LDA econet_data_continue_frame ; Read destination network byte |
| 8760 | BNE reject_reply ; Non-zero -- network mismatch, error |
| 8762 | LDA #&6f ; Install next handler at &876F (reply validation) |
| 8764 | BIT econet_control1_or_status1 ; BIT SR1: test IRQ (N=bit7) -- more data ready? |
| 8767 | BMI nmi_reply_validate ; IRQ set -- fall through to &876F without RTI |
| 8769 | JMP install_nmi_handler ; IRQ not set -- install handler and RTI |
| 876C | .reject_reply←7← 8751 BNE← 875B BPL← 8760 BNE← 8772 BPL← 877A BNE← 8782 BNE← 8789 BEQ |
| JMP tx_result_fail ; Store error and return to idle | |
RX reply validation (Path 2 for FV/PSE interaction)Reads the source station and source network from the reply scout and validates them against the original TX destination (&0D20/&0D21). Sequence: 1. Check SR2 bit7 (RDA) at &876F -- must see data available 2. Read source station at &8774, compare to &0D20 (tx_dst_stn) 3. Read source network at &877C, compare to &0D21 (tx_dst_net) 4. Check SR2 bit1 (FV) at &8786 -- must see frame complete If all checks pass, the reply scout is valid and the ROM proceeds to send the scout ACK (CR2=&A7 for RTS, CR1=&44 for TX mode). |
|
| 876F | .nmi_reply_validate←1← 8767 BMI |
| BIT econet_control23_or_status2 ; BIT SR2: test RDA (bit7). Must be set for valid reply. | |
| 8772 | BPL reject_reply ; No RDA -- error (FV masking RDA via PSE would cause this) |
| 8774 | LDA econet_data_continue_frame ; Read source station |
| 8777 | CMP tx_dst_stn ; Compare to original TX destination station (&0D20) |
| 877A | BNE reject_reply ; Mismatch -- not the expected reply, error |
| 877C | LDA econet_data_continue_frame ; Read source network |
| 877F | CMP tx_dst_net ; Compare to original TX destination network (&0D21) |
| 8782 | BNE reject_reply ; Mismatch -- error |
| 8784 | LDA #2 ; A=&02: FV mask for SR2 bit1 |
| 8786 | BIT econet_control23_or_status2 ; BIT SR2: test FV -- frame must be complete |
| 8789 | BEQ reject_reply ; No FV -- incomplete frame, error |
| 878B | LDA #&a7 ; CR2=&A7: RTS|CLR_TX_ST|FC_TDRA|2_ 1_BYTE|PSE (TX in handshake) |
| 878D | STA econet_control23_or_status2 ; Write CR2: enable RTS for TX handshake |
| 8790 | LDA #&44 ; CR1=&44: RX_RESET | TIE (TX active for scout ACK) |
| 8792 | STA econet_control1_or_status1 ; Write CR1: reset RX, enable TX interrupt |
| 8795 | LDA #&6e ; Install next handler at &886E (four-way data phase) into &0D43/&0D44 |
| 8797 | LDY #&88 ; High byte &88 of next handler address |
| 8799 | STA saved_nmi_lo ; Store low byte to nmi_next_lo |
| 879C | STY saved_nmi_hi ; Store high byte to nmi_next_hi |
| 879F | LDA tx_dst_stn ; Load dest station for scout ACK TX |
| 87A2 | BIT econet_control1_or_status1 ; BIT SR1: test TDRA (V=bit6) |
| 87A5 | BVC tx_check_tdra_ready ; TDRA not ready -- error |
| 87A7 | STA econet_data_continue_frame ; Write dest station to TX FIFO |
| 87AA | LDA tx_dst_net ; Write dest network to TX FIFO |
| 87AD | STA econet_data_continue_frame ; Write dest network to TX FIFO |
| 87B0 | LDA #&b7 ; Install handler at &87B7 (write src addr for scout ACK) |
| 87B2 | LDY #&87 ; High byte &87 of handler address |
| 87B4 | JMP set_nmi_vector ; Set NMI vector and return |
TX scout ACK: write source addressContinuation of the TX-side scout ACK. Reads our station ID from &FE18 (INTOFF), tests TDRA via SR1, and writes station + network=0 to the TX FIFO. Then checks bit 1 of rx_src_net to select between the immediate-op data NMI handler and the normal nmi_data_tx handler at &87E4. Installs the chosen handler via set_nmi_vector. Shares the tx_check_tdra entry at &87BD with ack_tx. |
|
| 87B7 | .nmi_scout_ack_src |
| LDA station_id_disable_net_nmis ; Load our station ID (also INTOFF) | |
| 87BA | BIT econet_control1_or_status1 ; BIT SR1: test TDRA |
| 87BD | .tx_check_tdra_ready←1← 87A5 BVC |
| BVC data_tx_check_fifo ; TDRA not ready -- error | |
| 87BF | STA econet_data_continue_frame ; Write our station to TX FIFO |
| 87C2 | LDA #0 ; Write network=0 to TX FIFO |
| 87C4 | STA econet_data_continue_frame ; Write network byte to TX FIFO |
| 87C7 | .data_tx_begin←1← 833E JMP |
| LDA #2 ; Test bit 1 of tx_flags | |
| 87C9 | BIT rx_src_net ; Check if immediate-op or data-transfer |
| 87CC | BNE install_imm_data_nmi ; Bit 1 set: immediate op, use alt handler |
| 87CE | LDA #&e4 ; Install nmi_data_tx at &87E4 |
| 87D0 | LDY #&87 ; High byte of handler address |
| 87D2 | JMP set_nmi_vector ; Install and return via set_nmi_vector |
| 87D5 | .install_imm_data_nmi←1← 87CC BNE |
| LDA #&2d ; Install nmi_imm_data at &882D | |
| 87D7 | LDY #&88 ; High byte of handler address |
| 87D9 | JMP set_nmi_vector ; Install and return via set_nmi_vector |
TX data phase: send payloadTransmits the data payload of a four-way handshake. Loads bytes from (open_port_buf),Y or from Tube R3 depending on the transfer mode, writing pairs to the TX FIFO. After each pair, decrements the byte count (port_buf_len). If the count reaches zero, branches to tx_last_data to signal end of frame. Otherwise tests SR1 bit 7 (IRQ): if still asserted, writes another pair without returning from NMI (tight loop optimisation). If IRQ clears, returns via RTI. |
|
| 87DC | .nmi_data_tx←1← 87E6 BEQ |
| LDY port_buf_len_hi ; Y = buffer offset, resume from last position | |
| 87DE | BEQ data_tx_last ; No pages left: send final partial page |
| 87E0 | LDY port_buf_len ; Load remaining byte count |
| 87E2 | BEQ check_tdra_status ; Zero bytes left: skip to TDRA check |
| 87E4 | LDY port_buf_len ; Load remaining byte count (alt entry) |
| 87E6 | BEQ nmi_data_tx ; Zero: loop back to top of handler |
| 87E8 | .check_tdra_status←1← 87E2 BEQ |
| BIT econet_control1_or_status1 ; BIT SR1: test TDRA (V=bit6) | |
| 87EB | .data_tx_check_fifo←2← 87BD BVC← 880E BMI |
| BVC tube_tx_fifo_write ; TDRA not ready -- error | |
| 87ED | LDA (open_port_buf),y ; Write data byte to TX FIFO |
| 87EF | STA econet_data_continue_frame ; Write first byte of pair to FIFO |
| 87F2 | INY ; Advance buffer offset |
| 87F3 | BNE write_second_tx_byte ; No page crossing |
| 87F5 | DEC port_buf_len_hi ; Page crossing: decrement page count |
| 87F7 | BEQ data_tx_last ; No pages left: send last data |
| 87F9 | INC open_port_buf_hi ; Increment buffer high byte |
| 87FB | .write_second_tx_byte←1← 87F3 BNE |
| LDA (open_port_buf),y ; Load second byte of pair | |
| 87FD | STA econet_data_continue_frame ; Write second byte to FIFO |
| 8800 | INY ; Advance buffer offset |
| 8801 | STY port_buf_len ; Save updated buffer position |
| 8803 | BNE check_irq_loop ; No page crossing |
| 8805 | DEC port_buf_len_hi ; Page crossing: decrement page count |
| 8807 | BEQ data_tx_last ; No pages left: send last data |
| 8809 | INC open_port_buf_hi ; Increment buffer high byte |
| 880B | .check_irq_loop←1← 8803 BNE |
| BIT econet_control1_or_status1 ; BIT SR1: test IRQ (N=bit7) for tight loop | |
| 880E | BMI data_tx_check_fifo ; IRQ still set: write 2 more bytes |
| 8810 | JMP nmi_rti ; No IRQ: return, wait for next NMI |
| 8813 | .data_tx_last←5← 87DE BEQ← 87F7 BEQ← 8807 BEQ← 8846 BEQ← 885C BEQ |
| LDA #&3f ; CR2=&3F: TX_LAST_DATA (close data frame) | |
| 8815 | STA econet_control23_or_status2 ; Write CR2 to close frame |
| 8818 | LDA rx_src_net ; Check tx_flags for next action |
| 881B | BPL install_saved_handler ; Bit7 clear: error, install saved handler |
| 881D | LDA #&eb ; Install discard_reset_listen at &83EB |
| 881F | LDY #&83 ; High byte of &83EB handler |
| 8821 | JMP set_nmi_vector ; Set NMI vector and return |
| 8824 | .install_saved_handler←1← 881B BPL |
| LDA saved_nmi_lo ; Load saved next handler low byte | |
| 8827 | LDY saved_nmi_hi ; Load saved next handler high byte |
| 882A | JMP set_nmi_vector ; Install saved handler and return |
| 882D | .nmi_data_tx_tube |
| BIT econet_control1_or_status1 ; Tube TX: BIT SR1 test TDRA | |
| 8830 | .tube_tx_fifo_write←2← 87EB BVC← 8861 BMI |
| BVC tx_tdra_error ; TDRA not ready -- error | |
| 8832 | LDA tube_data_register_3 ; Read byte from Tube R3 |
| 8835 | STA econet_data_continue_frame ; Write to TX FIFO |
| 8838 | INC port_buf_len ; Increment 4-byte buffer counter |
| 883A | BNE write_second_tube_byte ; Low byte didn't wrap |
| 883C | INC port_buf_len_hi ; Carry into second byte |
| 883E | BNE write_second_tube_byte ; No further carry |
| 8840 | INC open_port_buf ; Carry into third byte |
| 8842 | BNE write_second_tube_byte ; No further carry |
| 8844 | INC open_port_buf_hi ; Carry into fourth byte |
| 8846 | BEQ data_tx_last ; Counter wrapped to zero: last data |
| 8848 | .write_second_tube_byte←3← 883A BNE← 883E BNE← 8842 BNE |
| LDA tube_data_register_3 ; Read second Tube byte from R3 | |
| 884B | STA econet_data_continue_frame ; Write second byte to TX FIFO |
| 884E | INC port_buf_len ; Increment 4-byte counter (second byte) |
| 8850 | BNE check_tube_irq_loop ; Low byte didn't wrap |
| 8852 | .tube_tx_inc_byte2 |
| INC port_buf_len_hi ; Carry into second byte | |
| 8854 | BNE check_tube_irq_loop ; No further carry |
| 8856 | .tube_tx_inc_byte3 |
| INC open_port_buf ; Carry into third byte | |
| 8858 | BNE check_tube_irq_loop ; No further carry |
| 885A | .tube_tx_inc_byte4 |
| INC open_port_buf_hi ; Carry into fourth byte | |
| 885C | BEQ data_tx_last ; Counter wrapped to zero: last data |
| 885E | .check_tube_irq_loop←3← 8850 BNE← 8854 BNE← 8858 BNE |
| BIT econet_control1_or_status1 ; BIT SR1: test IRQ for tight loop | |
| 8861 | BMI tube_tx_fifo_write ; IRQ still set: write 2 more bytes |
| 8863 | JMP nmi_rti ; No IRQ: return, wait for next NMI |
| 8866 | .tx_tdra_error←1← 8830 BVC |
| LDA rx_src_net ; TX error: check flags for path | |
| 8869 | BPL tx_result_fail ; Bit7 clear: TX result = not listening |
| 886B | JMP discard_reset_rx ; Bit7 set: discard and return to listen |
Four-way handshake: switch to RX for final ACK |
|
| 886E | .handshake_await_ack←1← 873C JMP |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (switch to RX for final ACK) | |
| 8870 | STA econet_control1_or_status1 ; Write to ADLC CR1 |
| 8873 | LDA #&7a ; Install nmi_final_ack handler |
| 8875 | LDY #&88 ; High byte of handler address |
| 8877 | JMP set_nmi_vector ; Install and return via set_nmi_vector |
RX final ACK handlerReceives the final ACK in a four-way handshake. Same validation pattern as the reply scout handler (&8744-&876F): &887A: Check AP, read dest_stn, compare to our station &888E: Check RDA, read dest_net, validate = 0 &88A2: Check RDA, read src_stn/net, compare to TX dest &88C1: Check FV for frame completion On success, stores result=0 at tx_result_ok. On failure, error &41. |
|
| 887A | .nmi_final_ack |
| LDA #1 ; A=&01: AP mask | |
| 887C | BIT econet_control23_or_status2 ; BIT SR2: test AP |
| 887F | BEQ tx_result_fail ; No AP -- error |
| 8881 | LDA econet_data_continue_frame ; Read dest station |
| 8884 | CMP station_id_disable_net_nmis ; Compare to our station (INTOFF side effect) |
| 8887 | BNE tx_result_fail ; Not our station -- error |
| 8889 | LDA #&8e ; Install nmi_final_ack_net handler |
| 888B | JMP install_nmi_handler ; Install continuation handler |
| 888E | .nmi_final_ack_net |
| BIT econet_control23_or_status2 ; BIT SR2: test RDA | |
| 8891 | BPL tx_result_fail ; No RDA -- error |
| 8893 | LDA econet_data_continue_frame ; Read dest network |
| 8896 | BNE tx_result_fail ; Non-zero -- network mismatch, error |
| 8898 | LDA #&a2 ; Install nmi_final_ack_validate handler |
| 889A | BIT econet_control1_or_status1 ; BIT SR1: test IRQ -- more data ready? |
| 889D | BMI nmi_final_ack_validate ; IRQ set -- fall through to validate |
| 889F | JMP install_nmi_handler ; Install handler and RTI |
Final ACK validationContinuation of nmi_final_ack. Tests SR2 for RDA, then reads the source station and source network bytes from the RX FIFO, comparing each against the original TX destination at tx_dst_stn (&0D20) and tx_dst_net (&0D21). Finally tests SR2 bit 1 (FV) for frame completion. Any mismatch or missing FV branches to tx_result_fail. On success, falls through to tx_result_ok. |
|
| 88A2 | .nmi_final_ack_validate←1← 889D BMI |
| BIT econet_control23_or_status2 ; BIT SR2: test RDA | |
| 88A5 | BPL tx_result_fail ; No RDA -- error |
| 88A7 | LDA econet_data_continue_frame ; Read source station |
| 88AA | CMP tx_dst_stn ; Compare to TX dest station (&0D20) |
| 88AD | BNE tx_result_fail ; Mismatch -- error |
| 88AF | LDA econet_data_continue_frame ; Read source network |
| 88B2 | CMP tx_dst_net ; Compare to TX dest network (&0D21) |
| 88B5 | BNE tx_result_fail ; Mismatch -- error |
| 88B7 | LDA rx_src_net ; Load TX flags for next action |
| 88BA | BPL check_fv_final_ack ; bit7 clear: no data phase |
| 88BC | JMP install_data_rx_handler ; Install data RX handler |
| 88BF | .check_fv_final_ack←1← 88BA BPL |
| LDA #2 ; A=&02: FV mask for SR2 bit1 | |
| 88C1 | BIT econet_control23_or_status2 ; BIT SR2: test FV -- frame must be complete |
| 88C4 | BEQ tx_result_fail ; No FV -- error |
| fall through ↓ | |
TX completion handlerLoads A=0 (success) and branches unconditionally to tx_store_result (BEQ is always taken since A=0). This two-instruction entry point exists so that JMP sites can target the success path without needing to set A. Called from ack_tx (&82EC) for final-ACK completion and from nmi_tx_complete (&8732) for immediate-op completion where no ACK is expected. |
|
| 88C6 | .tx_result_ok←2← 82EC JMP← 8732 JMP |
| LDA #0 ; A=0: success result code | |
| 88C8 | BEQ tx_store_result ; BEQ: always taken (A=0) ALWAYS branch |
TX failure: not listeningLoads error code &41 (not listening) and falls through to tx_store_result. The most common TX error path — reached from 11 sites across the final-ACK validation chain when the remote station doesn't respond or the frame is malformed. |
|
| 88CA | .tx_result_fail←11← 8230 JMP← 876C JMP← 8869 BPL← 887F BEQ← 8887 BNE← 8891 BPL← 8896 BNE← 88A5 BPL← 88AD BNE← 88B5 BNE← 88C4 BEQ |
| LDA #&41 ; A=&41: not listening error code | |
| fall through ↓ | |
TX result store and completionStores the TX result code (in A) at offset 0 of the TX control block via (nmi_tx_block),Y=0. Sets ws_0d60 to &80 to signal TX completion to the foreground polling loop. Then jumps to discard_reset_rx for a full ADLC reset and return to idle RX listen mode.
|
||||
| 88CC | .tx_store_result←2← 8719 JMP← 88C8 BEQ | |||
| LDY #0 ; Y=0: index into TX control block | ||||
| 88CE | STA (nmi_tx_block),y ; Store result/error code at (nmi_tx_block),0 | |||
| 88D0 | LDA #&80 ; &80: completion flag for &0D3A | |||
| 88D2 | STA ws_0d60 ; Signal TX complete | |||
| 88D5 | JMP discard_reset_rx ; Full ADLC reset and return to idle listen | |||
| ; Unreferenced dead data (16 bytes) | ||||
| ; 16 bytes between JMP discard_reset_rx (&88D5) and | ||||
| ; tx_calc_transfer (&88E8). Unreachable as code (after | ||||
| ; an unconditional JMP) and unreferenced as data. No | ||||
| ; label, index, or indirect pointer targets any address | ||||
| ; in the &88D8-&88E7 range. Likely unused remnant from | ||||
| ; development. | ||||
| 88D8 | EQUB &0E ; Dead data: &0E | |||
| 88D9 | EQUB &0E ; Dead data: &0E | |||
| 88DA | EQUB &0A ; Dead data: &0A | |||
| 88DB | EQUB &0A ; Dead data: &0A | |||
| 88DC | EQUB &0A ; Dead data: &0A | |||
| 88DD | EQUB &06 ; Dead data: &06 | |||
| 88DE | EQUB &06 ; Dead data: &06 | |||
| 88DF | EQUB &0A ; Dead data: &0A | |||
| 88E0 | EQUB &81 ; Dead data: &81 | |||
| 88E1 | EQUB &00 ; Dead data: &00 | |||
| 88E2 | EQUB &00 ; Dead data: &00 | |||
| 88E3 | EQUB &00 ; Dead data: &00 | |||
| 88E4 | EQUB &00 ; Dead data: &00 | |||
| 88E5 | EQUB &01 ; Dead data: &01 | |||
| 88E6 | EQUB &01 ; Dead data: &01 | |||
| 88E7 | EQUB &81 ; Dead data: &81 | |||
Calculate transfer sizeComputes the data transfer byte count from the RXCB buffer pointers. Reads the 4-byte buffer end address from (port_ws_offset) and checks for Tube addresses (&FExx/&FFxx). For Tube transfers, claims the Tube address and sets the transfer flag in rx_src_net. Subtracts the buffer start from the buffer end to compute the byte count, storing it in port_buf_len/port_buf_len_hi. Also copies the buffer start address to open_port_buf for the RX/TX handlers to use as their working pointer. |
|
| 88E8 | .tx_calc_transfer←3← 81B4 JSR← 84CE JSR← 86D6 JSR |
| LDY #7 ; Y=7: offset to RXCB buffer addr byte 3 | |
| 88EA | LDA (port_ws_offset),y ; Read RXCB[7] (buffer addr high byte) |
| 88EC | CMP #&ff ; Compare to &FF |
| 88EE | BNE check_tx_in_progress ; Not &FF: normal buffer, skip Tube check |
| 88F0 | DEY ; Y=&06 |
| 88F1 | LDA (port_ws_offset),y ; Read RXCB[6] (buffer addr byte 2) |
| 88F3 | CMP #&fe ; Check if addr byte 2 >= &FE (Tube range) |
| 88F5 | BCS fallback_calc_transfer ; Tube/IO address: use fallback path |
| 88F7 | .check_tx_in_progress←1← 88EE BNE |
| LDA tube_present ; Transmit in progress? | |
| 88FA | BEQ fallback_calc_transfer ; No: fallback path |
| 88FC | LDA rx_src_net ; Load TX flags for transfer setup |
| 88FF | ORA #2 ; Set bit 1 (transfer complete) |
| 8901 | STA rx_src_net ; Store with bit 1 set (Tube xfer) |
| 8904 | SEC ; Init borrow for 4-byte subtract |
| 8905 | PHP ; Save carry on stack |
| 8906 | LDY #4 ; Y=4: start at RXCB offset 4 |
| 8908 | .calc_transfer_size←1← 891A BCC |
| LDA (port_ws_offset),y ; Load RXCB[Y] (current ptr byte) | |
| 890A | INY ; Y += 4: advance to RXCB[Y+4] |
| 890B | INY ; (continued) |
| 890C | INY ; (continued) |
| 890D | INY ; (continued) |
| 890E | PLP ; Restore borrow from previous byte |
| 890F | SBC (port_ws_offset),y ; Subtract RXCB[Y+4] (start ptr byte) |
| 8911 | STA net_tx_ptr,y ; Store result byte |
| 8914 | DEY ; Y -= 3: next source byte |
| 8915 | DEY ; (continued) |
| 8916 | DEY ; (continued) |
| 8917 | PHP ; Save borrow for next byte |
| 8918 | CPY #8 ; Done all 4 bytes? |
| 891A | BCC calc_transfer_size ; No: next byte pair |
| 891C | PLP ; Discard final borrow |
| 891D | TXA ; Save X |
| 891E | PHA ; Save X |
| 891F | LDA #4 ; Compute address of RXCB+4 |
| 8921 | CLC ; CLC for base pointer addition |
| 8922 | ADC port_ws_offset ; Add RXCB base to get RXCB+4 addr |
| 8924 | TAX ; X = low byte of RXCB+4 |
| 8925 | LDY rx_buf_offset ; Y = high byte of RXCB ptr |
| 8927 | LDA #&c2 ; Tube claim type &C2 |
| 8929 | JSR tube_addr_data_dispatch ; Claim Tube transfer address |
| 892C | BCC restore_x_and_return ; No Tube: skip reclaim |
| 892E | LDA rx_port ; Tube: reclaim with scout status |
| 8931 | JSR tube_addr_data_dispatch ; Reclaim with scout status type |
| 8934 | JSR release_tube ; Release Tube claim after reclaim |
| 8937 | SEC ; C=1: Tube address claimed |
| 8938 | .restore_x_and_return←1← 892C BCC |
| PLA ; Restore X | |
| 8939 | TAX ; Restore X from stack |
| 893A | RTS ; Return with C = transfer status |
| 893B | .fallback_calc_transfer←2← 88F5 BCS← 88FA BEQ |
| LDY #4 ; Y=4: RXCB current pointer offset | |
| 893D | LDA (port_ws_offset),y ; Load RXCB[4] (current ptr lo) |
| 893F | LDY #8 ; Y=8: RXCB start address offset |
| 8941 | SEC ; Set carry for subtraction |
| 8942 | SBC (port_ws_offset),y ; Subtract RXCB[8] (start ptr lo) |
| 8944 | STA port_buf_len ; Store transfer size lo |
| 8946 | LDY #5 ; Y=5: current ptr hi offset |
| 8948 | LDA (port_ws_offset),y ; Load RXCB[5] (current ptr hi) |
| 894A | SBC #0 ; Propagate borrow only |
| 894C | STA open_port_buf_hi ; Temp store of adjusted hi byte |
| 894E | LDY #8 ; Y=8: start address lo offset |
| 8950 | LDA (port_ws_offset),y ; Copy RXCB[8] to open port buffer lo |
| 8952 | STA open_port_buf ; Store to scratch (side effect) |
| 8954 | LDY #9 ; Y=9: start address hi offset |
| 8956 | LDA (port_ws_offset),y ; Load RXCB[9] |
| 8958 | SEC ; Set carry for subtraction |
| 8959 | SBC open_port_buf_hi ; Subtract adjusted hi byte |
| 895B | STA port_buf_len_hi ; Store transfer size hi |
| 895D | SEC ; Return with C=1 |
| 895E | .nmi_shim_rom_src |
| RTS ; Return with C=1 (success) | |
ADLC full resetPerforms a full ADLC hardware reset. Writes CR1=&C1 (TX_RESET|RX_RESET|AC) to put both TX and RX sections in reset with address control enabled. Then configures CR4=&1E (8-bit RX word, abort extend, NRZ encoding) and CR3=&00 (no loopback, no AEX, NRZ, no DTR). Falls through to adlc_rx_listen to re-enter RX listen mode. |
|
| 895F | .adlc_full_reset←3← 806C JSR← 80F9 JSR← 8233 JSR |
| LDA #&c1 ; CR1=&C1: TX_RESET | RX_RESET | AC (both sections in reset, address control set) | |
| 8961 | STA econet_control1_or_status1 ; Write CR1 to ADLC register 0 |
| 8964 | LDA #&1e ; CR4=&1E (via AC=1): 8-bit RX word length, abort extend enabled, NRZ encoding |
| 8966 | STA econet_data_terminate_frame ; Write CR4 to ADLC register 3 |
| 8969 | LDA #0 ; CR3=&00 (via AC=1): no loop-back, no AEX, NRZ, no DTR |
| 896B | STA econet_control23_or_status2 ; Write CR3 to ADLC register 1 |
| fall through ↓ | |
Enter RX listen modeConfigures the ADLC for passive RX listen mode. Writes CR1=&82 (TX_RESET|RIE): TX section held in reset, RX interrupts enabled. Writes CR2=&67 (CLR_TX_ST|CLR_RX_ST|FC_TDRA|2_1_BYTE|PSE) to clear all pending status and enable prioritised status. This is the idle state where the ADLC listens for incoming scout frames via NMI. |
|
| 896E | .adlc_rx_listen←2← 83EE JSR← 899A JMP |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (TX in reset, RX interrupts enabled) | |
| 8970 | STA econet_control1_or_status1 ; Write to ADLC CR1 |
| 8973 | LDA #&67 ; CR2=&67: CLR_TX_ST | CLR_RX_ST | FC_TDRA | 2_1_BYTE | PSE |
| 8975 | STA econet_control23_or_status2 ; Write to ADLC CR2 |
| 8978 | RTS ; Return; ADLC now in RX listen mode |
Wait for idle NMI state and reset EconetService 12 handler: NMI release. Checks ws_0d62 to see if Econet has been initialised; if not, skips straight to adlc_rx_listen. Otherwise spins in a tight loop comparing the NMI handler vector at &0D0C/&0D0D against the address of nmi_rx_scout (&80B3). When the NMI handler returns to idle, falls through to save_econet_state to clear the initialised flags and re-enter RX listen mode. |
|
| 8979 | .wait_idle_and_reset |
| BIT ws_0d62 ; Check if Econet has been initialised | |
| 897C | BPL reset_enter_listen ; Not initialised: skip to RX listen |
| 897E | .poll_nmi_idle←2← 8983 BNE← 898A BNE |
| LDA nmi_jmp_lo ; Read current NMI handler low byte | |
| 8981 | CMP #&b3 ; Expected: &B3 (nmi_rx_scout low) |
| 8983 | BNE poll_nmi_idle ; Not idle: spin and wait |
| 8985 | LDA nmi_jmp_hi ; Read current NMI handler high byte |
| 8988 | EOR #&80 ; Test if high byte = &80 (page of nmi_rx_scout) |
| 898A | BNE poll_nmi_idle ; Not idle: spin and wait |
| fall through ↓ | |
Reset Econet flags and enter RX listenDisables NMIs via two reads of &FE18 (INTOFF), then clears ws_0d60 (TX complete) and ws_0d62 (Econet initialised) by storing the current A value. Sets Y=5 (service call workspace page) and jumps to adlc_rx_listen to configure the ADLC for passive listening. Used during NMI release (service 12) to safely tear down the Econet state before another ROM can claim the NMI workspace. |
|
| 898C | .save_econet_state |
| BIT station_id_disable_net_nmis ; INTOFF: disable NMIs | |
| 898F | BIT station_id_disable_net_nmis ; INTOFF again (belt-and-braces) |
| 8992 | STA ws_0d60 ; TX not in progress |
| 8995 | STA ws_0d62 ; Econet not initialised |
| 8998 | LDY #5 ; Y=5: service call workspace page |
| 899A | .reset_enter_listen←1← 897C BPL |
| JMP adlc_rx_listen ; Set ADLC to RX listen mode | |
Bootstrap NMI entry point (in ROM)An alternate NMI handler that lives in the ROM itself rather than in the RAM workspace at &0D00. Unlike the RAM shim (which uses a self-modifying JMP to dispatch to different handlers), this one hardcodes JMP nmi_rx_scout (&80B3). Used as the initial NMI handler before the workspace has been properly set up during initialisation. Same sequence as the RAM shim: BIT &FE18 (INTOFF), PHA, TYA, PHA, LDA romsel, STA &FE30, JMP &80B3. |
|
| 899D | .nmi_bootstrap_entry |
| BIT station_id_disable_net_nmis ; INTOFF: disable NMIs while switching ROM | |
| 89A0 | PHA ; Save A |
| 89A1 | TYA ; Transfer Y to A |
| 89A2 | PHA ; Save Y (via A) |
| 89A3 | LDA #0 ; ROM bank 0 (patched during init for actual bank) |
| 89A5 | STA romsel ; Select Econet ROM bank via ROMSEL |
| 89A8 | JMP nmi_rx_scout ; Jump to scout handler in ROM |
ROM copy of set_nmi_vector + nmi_rti |
|
| 89AB | .rom_set_nmi_vector |
| STY nmi_jmp_hi ; Store handler high byte at &0D0D | |
| 89AE | STA nmi_jmp_lo ; Store handler low byte at &0D0C |
| 89B1 | LDA romsel_copy ; Restore NFS ROM bank |
| 89B3 | STA romsel ; Page in via hardware latch |
| 89B6 | PLA ; Restore Y from stack |
| 89B7 | TAY ; Transfer ROM bank to Y |
| 89B8 | PLA ; Restore A from stack |
| 89B9 | BIT video_ula_control ; INTON: re-enable NMIs |
| 89BC | RTI ; Return from interrupt |
| ; Unreferenced dead data (3 bytes) | |
| ; 3 bytes between the RTI at &89BC (end of the NMI | |
| ; shim ROM source) and svc_dispatch_lo at &89C0. | |
| ; The init copy loop (Y=1..&20) copies &899D-&89BC | |
| ; to &0D00-&0D1F; these bytes are outside that range | |
| ; and unreferenced. Likely unused development remnant. | |
| 89BD | EQUB &01 ; Dead data: &01 |
| 89BE | EQUB &00 ; Dead data: &00 |
| 89BF | EQUB &08 ; Dead data: &08 |
| ; Service dispatch table (37 entries, split lo/hi). | |
| ; PHA/PHA/RTS dispatch used by svc_dispatch. | |
| ; Indices 0-14: service calls (index = service + 1). | |
| ; Indices 15-36: FS command and OSWORD routing. | |
| ; Indices 1, 7, 11 point to return_4 (no-op RTS). | |
| 89C0 | .svc_dispatch_lo←1← 8E3C LDA |
| EQUB &04 ; lo - dummy entry (outside ROM range) | |
| 89C1 | EQUB <(dispatch_rts-1) ; lo - Svc 0: already claimed (no-op) |
| 89C2 | EQUB <(svc_1_abs_workspace-1) ; lo - Svc 1: absolute workspace |
| 89C3 | EQUB <(svc_2_private_workspace-1) ; lo - Svc 2: private workspace |
| 89C4 | EQUB <(svc_3_autoboot-1) ; lo - Svc 3: auto-boot |
| 89C5 | EQUB <(svc_4_star_command-1) ; lo - Svc 4: unrecognised star command |
| 89C6 | EQUB <(svc5_irq_check-1) ; lo - Svc 5: unrecognised interrupt |
| 89C7 | EQUB <(dispatch_rts-1) ; lo - Svc 6: BRK (no-op) |
| 89C8 | EQUB <(svc_7_osbyte-1) ; lo - Svc 7: unrecognised OSBYTE |
| 89C9 | EQUB <(svc_8_osword-1) ; lo - Svc 8: unrecognised OSWORD |
| 89CA | EQUB <(svc_9_help-1) ; lo - Svc 9: *HELP |
| 89CB | EQUB <(dispatch_rts-1) ; lo - Svc 10: static workspace (no-op) |
| 89CC | EQUB <(econet_restore-1) ; lo - Svc 11: NMI release (reclaim NMIs) |
| 89CD | EQUB <(wait_idle_and_reset-1) ; lo - Svc 12: NMI claim (save NMI state) |
| 89CE | EQUB <(svc_18_fs_select-1) ; lo - Svc 18: filing system selection |
| 89CF | EQUB <(lang_0_insert_remote_key-1) ; lo - Lang 0: no language / Tube |
| 89D0 | EQUB <(lang_1_remote_boot-1) ; lo - Lang 1: normal startup |
| 89D1 | EQUB <(lang_2_save_palette_vdu-1) ; lo - Lang 2: softkey byte (Electron) |
| 89D2 | EQUB <(lang_3_execute_at_0100-1) ; lo - Lang 3: softkey length (Electron) |
| 89D3 | EQUB <(lang_4_remote_validated-1) ; lo - Lang 4: remote validated |
| 89D4 | EQUB <(fscv_0_opt_entry-1) ; lo - FSCV 0: *OPT |
| 89D5 | EQUB <(fscv_1_eof-1) ; lo - FSCV 1: EOF check |
| 89D6 | EQUB <(fscv_2_star_run-1) ; lo - FSCV 2: */ (run) |
| 89D7 | EQUB <(fscv_3_star_cmd-1) ; lo - FSCV 3: unrecognised star command |
| 89D8 | EQUB <(fscv_2_star_run-1) ; lo - FSCV 4: *RUN |
| 89D9 | EQUB <(fscv_5_cat-1) ; lo - FSCV 5: *CAT |
| 89DA | EQUB <(fscv_6_shutdown-1) ; lo - FSCV 6: shutdown |
| 89DB | EQUB <(fscv_7_read_handles-1) ; lo - FSCV 7: read handle range |
| 89DC | EQUB <(fsreply_0_print_dir-1) ; lo - FS reply: print directory name |
| 89DD | EQUB <(fsreply_1_copy_handles_boot-1) ; lo - FS reply: copy handles + boot |
| 89DE | EQUB <(fsreply_2_copy_handles-1) ; lo - FS reply: copy handles |
| 89DF | EQUB <(fsreply_3_set_csd-1) ; lo - FS reply: set CSD handle |
| 89E0 | EQUB <(fscv_2_star_run-1) ; lo - FS reply: notify + execute |
| 89E1 | EQUB <(fsreply_5_set_lib-1) ; lo - FS reply: set library handle |
| 89E2 | EQUB <(net_1_read_handle-1) ; lo - *NET1: read handle from packet |
| 89E3 | EQUB <(net_2_read_handle_entry-1) ; lo - *NET2: read handle from workspace |
| 89E4 | EQUB <(net_3_close_handle-1) ; lo - *NET3: close handle |
| 89E5 | .svc_dispatch_hi←1← 8E38 LDA |
| EQUB &CB ; hi - dummy entry (outside ROM range) | |
| 89E6 | EQUB >(dispatch_rts-1) ; hi - Svc 0: already claimed (no-op) |
| 89E7 | EQUB >(svc_1_abs_workspace-1) ; hi - Svc 1: absolute workspace |
| 89E8 | EQUB >(svc_2_private_workspace-1) ; hi - Svc 2: private workspace |
| 89E9 | EQUB >(svc_3_autoboot-1) ; hi - Svc 3: auto-boot |
| 89EA | EQUB >(svc_4_star_command-1) ; hi - Svc 4: unrecognised star command |
| 89EB | EQUB >(svc5_irq_check-1) ; hi - Svc 5: unrecognised interrupt |
| 89EC | EQUB >(dispatch_rts-1) ; hi - Svc 6: BRK (no-op) |
| 89ED | EQUB >(svc_7_osbyte-1) ; hi - Svc 7: unrecognised OSBYTE |
| 89EE | EQUB >(svc_8_osword-1) ; hi - Svc 8: unrecognised OSWORD |
| 89EF | EQUB >(svc_9_help-1) ; hi - Svc 9: *HELP |
| 89F0 | EQUB >(dispatch_rts-1) ; hi - Svc 10: static workspace (no-op) |
| 89F1 | EQUB >(econet_restore-1) ; hi - Svc 11: NMI release (reclaim NMIs) |
| 89F2 | EQUB >(wait_idle_and_reset-1) ; hi - Svc 12: NMI claim (save NMI state) |
| 89F3 | EQUB >(svc_18_fs_select-1) ; hi - Svc 18: filing system selection |
| 89F4 | EQUB >(lang_0_insert_remote_key-1) ; hi - Lang 0: no language / Tube |
| 89F5 | EQUB >(lang_1_remote_boot-1) ; hi - Lang 1: normal startup |
| 89F6 | EQUB >(lang_2_save_palette_vdu-1) ; hi - Lang 2: softkey byte (Electron) |
| 89F7 | EQUB >(lang_3_execute_at_0100-1) ; hi - Lang 3: softkey length (Electron) |
| 89F8 | EQUB >(lang_4_remote_validated-1) ; hi - Lang 4: remote validated |
| 89F9 | EQUB >(fscv_0_opt_entry-1) ; hi - FSCV 0: *OPT |
| 89FA | EQUB >(fscv_1_eof-1) ; hi - FSCV 1: EOF check |
| 89FB | EQUB >(fscv_2_star_run-1) ; hi - FSCV 2: */ (run) |
| 89FC | EQUB >(fscv_3_star_cmd-1) ; hi - FSCV 3: unrecognised star command |
| 89FD | EQUB >(fscv_2_star_run-1) ; hi - FSCV 4: *RUN |
| 89FE | EQUB >(fscv_5_cat-1) ; hi - FSCV 5: *CAT |
| 89FF | EQUB >(fscv_6_shutdown-1) ; hi - FSCV 6: shutdown |
| 8A00 | EQUB >(fscv_7_read_handles-1) ; hi - FSCV 7: read handle range |
| 8A01 | EQUB >(fsreply_0_print_dir-1) ; hi - FS reply: print directory name |
| 8A02 | EQUB >(fsreply_1_copy_handles_boot-1) ; hi - FS reply: copy handles + boot |
| 8A03 | EQUB >(fsreply_2_copy_handles-1) ; hi - FS reply: copy handles |
| 8A04 | EQUB >(fsreply_3_set_csd-1) ; hi - FS reply: set CSD handle |
| 8A05 | EQUB >(fscv_2_star_run-1) ; hi - FS reply: notify + execute |
| 8A06 | EQUB >(fsreply_5_set_lib-1) ; hi - FS reply: set library handle |
| 8A07 | EQUB >(net_1_read_handle-1) ; hi - *NET1: read handle from packet |
| 8A08 | EQUB >(net_2_read_handle_entry-1) ; hi - *NET2: read handle from workspace |
| 8A09 | EQUB >(net_3_close_handle-1) ; hi - *NET3: close handle |
| 8A0A | EQUB &8A |
Service call dispatchHandles service calls 1, 4, 8, 9, 13, 14, and 15. Service 1: absolute workspace claim. Service 4: unrecognised star command. Service 8: unrecognised OSWORD. Service 9: *HELP. Service 13: ROM initialisation. Service 14: ROM initialisation complete. Service 15: vectors claimed.
|
||||||||
| 8A0B | .service_handler←1← 8003 JMP | |||||||
| PHA ; Save service call number | ||||||||
| 8A0C | CMP #&0f ; Is it service 15 (vectors claimed)? | |||||||
| 8A0E | BNE dispatch_service ; No: skip vectors-claimed handling | |||||||
| 8A10 | TYA ; Save Y parameter | |||||||
| 8A11 | PHA ; Save Y on stack | |||||||
| 8A12 | LDA #osbyte_read_os_version ; OSBYTE 0: read OS version | |||||||
| 8A14 | LDX #1 ; X=1 to request version number | |||||||
| 8A16 | JSR osbyte ; Read OS version number into X | |||||||
| ; X is the OS version number: | ||||||||
| ; X=0, OS 1.00 (Early BBC B or Electron OS 1.00) | ||||||||
| ; X=1, OS 1.20 or American OS | ||||||||
| ; X=2, OS 2.00 (BBC B+) | ||||||||
| ; X=3, OS 3.2/3.5 (Master 128) | ||||||||
| ; X=4, OS 4.0 (Master Econet Terminal) | ||||||||
| ; X=5, OS 5.0 (Master Compact) | ||||||||
| 8A19 | CPX #1 ; OS 1.20? | |||||||
| 8A1B | BEQ restore_rom_slot ; Yes: skip workspace setup | |||||||
| 8A1D | CPX #2 ; OS 2.00 (BBC B+)? | |||||||
| 8A1F | BEQ restore_rom_slot ; Yes: skip workspace setup | |||||||
| 8A21 | TXA ; Transfer OS version to A | |||||||
| 8A22 | PHP ; Save flags (Z set if OS 1.00) | |||||||
| 8A23 | LDX romsel_copy ; Get current ROM slot number | |||||||
| 8A25 | PLP ; Restore flags | |||||||
| 8A26 | BEQ clear_workspace_byte ; OS 1.00: skip INX | |||||||
| 8A28 | INX ; Adjust index for OS 3+ workspace | |||||||
| 8A29 | .clear_workspace_byte←1← 8A26 BEQ | |||||||
| LDA #0 ; A=0 | ||||||||
| 8A2B | STA rom_type_table,x ; Clear workspace byte for this ROM | |||||||
| 8A2E | .restore_rom_slot←2← 8A1B BEQ← 8A1F BEQ | |||||||
| LDX romsel_copy ; Restore ROM slot to X | ||||||||
| 8A30 | PLA ; Restore Y parameter | |||||||
| 8A31 | TAY ; Transfer to Y | |||||||
| 8A32 | .dispatch_service←1← 8A0E BNE | |||||||
| PLA ; Restore service call number | ||||||||
| 8A33 | JSR tube_vdu_dispatch ; Check relocated code service dispatch | |||||||
| 8A36 | PHA ; Save service call number | |||||||
| 8A37 | CMP #1 ; Service 1 (workspace claim)? | |||||||
| 8A39 | BNE check_adlc_flag ; No: skip ADLC check | |||||||
| 8A3B | LDA econet_control1_or_status1 ; Read ADLC status register 1 | |||||||
| 8A3E | AND #&ed ; Mask relevant status bits | |||||||
| 8A40 | BNE set_adlc_absent ; Non-zero: ADLC absent, set flag | |||||||
| 8A42 | LDA econet_control23_or_status2 ; Read ADLC status register 2 | |||||||
| 8A45 | AND #&db ; Mask relevant status bits | |||||||
| 8A47 | BEQ check_adlc_flag ; Zero: ADLC present, skip | |||||||
| 8A49 | .set_adlc_absent←1← 8A40 BNE | |||||||
| ROL rom_ws_pages,x ; Shift bit 7 into carry | ||||||||
| 8A4C | SEC ; Set carry to mark ADLC absent | |||||||
| 8A4D | ROR rom_ws_pages,x ; Rotate carry into bit 7 of slot flag | |||||||
| 8A50 | .check_adlc_flag←2← 8A39 BNE← 8A47 BEQ | |||||||
| LDA rom_ws_pages,x ; Load ROM slot flag byte | ||||||||
| 8A53 | ASL ; Shift bit 7 (ADLC absent) into carry | |||||||
| 8A54 | PLA ; Restore service call number | |||||||
| 8A55 | BCC handle_vectors_claimed ; ADLC present: continue dispatch | |||||||
| 8A57 | RTS ; ADLC absent: decline service, return | |||||||
| 8A58 | .handle_vectors_claimed←1← 8A55 BCC | |||||||
| CMP #&0f ; Service 15 (vectors claimed)? | ||||||||
| 8A5A | BNE dispatch_svc_with_state ; No: handle other services | |||||||
| 8A5C | LDX ws_0d6a ; Already initialised? | |||||||
| 8A5F | BNE init_rom_scan ; Yes: skip first-time init | |||||||
| 8A61 | INX ; X=1 (mark as initialised) | |||||||
| 8A62 | STX last_break_type ; Set ROM present flag | |||||||
| 8A65 | .init_rom_scan←1← 8A5F BNE | |||||||
| STA error_block ; Store service number as ROM counter | ||||||||
| 8A68 | .loop_scan_net_roms←1← 8A93 BPL | |||||||
| LDA #&80 ; Point to ROM header copyright offset | ||||||||
| 8A6A | STA osrdsc_ptr_hi ; Set high byte of OSRDSC pointer | |||||||
| 8A6C | LDA #&0c ; Offset &0C: copyright string offset | |||||||
| 8A6E | STA osrdsc_ptr ; Set low byte of OSRDSC pointer | |||||||
| 8A70 | JSR read_paged_rom ; Read next ROM title char | |||||||
| 8A73 | CMP #&4e ; First char 'N'? | |||||||
| 8A75 | BNE next_rom_slot ; No: not a NET ROM, try next | |||||||
| 8A77 | JSR read_paged_rom ; Read next ROM title char | |||||||
| 8A7A | CMP #&45 ; Second char 'E'? | |||||||
| 8A7C | BNE next_rom_slot ; No: not a NET ROM, try next | |||||||
| 8A7E | JSR read_paged_rom ; Read next ROM title char | |||||||
| 8A81 | CMP #&54 ; Third char 'T'? | |||||||
| 8A83 | BNE next_rom_slot ; No: not a NET ROM, try next | |||||||
| 8A85 | LDX error_block ; Get ROM slot being checked | |||||||
| 8A88 | LDA rom_ws_pages,x ; Load its slot flag byte | |||||||
| 8A8B | ORA #&80 ; Set bit 7 to mark as NET ROM | |||||||
| 8A8D | STA rom_ws_pages,x ; Store updated flag | |||||||
| 8A90 | .next_rom_slot←3← 8A75 BNE← 8A7C BNE← 8A83 BNE | |||||||
| DEC error_block ; Decrement ROM counter | ||||||||
| 8A93 | BPL loop_scan_net_roms ; More ROMs to check: loop | |||||||
| 8A95 | BMI restore_romsel_rts ; ALWAYS branch | |||||||
Read next byte from paged ROM via OSRDSCIncrements the read pointer at osrdsc_ptr (&F6) first, then calls OSRDSC (&FFB9) with the ROM number from error_block (&0100) in Y. Called three times by service_handler during ROM identification to read the copyright string and ROM type byte.
|
||||
| 8A97 | .read_paged_rom←3← 8A70 JSR← 8A77 JSR← 8A7E JSR | |||
| INC osrdsc_ptr ; Advance read pointer to next byte | ||||
| 8A99 | LDY error_block ; Y=ROM number | |||
| 8A9C | JMP osrdsc ; Read byte from ROM Y or screen | |||
| 8A9F | .dispatch_svc_with_state←1← 8A5A BNE | |||
| TAX ; Transfer service number to X | ||||
| 8AA0 | LDA svc_state ; Save current service state | |||
| 8AA2 | PHA ; Push old state | |||
| 8AA3 | TXA ; Restore service number to A | |||
| 8AA4 | STA svc_state ; Store as current service state | |||
| 8AA6 | CMP #&0d ; Service < 13? | |||
| 8AA8 | BCC dispatch_svc_index ; Yes: use as dispatch index directly | |||
| 8AAA | SBC #5 ; Subtract 5 (map 13-17 to 8-12) | |||
| 8AAC | CMP #&0d ; Mapped value = 13? (original was 18) | |||
| 8AAE | BEQ dispatch_svc_index ; Yes: valid service 18 (FS select) | |||
| 8AB0 | LDA #0 ; Unknown service: set index to 0 | |||
| 8AB2 | .dispatch_svc_index←2← 8AA8 BCC← 8AAE BEQ | |||
| TAX ; Transfer dispatch index to X | ||||
| 8AB3 | BEQ restore_svc_state ; Index 0: unhandled service, skip | |||
| 8AB5 | LDA ws_page ; Save current workspace page | |||
| 8AB7 | PHA ; Push old page | |||
| 8AB8 | STY ws_page ; Set workspace page from Y parameter | |||
| 8ABA | TYA ; Transfer Y to A | |||
| 8ABB | LDY #0 ; Y=0 for dispatch offset | |||
| 8ABD | JSR svc_dispatch ; Dispatch to service handler via table | |||
| 8AC0 | PLA ; Restore old workspace page | |||
| 8AC1 | STA ws_page ; Store it back | |||
| 8AC3 | .restore_svc_state←1← 8AB3 BEQ | |||
| LDX svc_state ; Get service state (return code) | ||||
| 8AC5 | PLA ; Restore old service state | |||
| 8AC6 | STA svc_state ; Store it back | |||
| 8AC8 | TXA ; Transfer return code to A | |||
| 8AC9 | .restore_romsel_rts←1← 8A95 BMI | |||
| LDX romsel_copy ; Restore ROM slot to X | ||||
| 8ACB | RTS ; Return to MOS | |||
*ROFF command handlerDisables remote operation by clearing the flag at offset 4 in the receive block. If remote operation was active, re-enables the keyboard via OSBYTE &C9 (with X=0, Y=0) and calls tx_econet_abort with A=&0A to reinitialise the workspace area. Falls through to scan_remote_keys which clears svc_state and nfs_workspace. |
|
| 8ACC | .cmd_roff |
| LDY #4 ; Offset 4 in receive block | |
| 8ACE | LDA (net_rx_ptr),y ; Load remote operation flag |
| 8AD0 | BEQ clear_svc_and_ws ; Zero: already off, skip to cleanup |
| 8AD2 | LDA #0 ; A=0 |
| 8AD4 | TAX ; X=&00 |
| 8AD5 | STA (net_rx_ptr),y ; Clear remote operation flag |
| 8AD7 | TAY ; Y=&00 |
| 8AD8 | LDA #osbyte_read_write_econet_keyboard_disable ; OSBYTE &C9: keyboard disable |
| 8ADA | JSR osbyte ; Enable keyboard (for Econet) |
| 8ADD | LDA #&0a ; A=&0A: workspace init parameter |
| 8ADF | JSR tx_econet_abort ; Initialise workspace area |
| fall through ↓ | |
Scan keyboard for remote operation keysUses OSBYTE &7A with Y=&7F to check whether remote operation keys (&CE-&CF) are currently pressed. If neither key is detected, clears svc_state and nfs_workspace to zero via the clear_svc_and_ws entry point, which is also used directly by cmd_roff. Called by check_escape. |
|
| 8AE2 | .scan_remote_keys←1← 9589 JSR |
| STX nfs_workspace ; Save X in workspace | |
| 8AE4 | LDA #&ce ; A=&CE: start of key range |
| 8AE6 | .loop_scan_key_range←1← 8AF1 BEQ |
| LDX nfs_workspace ; Restore X from workspace | |
| 8AE8 | LDY #&7f ; Y=&7F: OSBYTE scan parameter |
| 8AEA | JSR osbyte ; OSBYTE: scan keyboard |
| 8AED | ADC #1 ; Advance to next key code |
| 8AEF | CMP #&d0 ; Reached &D0? |
| 8AF1 | BEQ loop_scan_key_range ; No: loop back (scan &CE and &CF) |
| 8AF3 | .clear_svc_and_ws←1← 8AD0 BEQ |
| LDA #0 ; A=0 | |
| 8AF5 | STA svc_state ; Clear service state |
| 8AF7 | STA nfs_workspace ; Clear workspace byte |
| 8AF9 | RTS ; Return |
Save OS text pointer for later retrievalCopies &F2/&F3 into fs_crc_lo/fs_crc_hi. Called by svc_4_star_command and svc_9_help before attempting command matches, and by match_fs_cmd during iterative help topic matching. Preserves A via PHA/PLA.
|
||||
| 8AFA | .save_text_ptr←3← 8C47 JSR← 8C72 JSR← A2A7 JSR | |||
| PHA ; Save A | ||||
| 8AFB | LDA os_text_ptr ; Copy OS text pointer low | |||
| 8AFD | STA fs_crc_lo ; to fs_crc_lo | |||
| 8AFF | LDA os_text_ptr_hi ; Copy OS text pointer high | |||
| 8B01 | STA fs_crc_hi ; to fs_crc_hi | |||
| 8B03 | PLA ; Restore A | |||
| 8B04 | .return_from_save_text_ptr←2← 8B07 BNE← 8B0C BMI | |||
| RTS ; Return | ||||
Service 18: filing system selection requestChecks if Y=5 (Econet filing system number); returns unclaimed if not. Also returns if bit 7 of &0D6C is already set, indicating the FS is already selected. Otherwise falls through to cmd_net_fs to perform the full network filing system selection sequence.
|
||||
| 8B05 | .svc_18_fs_select | |||
| CPY #5 ; Y=5 (Econet filing system)? | ||||
| 8B07 | BNE return_from_save_text_ptr ; No: not ours, return unclaimed | |||
| 8B09 | BIT fs_flags ; Already selected? | |||
| 8B0C | BMI return_from_save_text_ptr ; Yes (bit 7 set): return unclaimed | |||
| fall through ↓ | ||||
Select Econet network filing systemComputes a checksum over the first &77 bytes of the workspace page and verifies against the stored value; raises an error on mismatch. On success, notifies the OS via FSCV reason 6, copies the FS context block from the receive block to &0DFA, installs 7 filing system vectors (FILEV etc.) from fs_vector_table, initialises the ADLC and extended vectors, sets up the channel table, and copies the workspace page to &1000 as a shadow. Sets bit 7 of &0D6C to mark the FS as selected, then issues service call 15. |
|
| 8B0E | .cmd_net_fs←1← 8CD5 JSR |
| JSR get_ws_page ; Get workspace page for this ROM slot | |
| 8B11 | STA fs_load_addr_hi ; Store as high byte of load address |
| 8B13 | LDA #0 ; A=0 |
| 8B15 | STA fs_load_addr ; Clear low byte of load address |
| 8B17 | CLC ; Clear carry for addition |
| 8B18 | LDY #&76 ; Y=&76: checksum range end |
| 8B1A | .loop_sum_rom_bytes←1← 8B1D BPL |
| ADC (fs_load_addr),y ; Add byte to running checksum | |
| 8B1C | DEY ; Decrement index |
| 8B1D | BPL loop_sum_rom_bytes ; Loop until all bytes summed |
| 8B1F | LDY #&77 ; Y=&77: checksum storage offset |
| 8B21 | EOR (fs_load_addr),y ; Compare with stored checksum |
| 8B23 | BEQ done_rom_checksum ; Match: checksum valid |
| 8B25 | JMP error_net_checksum ; Mismatch: raise checksum error |
| 8B28 | .done_rom_checksum←1← 8B23 BEQ |
| JSR notify_new_fs ; Call FSCV with A=6 (new FS) | |
| 8B2B | LDY #&0d ; Y=&0D: end of FS context block |
| 8B2D | .loop_copy_fs_ctx←1← 8B35 BNE |
| LDA (net_rx_ptr),y ; Load byte from receive block | |
| 8B2F | STA fs_context_save,y ; Store into FS workspace |
| 8B32 | DEY ; Decrement index |
| 8B33 | CPY #5 ; Reached offset 5? |
| 8B35 | BNE loop_copy_fs_ctx ; No: continue copying |
| 8B37 | ROL fs_flags ; Shift bit 7 of FS flags into carry |
| 8B3A | CLC ; Clear carry |
| 8B3B | ROR fs_flags ; Clear bit 7 of FS flags |
| 8B3E | LDY #&0d ; Y=&0D: vector table size - 1 |
| 8B40 | .loop_set_vectors←1← 8B47 BPL |
| LDA fs_vector_table,y ; Load FS vector address | |
| 8B43 | STA filev,y ; Store into FILEV vector table |
| 8B46 | DEY ; Decrement index |
| 8B47 | BPL loop_set_vectors ; Loop until all vectors installed |
| 8B49 | JSR init_adlc_and_vectors ; Initialise ADLC and NMI workspace |
| 8B4C | LDY #&1b ; Y=&1B: extended vector offset |
| 8B4E | LDX #7 ; X=7: two more vectors to set up |
| 8B50 | JSR write_vector_entry ; Set up extended vectors |
| 8B53 | LDA #0 ; A=0 |
| 8B55 | STA fs_eof_flags ; Clear FS state byte |
| 8B58 | STA cur_chan_attr ; Clear workspace byte |
| 8B5B | STA fs_lib_flags ; Clear workspace byte |
| 8B5E | STA svc_state ; Clear service state |
| 8B60 | LDY #&0e ; Offset &0E in receive block |
| 8B62 | STA (net_rx_ptr),y ; Clear receive block flag |
| 8B64 | STA net_context ; Clear workspace byte |
| 8B67 | JSR setup_ws_ptr ; Set up workspace pointers |
| 8B6A | JSR init_channel_table ; Initialise FS state |
| 8B6D | LDY #&77 ; Y=&77: workspace block size - 1 |
| 8B6F | .loop_copy_ws_page←1← 8B75 BPL |
| LDA (fs_ws_ptr),y ; Load byte from source workspace | |
| 8B71 | STA fcb_count_lo,y ; Store to page &10 shadow copy |
| 8B74 | DEY ; Decrement index |
| 8B75 | BPL loop_copy_ws_page ; Loop until all bytes copied |
| 8B77 | LDA #&80 ; A=&80: FS selected flag |
| 8B79 | ORA fs_flags ; Set bit 7 of FS flags |
| 8B7C | STA fs_flags ; Store updated flags |
| 8B7F | JMP issue_svc_15 ; Issue service 15 (FS initialised) |
| 8B82 | .help_print_nfs_cmds←1← 8C6F JMP |
| LDX #&4a ; X=&4A: NFS command table offset | |
| 8B84 | JSR print_cmd_table ; Print help for NFS commands |
| fall through ↓ | |
*HELP UTILS topic handlerSets X=0 to select the utility command sub-table and branches to print_cmd_table to display the command list. Prints the version header followed by all utility commands. |
|
| 8B87 | .help_utils |
| LDX #0 ; X=0: utility command table offset | |
| 8B89 | BEQ print_cmd_table ; ALWAYS branch |
*HELP NET topic handlerSets X to &4A (the NFS command sub-table offset) and falls through to print_cmd_table to display the NFS command list with version header. |
|
| 8B8B | .help_net |
| LDX #&4a ; X=&4A: NFS command table offset | |
| fall through ↓ | |
Print *HELP command listing with optional headerIf V flag is set, saves X/Y, calls print_version_header to show the ROM version string and station number, then restores X/Y. If V flag is clear, outputs a newline only. Either path then falls through to print_cmd_table_loop to enumerate commands.
|
||||||
| 8B8D | .print_cmd_table←2← 8B84 JSR← 8B89 BEQ | |||||
| BVC print_table_newline ; V clear: need to print header first | ||||||
| 8B8F | TXA ; Save X (table offset) | |||||
| 8B90 | PHA ; Push it | |||||
| 8B91 | TYA ; Save Y | |||||
| 8B92 | PHA ; Push it | |||||
| 8B93 | JSR print_version_header ; Print version string header | |||||
| 8B96 | PLA ; Restore Y | |||||
| 8B97 | TAY ; Transfer to Y | |||||
| 8B98 | PLA ; Restore X | |||||
| 8B99 | TAX ; Transfer to X | |||||
| 8B9A | CLV ; Clear overflow flag | |||||
| 8B9B | BVC print_cmd_table_loop ; ALWAYS branch | |||||
| 8B9D | .print_table_newline←1← 8B8D BVC | |||||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||||
| fall through ↓ | ||||||
Enumerate and print command table entriesWalks the ANFS command table from offset X, printing each command name padded to 9 characters followed by its syntax description. Entries with bit 7 set mark end-of-table. The syntax descriptor byte's low 5 bits index into cmd_syntax_table; index &0E triggers special handling that lists shared command names in parentheses. Calls help_wrap_if_serial to handle line continuation on serial output streams. Preserves Y.
|
||||
| 8BA0 | .print_cmd_table_loop←2← 8B9B BVC← 8C62 JSR | |||
| TYA ; Save Y (command line offset) | ||||
| 8BA1 | PHA ; Push it | |||
| 8BA2 | PHP ; Save processor status | |||
| 8BA3 | .loop_next_entry←1← 8C21 JMP | |||
| LDA cmd_table_fs,x ; Load byte from command table | ||||
| 8BA6 | BPL print_indent ; Bit 7 clear: valid entry, continue | |||
| 8BA8 | JMP done_print_table ; End of table: finish up | |||
| 8BAB | .print_indent←1← 8BA6 BPL | |||
| JSR print_inline ; Print two-space indent | ||||
| 8BAE | EQUS " " | |||
| 8BB0 | LDY #9 ; Y=9: max command name length | |||
| 8BB2 | LDA cmd_table_fs,x ; Load first char of command name | |||
| 8BB5 | .loop_print_name←1← 8BBD BPL | |||
| JSR osasci ; Write character | ||||
| 8BB8 | INX ; Advance table pointer | |||
| 8BB9 | DEY ; Decrement padding counter | |||
| 8BBA | LDA cmd_table_fs,x ; Load next character | |||
| 8BBD | BPL loop_print_name ; Bit 7 clear: more chars, continue | |||
| 8BBF | .loop_pad_spaces←1← 8BC5 BPL | |||
| LDA #&20 ; Pad with spaces | ||||
| 8BC1 | JSR osasci ; Write character 32 | |||
| 8BC4 | DEY ; Decrement remaining pad count | |||
| 8BC5 | BPL loop_pad_spaces ; More padding needed: loop | |||
| 8BC7 | LDA cmd_table_fs,x ; Load syntax descriptor byte | |||
| 8BCA | AND #&1f ; Mask to get syntax string index | |||
| 8BCC | CMP #&0e ; Index &0E: shared commands? | |||
| 8BCE | BEQ print_shared_prefix ; Yes: handle shared commands list | |||
| 8BD0 | TAY ; Use index as Y | |||
| 8BD1 | LDA cmd_syntax_table,y ; Look up syntax string offset | |||
| 8BD4 | TAY ; Transfer offset to Y | |||
| 8BD5 | .loop_print_syntax←2← 8BE2 JMP← 8BE8 JMP | |||
| INY ; Advance to next character | ||||
| 8BD6 | LDA cmd_syntax_strings,y ; Load syntax string character | |||
| 8BD9 | BEQ done_entry_newline ; Zero terminator: end of syntax | |||
| 8BDB | CMP #&0d ; Carriage return: line continuation | |||
| 8BDD | BNE print_syntax_char ; No: print the character | |||
| 8BDF | JSR help_wrap_if_serial ; Handle line wrap in syntax output | |||
| 8BE2 | JMP loop_print_syntax ; Continue with next character | |||
| 8BE5 | .print_syntax_char←1← 8BDD BNE | |||
| JSR osasci ; Write character | ||||
| 8BE8 | JMP loop_print_syntax ; Continue with next character | |||
| 8BEB | .print_shared_prefix←1← 8BCE BEQ | |||
| TXA ; Save table pointer | ||||
| 8BEC | PHA ; Push it | |||
| 8BED | JSR print_inline ; Print opening parenthesis | |||
| 8BF0 | EQUS "(" | |||
| 8BF1 | LDY #0 ; Y=0: shared command counter | |||
| 8BF3 | LDX #&d3 ; X=&D3: shared command table start | |||
| 8BF5 | .loop_next_shared←1← 8C17 BNE | |||
| LDA cmd_table_fs,x ; Load byte from shared command table | ||||
| 8BF8 | BMI done_shared_cmds ; Bit 7 set: end of shared commands | |||
| 8BFA | DEX ; Back up one position | |||
| 8BFB | .loop_print_shared←1← 8C04 JMP | |||
| INX ; Advance to next character | ||||
| 8BFC | LDA cmd_table_fs,x ; Load command name character | |||
| 8BFF | BMI print_last_char ; Bit 7 set: end of this name | |||
| 8C01 | JSR osasci ; Write character | |||
| 8C04 | JMP loop_print_shared ; Print more characters of name | |||
| 8C07 | .print_last_char←1← 8BFF BMI | |||
| AND #&7f ; Strip bit 7 from final character | ||||
| 8C09 | JSR osasci ; Write character | |||
| 8C0C | INY ; Count this shared command | |||
| 8C0D | CPY #4 ; Printed 4 commands? | |||
| 8C0F | BNE skip_syntax_bytes ; No: continue on same line | |||
| 8C11 | JSR help_wrap_if_serial ; Handle line wrap after 4 commands | |||
| 8C14 | .skip_syntax_bytes←1← 8C0F BNE | |||
| INX ; X += 3: skip syntax descriptor and address | ||||
| 8C15 | INX ; (continued) | |||
| 8C16 | INX ; (continued) | |||
| 8C17 | BNE loop_next_shared ; Loop for more shared commands | |||
| 8C19 | .done_shared_cmds←1← 8BF8 BMI | |||
| PLA ; Restore original table pointer | ||||
| 8C1A | TAX ; Transfer to X | |||
| 8C1B | .done_entry_newline←1← 8BD9 BEQ | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| 8C1E | INX ; X += 3: skip syntax descriptor and address | |||
| 8C1F | INX ; (continued) | |||
| 8C20 | INX ; (continued) | |||
| 8C21 | JMP loop_next_entry ; Loop for next command | |||
| 8C24 | .done_print_table←1← 8BA8 JMP | |||
| PLP ; Restore processor status | ||||
| 8C25 | PLA ; Restore Y | |||
| 8C26 | TAY ; Transfer to Y | |||
| 8C27 | RTS ; Return | |||
Wrap *HELP syntax lines for serial outputChecks the output destination via &0355. Returns immediately for VDU (stream 0) or printer (stream 3) output. For serial streams, outputs a newline followed by 12 spaces of indentation to align continuation lines with the syntax description column.
|
||||
| 8C28 | .help_wrap_if_serial←2← 8BDF JSR← 8C11 JSR | |||
| LDA vdu_mode ; Read output stream type | ||||
| 8C2B | BEQ return_from_help_wrap ; Stream 0 (VDU): no wrapping | |||
| 8C2D | CMP #3 ; Stream 3 (printer)? | |||
| 8C2F | BEQ return_from_help_wrap ; Yes: no wrapping | |||
| 8C31 | TYA ; Save Y | |||
| 8C32 | PHA ; Push it | |||
| 8C33 | JSR osnewl ; Write newline (characters 10 and 13) | |||
| 8C36 | LDY #&0b ; Y=&0B: indent width - 1 | |||
| 8C38 | LDA #&20 ; Space character | |||
| 8C3A | .loop_indent_spaces←1← 8C3E BPL | |||
| JSR osasci ; Write character 32 | ||||
| 8C3D | DEY ; Decrement indent counter | |||
| 8C3E | BPL loop_indent_spaces ; More spaces needed: loop | |||
| 8C40 | PLA ; Restore Y | |||
| 8C41 | TAY ; Transfer to Y | |||
| 8C42 | .return_from_help_wrap←2← 8C2B BEQ← 8C2F BEQ | |||
| RTS ; Return | ||||
Service 4: unrecognised star commandSaves the OS text pointer, then calls match_fs_cmd to search the command table starting at offset 0 (all command sub-tables). If no match is found (carry set), returns with the service call unclaimed. On a match, JMPs to cmd_fs_reentry to execute the matched command handler via the PHA/PHA/RTS dispatch mechanism.
|
||||
| 8C43 | .svc_4_star_command | |||
| LDX #0 ; X=0: start of utility command table | ||||
| 8C45 | LDY ws_page ; Get command line offset | |||
| 8C47 | JSR save_text_ptr ; Save text pointer to fs_crc | |||
| 8C4A | JSR match_fs_cmd ; Try to match command in table | |||
| 8C4D | BCS svc_return_unclaimed ; No match: return to caller | |||
| 8C4F | JMP cmd_fs_reentry ; Match found: execute command | |||
Service 9: *HELPHandles MOS service call 9 (*HELP). First checks for the credits Easter egg. For bare *HELP (CR at text pointer), prints the version header and full command list starting at table offset &C4. For *HELP with an argument, handles '.' as a shortcut to list all NFS commands, otherwise iterates through help topics using PHA/PHA/RTS dispatch to print matching command groups. Returns with Y = ws_page (unclaimed). |
|
| 8C52 | .svc_9_help |
| JSR check_credits_easter_egg ; Check for credits Easter egg | |
| 8C55 | LDY ws_page ; Get command line offset |
| 8C57 | LDA (os_text_ptr),y ; Load character at offset |
| 8C59 | CMP #&0d ; Is it CR (bare *HELP)? |
| 8C5B | BNE check_help_topic ; No: check for specific topic |
| 8C5D | JSR print_version_header ; Print version string |
| 8C60 | LDX #&c4 ; X=&C4: start of help command list |
| 8C62 | JSR print_cmd_table_loop ; Print command list from table |
| 8C65 | .svc_return_unclaimed←3← 8C4D BCS← 8C92 BEQ← 8CCD BNE |
| LDY ws_page ; Restore Y (command line offset) | |
| 8C67 | RTS ; Return unclaimed |
| 8C68 | .check_help_topic←1← 8C5B BNE |
| BIT bit_test_ff ; Test for topic match (sets flags) | |
| 8C6B | CMP #&2e ; Is first char '.' (abbreviation)? |
| 8C6D | BNE match_help_topic ; No: try topic-specific help |
| 8C6F | JMP help_print_nfs_cmds ; '.' found: show full command list |
| 8C72 | .match_help_topic←1← 8C6D BNE |
| JSR save_text_ptr ; Save text pointer to fs_crc | |
| 8C75 | .loop_dispatch_help←1← 8C90 BNE |
| PHP ; Save flags | |
| 8C76 | LDX #&c4 ; X=&C4: help command table start |
| 8C78 | JSR match_fs_cmd ; Try to match help topic in table |
| 8C7B | BCS skip_if_no_match ; No match: try next topic |
| 8C7D | PLP ; Restore flags |
| 8C7E | LDA #&8c ; Push return address high (&8C) |
| 8C80 | PHA ; Push it for RTS dispatch |
| 8C81 | LDA #&74 ; Push return address low (&74) |
| 8C83 | PHA ; Push it for RTS dispatch |
| 8C84 | LDA cmd_table_fs_hi,x ; Load dispatch address high |
| 8C87 | PHA ; Push dispatch high for RTS |
| 8C88 | LDA cmd_table_fs_lo,x ; Load dispatch address low |
| 8C8B | PHA ; Push dispatch low for RTS |
| 8C8C | RTS ; Dispatch via RTS (returns to &8C75) |
| 8C8D | .skip_if_no_match←1← 8C7B BCS |
| PLP ; Restore flags from before match | |
| 8C8E | CMP #&0d ; End of command line? |
| 8C90 | BNE loop_dispatch_help ; No: try matching next topic |
| 8C92 | BEQ svc_return_unclaimed ; ALWAYS branch |
Print ANFS version string and station numberUses an inline string after JSR print_inline: CR + "Advanced 4.08.53" + CR. After the inline string, JMPs to print_station_id to append the local Econet station number. |
|
| 8C94 | .print_version_header←2← 8B93 JSR← 8C5D JSR |
| JSR print_inline ; Print version string via inline | |
| 8C97 | .version_string_cr |
| EQUS ".Advanced 4.08.53." | |
| 8CAA | NOP ; NOP (string terminator) |
| 8CAB | JMP print_station_id ; Print station number |
Read workspace page number for current ROM slotIndexes into the MOS per-ROM workspace table at &0DF0 using romsel_copy (&F4) as the ROM slot. Returns the allocated page number in both A and Y for caller convenience.
|
||||||
| 8CAE | .get_ws_page←4← 8B0E JSR← 8CB5 JSR← 8F6E JSR← AFC2 JSR | |||||
| LDY romsel_copy ; Get current ROM slot number | ||||||
| 8CB0 | LDA rom_ws_pages,y ; Load workspace page for this slot | |||||
| 8CB3 | TAY ; Transfer to Y | |||||
| 8CB4 | RTS ; Return with page in A and Y | |||||
Set up zero-page pointer to workspace pageCalls get_ws_page to read the page number, stores it as the high byte in nfs_temp (&CD), and clears the low byte at &CC to zero. This gives a page-aligned pointer used by FS initialisation and cmd_net_fs to access the private workspace.
|
||||||
| 8CB5 | .setup_ws_ptr←2← 8B67 JSR← 8ECE JSR | |||||
| JSR get_ws_page ; Get workspace page for ROM slot | ||||||
| 8CB8 | STY nfs_temp ; Store page in nfs_temp | |||||
| 8CBA | LDA #0 ; A=0 | |||||
| 8CBC | STA fs_ws_ptr ; Clear low byte of pointer | |||||
| 8CBE | .return_from_setup_ws_ptr←1← 8CE0 BNE | |||||
| RTS ; Return | ||||||
Service 3: auto-boot on resetScans the keyboard via OSBYTE &7A for the 'N' key (&19 or &55 EOR'd with &55). If pressed, records the key state via OSBYTE &78. Selects the network filing system by calling cmd_net_fs, prints the station ID, then checks if this is the first boot (ws_page = 0). If so, sets the auto-boot flag in &1071 and JMPs to cmd_fs_entry to execute the boot file. |
|
| 8CBF | .svc_3_autoboot |
| LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: scan keyboard from key 16 | |
| 8CC1 | JSR osbyte ; Keyboard scan starting from key 16 |
| 8CC4 | TXA ; X is key number if key is pressed, or &ff otherwise |
| 8CC5 | BMI select_net_fs ; No key pressed: select Net FS |
| 8CC7 | CMP #&19 ; Key &19 (N)? |
| 8CC9 | BEQ write_key_state ; Yes: write key state and boot |
| 8CCB | EOR #&55 ; EOR with &55: maps to zero if 'N' |
| 8CCD | BNE svc_return_unclaimed ; Not N key: return unclaimed |
| 8CCF | .write_key_state←1← 8CC9 BEQ |
| TAY ; Y=key | |
| 8CD0 | LDA #osbyte_write_keys_pressed ; OSBYTE &78: write keys pressed |
| 8CD2 | JSR osbyte ; Write current keys pressed (X and Y) |
| 8CD5 | .select_net_fs←1← 8CC5 BMI |
| JSR cmd_net_fs ; Select NFS as current filing system | |
| 8CD8 | JSR print_station_id ; Print station number |
| 8CDB | JSR osnewl ; Write newline (characters 10 and 13) |
| 8CDE | LDX ws_page ; Get workspace page |
| 8CE0 | BNE return_from_setup_ws_ptr ; Non-zero: already initialised, return |
| 8CE2 | LDA fs_lib_flags ; Load boot flags |
| 8CE5 | ORA #4 ; Set bit 2 (auto-boot in progress) |
| 8CE7 | STA fs_lib_flags ; Store updated boot flags |
| 8CEA | LDX #4 ; X=4: boot filename parameter |
| 8CEC | LDY #&8d ; Y=&8D: boot filename address high |
| 8CEE | JMP fscv_3_star_cmd ; Execute boot file |
Notify OS of filing system selectionCalls FSCV with A=6 to announce the FS change, then issues paged ROM service call 10 via OSBYTE &8F to inform other ROMs. Sets X=&0A and branches to issue_svc_osbyte which falls through from the call_fscv subroutine. |
|
| 8CF1 | .notify_new_fs←1← 8B28 JSR |
| LDA #6 ; A=6: notify new filing system | |
| 8CF3 | JSR call_fscv ; Call FSCV |
| 8CF6 | LDX #&0a ; X=&0A: service 10 parameter |
| 8CF8 | BNE issue_svc_osbyte ; ALWAYS branch |
Dispatch to filing system control vector (FSCV)Indirect JMP through FSCV at &021E, providing OS-level filing system services such as FS selection notification (A=6) and *RUN handling. Also contains issue_svc_15 and issue_svc_osbyte entry points that issue paged ROM service requests via OSBYTE &8F.
|
||||
| 8CFA | .call_fscv←1← 8CF3 JSR | |||
| JMP (fscv) ; Dispatch via FSCV | ||||
| 8CFD | .issue_svc_15←1← 8B7F JMP | |||
| LDX #&0f ; X=&0F: service 15 parameter | ||||
| 8CFF | .issue_svc_osbyte←1← 8CF8 BNE | |||
| LDA #osbyte_issue_service_request ; OSBYTE &8F: issue service request | ||||
| 8D01 | JMP osbyte ; Issue paged ROM service call | |||
| 8D04 | EQUS "i .boot" | |||
| 8D0B | EQUB &0D | |||
Easter egg: match *HELP keyword to author creditsMatches the *HELP argument against a keyword embedded in the credits data at credits_keyword_start. Starts matching from offset 5 in the data (X=5) and checks each byte against the command line text until a mismatch or X reaches &0D. On a full match, prints the ANFS author credits string: B Cockburn, J Dunn, B Robertson, and J Wills, each terminated by CR. |
|
| 8D0C | .check_credits_easter_egg←1← 8C52 JSR |
| LDY ws_page ; Get command line offset | |
| 8D0E | LDX #5 ; X=5: start of credits keyword |
| 8D10 | .loop_match_credits←1← 8D19 BNE |
| LDA (os_text_ptr),y ; Load character from command line | |
| 8D12 | CMP credits_keyword_start,x ; Compare with credits keyword |
| 8D15 | BNE done_credits_check ; Mismatch: check if keyword complete |
| 8D17 | INY ; Advance command line pointer |
| 8D18 | INX ; Advance keyword pointer |
| 8D19 | BNE loop_match_credits ; Continue matching |
| 8D1B | .done_credits_check←1← 8D15 BNE |
| CPX #&0d ; Reached end of keyword (X=&0D)? | |
| 8D1D | BNE return_from_credits_check ; No: keyword not fully matched, return |
| 8D1F | LDX #0 ; X=0: start of credits text |
| 8D21 | .loop_emit_credits←1← 8D2A BNE |
| LDA credits_keyword_start,x ; Load character from credits string | |
| 8D24 | BEQ return_from_credits_check ; Zero terminator: done printing |
| 8D26 | JSR osasci ; Write character |
| 8D29 | INX ; Advance string pointer |
| 8D2A | BNE loop_emit_credits ; Continue printing |
| 8D2C | .return_from_credits_check←2← 8D1D BNE← 8D24 BEQ |
| RTS ; Return | |
| 8D2D | .credits_keyword_start←2← 8D12 CMP← 8D21 LDA |
| EQUB &0D ; CR | |
| 8D2E | .credits_string |
| EQUS "The authors of ANFS are;" | |
| 8D46 | EQUB &0D ; CR |
| 8D47 | EQUS "B Co" |
| 8D4B | .ps_template_base←1← AFFB LDA |
| EQUS "ckburn" | |
| 8D51 | EQUB &0D ; CR |
| 8D52 | EQUS "J Dunn" |
| 8D58 | EQUB &0D ; CR |
| 8D59 | EQUS "B Robertson" |
| 8D64 | EQUB &0D ; CR |
| 8D65 | EQUS "J Wills" |
| 8D6C | EQUB &0D ; CR |
| 8D6D | EQUB &00 ; String terminator |
*I AM command handler (file server logon)Closes any *SPOOL/*EXEC files via OSBYTE &77, resets all file control blocks via process_all_fcbs, then parses the command line for an optional station number and file server address. If a station number is present, stores it and calls clear_if_station_match to validate. Copies the logon command template from cmd_table_nfs_iam into the transmit buffer and sends via copy_arg_validated. Falls through to cmd_pass for password entry. |
|
| 8D6E | .cmd_iam |
| TYA ; Save Y (command line offset) | |
| 8D6F | PHA ; Push it |
| 8D70 | LDA #osbyte_close_spool_exec ; OSBYTE &77: close SPOOL/EXEC |
| 8D72 | STA fs_eof_flags ; Store as pending operation marker |
| 8D75 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 8D78 | LDY #0 ; Y=0 |
| 8D7A | STY fs_work_4 ; Clear password entry flag |
| 8D7C | JSR process_all_fcbs ; Reset FS connection state |
| 8D7F | LDA #0 ; A=0 |
| 8D81 | STA fs_eof_flags ; Clear pending operation marker |
| 8D84 | PLA ; Restore command line offset |
| 8D85 | TAY ; Transfer to Y |
| 8D86 | LDA (fs_options),y ; Load first option byte |
| 8D88 | JSR is_decimal_digit ; Parse station number if present |
| 8D8B | BCC cmd_pass ; Not a digit: skip to password entry |
| 8D8D | JSR parse_addr_arg ; Parse user ID string |
| 8D90 | BCS skip_no_fs_addr ; No user ID: go to password |
| 8D92 | STA fs_server_net ; Store file server station low |
| 8D95 | JSR clear_if_station_match ; Check and store FS network |
| 8D98 | INY ; Skip separator |
| 8D99 | JSR parse_addr_arg ; Parse next argument |
| 8D9C | .skip_no_fs_addr←1← 8D90 BCS |
| BEQ cmd_pass ; No FS address: skip to password | |
| 8D9E | STA fs_server_stn ; Store file server station high |
| 8DA1 | LDX #&ff ; X=&FF: pre-decrement for loop |
| 8DA3 | .loop_copy_logon_cmd←1← 8DAA BPL |
| INX ; Advance index | |
| 8DA4 | LDA cmd_table_nfs_iam,x ; Load logon command template byte |
| 8DA7 | STA fs_cmd_data,x ; Store into transmit buffer |
| 8DAA | BPL loop_copy_logon_cmd ; Bit 7 clear: more bytes, loop |
| 8DAC | JSR copy_arg_validated ; Send logon with file server lookup |
| 8DAF | BEQ scan_pass_prompt ; Success: skip to password entry |
| fall through ↓ | |
*PASS command handler (change password)Builds the FS command packet via copy_arg_to_buf_x0, then scans the reply buffer for a ':' separator indicating a password prompt. If found, reads characters from the keyboard without echo, handling Delete (&7F) for backspace and NAK (&15) to restart from the colon position. Sends the completed password to the file server via save_net_tx_cb and branches to send_cmd_and_dispatch for the reply. |
|
| 8DB1 | .cmd_pass←2← 8D8B BCC← 8D9C BEQ |
| JSR copy_arg_to_buf_x0 ; Build FS command packet | |
| 8DB4 | .scan_pass_prompt←1← 8DAF BEQ |
| LDY #&ff ; Y=&FF: pre-increment for loop | |
| 8DB6 | .loop_scan_colon←1← 8DC0 BNE |
| INY ; Advance to next byte | |
| 8DB7 | LDA fs_cmd_data,y ; Load byte from reply buffer |
| 8DBA | CMP #&0d ; Is it CR (end of prompt)? |
| 8DBC | BEQ send_pass_to_fs ; Yes: no colon found, skip to send |
| 8DBE | CMP #&3a ; Is it ':' (password prompt)? |
| 8DC0 | BNE loop_scan_colon ; No: keep scanning |
| 8DC2 | JSR oswrch ; Write character |
| 8DC5 | STY fs_work_4 ; Save position of colon |
| 8DC7 | .read_pw_char←4← 8DD7 BNE← 8DDB BEQ← 8DDE BNE← 8DEA BNE |
| LDA #&ff ; A=&FF: mark as escapable | |
| 8DC9 | STA escapable ; Set escape flag |
| 8DCB | JSR check_escape ; Check for escape condition |
| 8DCE | JSR osrdch ; Read a character from the current input stream |
| 8DD1 | CMP #&15 ; A=character read |
| 8DD3 | BNE check_pw_special ; Not NAK (&15): check other chars |
| 8DD5 | LDY fs_work_4 ; Restore colon position |
| 8DD7 | BNE read_pw_char ; Non-zero: restart from colon |
| 8DD9 | .loop_erase_pw←1← 8DE2 BEQ |
| CPY fs_work_4 ; At colon position? | |
| 8DDB | BEQ read_pw_char ; Yes: restart password input |
| 8DDD | DEY ; Backspace: move back one character |
| 8DDE | BNE read_pw_char ; If not at start: restart input |
| 8DE0 | .check_pw_special←1← 8DD3 BNE |
| CMP #&7f ; Delete key (&7F)? | |
| 8DE2 | BEQ loop_erase_pw ; Yes: handle backspace |
| 8DE4 | STA fs_cmd_data,y ; Store character in password buffer |
| 8DE7 | INY ; Advance buffer pointer |
| 8DE8 | CMP #&0d ; Is it CR (end of password)? |
| 8DEA | BNE read_pw_char ; No: read another character |
| 8DEC | JSR osnewl ; Write newline (characters 10 and 13) |
| 8DEF | .send_pass_to_fs←1← 8DBC BEQ |
| TYA ; Transfer string length to A | |
| 8DF0 | PHA ; Save string length |
| 8DF1 | JSR init_txcb ; Set up transmit control block |
| 8DF4 | JSR init_tx_ptr_for_pass ; Send to file server and get reply |
| 8DF7 | PLA ; Restore string length |
| 8DF8 | TAX ; Transfer to X (byte count) |
| 8DF9 | INX ; Include terminator |
| 8DFA | LDY #0 ; Y=0 |
| 8DFC | BEQ send_cmd_and_dispatch ; ALWAYS branch |
Clear stored station if parsed argument matchesParses a station number from the command line via init_bridge_poll and compares it with the expected station at &0E01 using EOR. If the parsed value matches (EOR result is zero), clears &0E01. Called by cmd_iam when processing a file server address in the logon command.
|
||||
| 8DFE | .clear_if_station_match←2← 8D95 JSR← A68A JSR | |||
| JSR init_bridge_poll ; Parse station number from cmd line | ||||
| 8E01 | EOR fs_server_net ; Compare with expected station | |||
| 8E04 | BNE return_from_station_match ; Different: return without clearing | |||
| 8E06 | STA fs_server_net ; Same: clear station byte | |||
| 8E09 | .return_from_station_match←1← 8E04 BNE | |||
| RTS ; Return | ||||
| 8E0A | .pass_send_cmd←1← 944E JMP | |||
| JSR copy_arg_to_buf_x0 ; Build FS command packet | ||||
| 8E0D | TAY ; Transfer result to Y | |||
| 8E0E | .send_cmd_and_dispatch←2← 8DFC BEQ← 9310 JMP | |||
| JSR save_net_tx_cb ; Set up command and send to FS | ||||
| 8E11 | LDX fs_cmd_csd ; Load reply function code | |||
| 8E14 | BEQ dispatch_rts ; Zero: no reply, return | |||
| 8E16 | LDA fs_cmd_data ; Load first reply byte | |||
| 8E19 | LDY #&17 ; Y=&17: logon dispatch offset | |||
| 8E1B | BNE svc_dispatch ; ALWAYS branch | |||
| 8E1D | JSR set_xfer_params ; Parse reply as decimal number | |||
| 8E20 | CMP #8 ; Result >= 8? | |||
| 8E22 | BCS dispatch_rts ; Yes: out of range, return | |||
| 8E24 | TAX ; Transfer handle to X | |||
| 8E25 | JSR mask_owner_access ; Look up in open files table | |||
| 8E28 | TYA ; Transfer result to A | |||
| 8E29 | LDY #&13 ; Y=&13: handle dispatch offset | |||
| 8E2B | BNE svc_dispatch ; ALWAYS branch | |||
| 8E2D | .dir_op_dispatch←1← 8556 JSR | |||
| CPX #5 ; Handle >= 5? | ||||
| 8E2F | BCS dispatch_rts ; Yes: out of range, return | |||
| 8E31 | LDY #&0e ; Y=&0E: directory dispatch offset | |||
| fall through ↓ | ||||
PHA/PHA/RTS table dispatchComputes a target index by incrementing X and decrementing Y until Y goes negative, effectively calculating X+Y+1. Pushes the target address (high then low byte) from svc_dispatch_lo/hi tables onto the stack, loads fs_options into X, then returns via RTS to dispatch to the target subroutine. Used for all service dispatch, FS command execution, and OSBYTE handler routing.
|
|||||||||
| 8E33 | .svc_dispatch←5← 8ABD JSR← 8E1B BNE← 8E2B BNE← 8E35 BPL← 8E8C JMP | ||||||||
| INX ; Advance X to target index | |||||||||
| 8E34 | DEY ; Decrement Y offset counter | ||||||||
| 8E35 | BPL svc_dispatch ; Y still positive: continue counting | ||||||||
| 8E37 | TAY ; Y=&FF: will be ignored by caller | ||||||||
| 8E38 | LDA svc_dispatch_hi,x ; Load dispatch address high byte | ||||||||
| 8E3B | PHA ; Push high byte for RTS dispatch | ||||||||
| 8E3C | .push_dispatch_lo | ||||||||
| LDA svc_dispatch_lo,x ; Load dispatch address low byte | |||||||||
| 8E3F | PHA ; Push low byte for RTS dispatch | ||||||||
| 8E40 | LDX fs_options ; Load FS options pointer | ||||||||
| 8E42 | .dispatch_rts←3← 8E14 BEQ← 8E22 BCS← 8E2F BCS | ||||||||
| RTS ; Dispatch via RTS | |||||||||
| ; Printer server template (8 bytes) | |||||||||
| ; Default printer server configuration data, read | |||||||||
| ; indirectly by copy_ps_data via LDA ps_template_base,X | |||||||||
| ; with X=&F8..&FF (reaching ps_template_base+&F8 = | |||||||||
| ; &8E43). Contains "PRINT " (6 bytes) as the default | |||||||||
| ; printer server name, followed by &01 and &00 as | |||||||||
| ; default status bytes. Absent from NFS versions; | |||||||||
| ; unique to ANFS. | |||||||||
| 8E43 | .ps_template_data | ||||||||
| EQUS "PRINT " ; PS template: default name "PRINT " | |||||||||
| 8E49 | EQUB &01 ; PS template: status &01 | ||||||||
| 8E4A | EQUB &00 ; PS template: terminator &00 | ||||||||
FS vector dispatch and handler addresses (34 bytes)Bytes 0-13: extended vector dispatch addresses, copied to FILEV-FSCV (&0212) by loop_set_vectors. Each 2-byte pair is a dispatch address (&FF1B-&FF2D) that the MOS uses to look up the handler in the extended vector table. Bytes 14-33: handler address pairs read by write_vector_entry. Each entry has addr_lo, addr_hi, then a padding byte that is not read at runtime (write_vector_entry writes the current ROM bank number instead). The last entry (FSCV) has no padding byte. |
|
| 8E4B | .fs_vector_table←1← 8B40 LDA |
| EQUW &FF1B ; FILEV dispatch (&FF1B) | |
| 8E4D | EQUW &FF1E ; ARGSV dispatch (&FF1E) |
| 8E4F | EQUW &FF21 ; BGETV dispatch (&FF21) |
| 8E51 | EQUW &FF24 ; BPUTV dispatch (&FF24) |
| 8E53 | EQUW &FF27 ; GBPBV dispatch (&FF27) |
| 8E55 | EQUW &FF2A ; FINDV dispatch (&FF2A) |
| 8E57 | EQUW &FF2D ; FSCV dispatch (&FF2D) |
| 8E59 | EQUW &9921 ; FILEV handler (&9921) |
| 8E5B | EQUB &4A ; (ROM bank — not read) |
| 8E5C | EQUW &9BAF ; ARGSV handler (&9BAF) |
| 8E5E | EQUB &44 ; (ROM bank — not read) |
| 8E5F | EQUW &B7CF ; BGETV handler (&B7CF) |
| 8E61 | EQUB &57 ; (ROM bank — not read) |
| 8E62 | EQUW &B850 ; BPUTV handler (&B850) |
| 8E64 | EQUB &42 ; (ROM bank — not read) |
| 8E65 | EQUW &9E23 ; GBPBV handler (&9E23) |
| 8E67 | EQUB &41 ; (ROM bank — not read) |
| 8E68 | EQUW &9D42 ; FINDV handler (&9D42) |
| 8E6A | EQUB &52 ; (ROM bank — not read) |
| 8E6B | EQUW &8E1D ; FSCV handler (&8E1D) |
OSBYTE wrapper with X=0, Y=&FFSets X=0 and falls through to osbyte_yff to also set Y=&FF. Provides a single call to execute OSBYTE with A as the function code. Used by adlc_init, init_adlc_and_vectors, and Econet OSBYTE handling.
|
|||||||||
| 8E6D | .osbyte_x0←3← 8076 JSR← 8F45 JSR← 96FA JSR | ||||||||
| LDX #0 ; X=0 | |||||||||
| fall through ↓ | |||||||||
OSBYTE wrapper with Y=&FFSets Y=&FF and JMPs to the MOS OSBYTE entry point. X must already be set by the caller. The osbyte_x0 entry point falls through to here after setting X=0.
|
|||||||||
| 8E6F | .osbyte_yff←1← 8080 JSR | ||||||||
| LDY #&ff ; Y=&FF | |||||||||
| 8E71 | .jmp_osbyte←1← 8E7A BEQ | ||||||||
| JMP osbyte ; Execute OSBYTE and return | |||||||||
| ; NETV handler address | |||||||||
| ; 2-byte handler address for the NETV extended | |||||||||
| ; vector, read by write_vector_entry at Y=&36 | |||||||||
| ; from svc_dispatch_lo_offset (push_dispatch_lo+2). | |||||||||
| ; Points to netv_handler (&A968) which dispatches | |||||||||
| ; OSWORDs 0-8 to Econet handlers. Interleaved with | |||||||||
| ; the OSBYTE wrapper code in the data area. | |||||||||
| 8E74 | .netv_handler_addr | ||||||||
| EQUW netv_handler ; NETV handler: netv_handler (&A968) | |||||||||
OSBYTE wrapper with X=0, Y=0Sets X=0 and Y=0 then branches to jmp_osbyte. Called from the Econet OSBYTE dispatch chain to handle OSBYTEs that require both X and Y cleared. The unconditional BEQ (after LDY #0 sets Z) reaches the JMP osbyte instruction at &8E71.
|
|||||||||
| 8E76 | .osbyte_x0_y0←1← 970D JSR | ||||||||
| LDX #0 ; X=0 | |||||||||
| 8E78 | LDY #0 ; Y=0 | ||||||||
| 8E7A | BEQ jmp_osbyte ; ALWAYS branch | ||||||||
Service 7: unrecognised OSBYTEMaps Econet OSBYTE codes &32-&35 to dispatch indices 0-3 by subtracting &31 (with carry from a preceding SBC). Returns unclaimed if the OSBYTE number is outside this range. For valid codes, claims the service (sets svc_state to 0) and JMPs to svc_dispatch with Y=&21 to reach the Econet OSBYTE handler table.
|
||||
| 8E7C | .svc_7_osbyte | |||
| LDA osbyte_a_copy ; Get original OSBYTE A parameter | ||||
| 8E7E | SBC #&31 ; Subtract &31 (map &32-&35 to 1-4) | |||
| 8E80 | CMP #4 ; In range 0-3? | |||
| 8E82 | BCS return_from_svc_1_workspace ; No: not ours, return unclaimed | |||
| 8E84 | TAX ; Transfer to X as dispatch index | |||
| 8E85 | LDA #0 ; A=0: claim the service call | |||
| 8E87 | STA svc_state ; Set return value to 0 (claimed) | |||
| 8E89 | TYA ; Transfer Y to A (OSBYTE Y param) | |||
| 8E8A | LDY #&21 ; Y=&21: OSBYTE dispatch offset | |||
| 8E8C | JMP svc_dispatch ; Dispatch to OSBYTE handler via table | |||
Service 1: absolute workspace claimEnsures the NFS workspace allocation is at least &16 pages by checking Y on entry. If Y < &16, sets Y = &16 to claim the required pages; otherwise returns Y unchanged. This is a passive claim — NFS only raises the allocation, never lowers it.
|
|||||||
| 8E8F | .svc_1_abs_workspace | ||||||
| CPY #&16 ; Need at least &16 pages? | |||||||
| 8E91 | BCS return_from_svc_1_workspace ; Already enough: return | ||||||
| 8E93 | LDY #&16 ; Request &16 pages of workspace | ||||||
| 8E95 | .return_from_svc_1_workspace←2← 8E82 BCS← 8E91 BCS | ||||||
| RTS ; Return | |||||||
Record workspace page count (capped at &21)Stores the workspace allocation from service 1 into offset &0F of the receive control block, capping the value at &21 to prevent overflow into adjacent workspace areas. Called by svc_2_private_workspace after issuing the absolute workspace claim service call.
|
||||
| 8E96 | .store_ws_page_count←1← 8EC6 JSR | |||
| TYA ; Transfer Y to A | ||||
| 8E97 | CMP #&21 ; Y >= &21? | |||
| 8E99 | BCC done_cap_ws_count ; No: use Y as-is | |||
| 8E9B | LDA #&21 ; Cap at &21 | |||
| 8E9D | .done_cap_ws_count←1← 8E99 BCC | |||
| LDY #&0f ; Offset &0F in receive block | ||||
| 8E9F | STA (net_rx_ptr),y ; Store workspace page count | |||
| 8EA1 | RTS ; Return | |||
Service 2: claim private workspace and initialise NFSHandles MOS service call 2 (private workspace claim). Allocates two workspace pages starting at Y: the receive block page (net_rx_ptr_hi) and NFS workspace page (nfs_workspace_hi), plus a per-ROM workspace page stored at &0DF0+ROM slot. Zeroes all workspace, initialises the station ID from the Econet hardware register at &FE18, allocates FS handle pages, copies initial state to page &10, and falls through to init_adlc_and_vectors.
|
||||
| 8EA2 | .svc_2_private_workspace | |||
| STY net_rx_ptr_hi ; Store Y as receive block page | ||||
| 8EA4 | INY ; Advance to next page | |||
| 8EA5 | STY nfs_workspace_hi ; Store as NFS workspace page | |||
| 8EA7 | INY ; Advance to next page | |||
| 8EA8 | TYA ; Transfer page to A | |||
| 8EA9 | LDY romsel_copy ; Get current ROM slot number | |||
| 8EAB | STA rom_ws_pages,y ; Store workspace page for this slot | |||
| 8EAE | LDA #0 ; A=0 | |||
| 8EB0 | STA net_rx_ptr ; Clear receive block pointer low | |||
| 8EB2 | STA nfs_workspace ; Clear NFS workspace pointer low | |||
| 8EB4 | STA ws_page ; Clear workspace page counter | |||
| 8EB6 | STA ws_0d60 ; Clear workspace byte | |||
| 8EB9 | LDY #4 ; Offset 4 in receive block | |||
| 8EBB | STA (net_rx_ptr),y ; Clear remote operation flag | |||
| 8EBD | LDA #osbyte_issue_service_request ; OSBYTE &8F: issue service request | |||
| 8EBF | LDX #1 ; X=1: workspace claim service | |||
| 8EC1 | LDY #&0e ; Y=&0E: requested pages | |||
| 8EC3 | JSR osbyte ; Issue paged ROM service call, Reason X=1 - Absolute public workspace claim | |||
| 8EC6 | JSR store_ws_page_count ; Record final workspace allocation | |||
| 8EC9 | LDA last_break_type ; Load ROM present flag | |||
| 8ECC | BEQ read_station_id ; Zero: first ROM init, skip FS setup | |||
| 8ECE | JSR setup_ws_ptr ; Set up workspace pointers | |||
| 8ED1 | STA fs_flags ; Clear FS flags | |||
| 8ED4 | TAY ; A=0, transfer to Y | |||
| 8ED5 | .loop_zero_workspace←1← 8EDA BNE | |||
| STA (fs_ws_ptr),y ; Clear byte in FS workspace | ||||
| 8ED7 | STA (nfs_workspace),y ; Clear byte in NFS workspace | |||
| 8ED9 | INY ; Advance index | |||
| 8EDA | BNE loop_zero_workspace ; Loop until full page zeroed | |||
| 8EDC | LDY #&0c ; Offset &0C in receive block | |||
| 8EDE | STA (net_rx_ptr),y ; Clear protection flags | |||
| 8EE0 | JSR copy_ps_data_y1c ; Initialise station identity block | |||
| 8EE3 | LDY #6 ; Offset 6 in receive block | |||
| 8EE5 | LDA #&fe ; A=&FE: default station ID marker | |||
| 8EE7 | STA fs_server_stn ; Store default station low | |||
| 8EEA | STA (net_rx_ptr),y ; Store into receive block | |||
| 8EEC | LDA #0 ; A=0 | |||
| 8EEE | STA fs_server_net ; Clear station high byte | |||
| 8EF1 | INY ; Y=&07 | |||
| 8EF2 | STA (net_rx_ptr),y ; Store into receive block | |||
| 8EF4 | LDY #3 ; Offset 3 in NFS workspace | |||
| 8EF6 | STA (nfs_workspace),y ; Clear NFS workspace byte 3 | |||
| 8EF8 | DEY ; Y=&02 | |||
| 8EF9 | LDA #&eb ; A=&EB: default listen state | |||
| 8EFB | STA (nfs_workspace),y ; Store at NFS workspace offset 2 | |||
| 8EFD | LDX #3 ; X=3: init data byte count | |||
| 8EFF | .loop_copy_init_data←1← 8F06 BNE | |||
| LDA ws_init_data,x ; Load initialisation data byte | ||||
| 8F02 | STA net_context,x ; Store in workspace | |||
| 8F05 | DEX ; Decrement counter | |||
| 8F06 | BNE loop_copy_init_data ; More bytes: loop | |||
| 8F08 | STX ws_0d68 ; Clear workspace flag | |||
| 8F0B | STX fs_boot_option ; Clear workspace byte | |||
| 8F0E | JSR reset_spool_buf_state ; Initialise ADLC protection table | |||
| 8F11 | .loop_alloc_handles←1← 8F1E BNE | |||
| LDA ws_page ; Get current workspace page | ||||
| 8F13 | JSR byte_to_2bit_index ; Allocate FS handle page | |||
| 8F16 | BCS read_station_id ; Allocation failed: finish init | |||
| 8F18 | LDA #&3f ; A=&3F: default handle permissions | |||
| 8F1A | STA (nfs_workspace),y ; Store handle permissions | |||
| 8F1C | INC ws_page ; Advance to next page | |||
| 8F1E | BNE loop_alloc_handles ; Continue allocating: loop | |||
| 8F20 | JSR restore_fs_context ; Restore FS context from saved state | |||
| 8F23 | .read_station_id←2← 8ECC BEQ← 8F16 BCS | |||
| LDY station_id_disable_net_nmis ; Read station ID from hardware | ||||
| 8F26 | TYA ; Transfer to A | |||
| 8F27 | BNE store_station_id ; Non-zero: station ID valid | |||
| 8F29 | .error_bad_station←1← 8F30 BEQ | |||
| JMP err_bad_station_num ; Station 0: report error | ||||
| ; Workspace init data | ||||
| ; 3 bytes read via LDA ws_init_data,X with X=3 | ||||
| ; down to 1. ws_init_data overlaps the high byte | ||||
| ; of JMP err_bad_station_num; byte 0 is the JMP | ||||
| ; operand (&92), never read (BNE exits when X=0). | ||||
| ; Stores to l0d6e, l0d6f, l0d70. | ||||
| 8F2C | EQUB &FF ; l0d6e: init=&FF (retry count) | |||
| 8F2D | EQUB &28 ; l0d6f: init=&28 (40, receive poll count) | |||
| 8F2E | EQUB &0A ; l0d70: init=&0A (10, machine peek retries) | |||
| 8F2F | .store_station_id←1← 8F27 BNE | |||
| INY ; Increment station ID | ||||
| 8F30 | BEQ error_bad_station ; Overflow to 0: report error | |||
| 8F32 | LDY #5 ; Offset 5: station ID in recv block | |||
| 8F34 | STA (net_rx_ptr),y ; Store station ID | |||
| 8F36 | LDX #&40 ; X=&40: Econet flag byte | |||
| 8F38 | STX econet_flags ; Store Econet control flag | |||
| 8F3B | LDA #3 ; A=3: protection level | |||
| 8F3D | JSR handle_spool_ctrl_byte ; Set up Econet protection | |||
| fall through ↓ | ||||
Initialise ADLC and install extended vectorsReads the ROM pointer table via OSBYTE &A8, writes vector addresses and ROM ID into the extended vector table for NETV and one additional vector, then restores any previous FS context. |
|
| 8F40 | .init_adlc_and_vectors←1← 8B49 JSR |
| JSR adlc_init ; Initialise ADLC hardware | |
| 8F43 | LDA #&a8 ; OSBYTE &A8: read ROM pointer table |
| 8F45 | JSR osbyte_x0 ; Read ROM pointer table address |
| 8F48 | STX fs_error_ptr ; Store table pointer low |
| 8F4A | STY fs_crflag ; Store table pointer high |
| 8F4C | LDY #&36 ; Y=&36: NETV vector offset |
| 8F4E | STY netv ; Set NETV address |
| 8F51 | LDX #1 ; X=1: one more vector pair to set |
| fall through ↓ | |
Install extended vector table entriesCopies vector addresses from the dispatch table at svc_dispatch_lo_offset+Y into the MOS extended vector table pointed to by fs_error_ptr. For each entry, writes address low, high, then the current ROM ID from romsel_copy (&F4). Loops X times. After the loop, stores &FF at &0D72 as an installed flag, calls deselect_fs_if_active and get_ws_page to restore FS state.
|
|||||||||
| 8F53 | .write_vector_entry←2← 8B50 JSR← 8F65 BNE | ||||||||
| LDA svc_dispatch_lo_offset,y ; Load vector address low byte | |||||||||
| 8F56 | STA (fs_error_ptr),y ; Store into extended vector table | ||||||||
| 8F58 | INY ; Advance to high byte | ||||||||
| 8F59 | LDA svc_dispatch_lo_offset,y ; Load vector address high byte | ||||||||
| 8F5C | STA (fs_error_ptr),y ; Store into extended vector table | ||||||||
| 8F5E | INY ; Advance to ROM ID byte | ||||||||
| 8F5F | LDA romsel_copy ; Load current ROM slot number | ||||||||
| 8F61 | STA (fs_error_ptr),y ; Store ROM ID in extended vector | ||||||||
| 8F63 | INY ; Advance to next vector entry | ||||||||
| 8F64 | DEX ; Decrement vector counter | ||||||||
| 8F65 | BNE write_vector_entry ; More vectors to set: loop | ||||||||
| 8F67 | DEX ; X=&FF | ||||||||
| 8F68 | STX bridge_status ; Store &FF in workspace flag | ||||||||
| 8F6B | JSR fscv_6_shutdown ; Restore FS state if previously active | ||||||||
| 8F6E | JSR get_ws_page ; Get workspace page for ROM slot | ||||||||
| 8F71 | INY ; Advance Y past workspace page | ||||||||
| 8F72 | RTS ; Return | ||||||||
Restore FS context from saved workspaceCopies 8 bytes (offsets 6 to &0D) from the saved workspace at &0DFA back into the receive control block via (net_rx_ptr). This restores the station identity, directory handles, and library path after a filing system reselection. Called by svc_2_private_workspace during init, deselect_fs_if_active during FS teardown, and flip_set_station_boot. |
|
| 8F73 | .restore_fs_context←3← 8F20 JSR← 8F8F JSR← A376 JMP |
| LDY #&0d ; Y=&0D: end of FS context block | |
| 8F75 | .loop_restore_ctx←1← 8F7D BNE |
| LDA fs_context_save,y ; Load FS context byte | |
| 8F78 | STA (net_rx_ptr),y ; Store into receive block |
| 8F7A | DEY ; Decrement index |
| 8F7B | CPY #5 ; Reached offset 5? |
| 8F7D | BNE loop_restore_ctx ; No: continue copying |
| 8F7F | RTS ; Return |
Deselect filing system and save workspaceIf the filing system is currently selected (bit 7 of &0D6C set), closes all open FCBs, closes SPOOL/EXEC files via OSBYTE &77, saves the FS workspace to page &10 shadow with checksum, and clears the selected flag. |
|
| 8F80 | .fscv_6_shutdown←1← 8F6B JSR |
| BIT fs_flags ; FS currently selected? | |
| 8F83 | BPL return_from_fs_shutdown ; No (bit 7 clear): return |
| 8F85 | LDY #0 ; Y=0 |
| 8F87 | JSR process_all_fcbs ; Reset FS connection state |
| 8F8A | LDA #osbyte_close_spool_exec ; OSBYTE &77: close SPOOL/EXEC |
| 8F8C | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 8F8F | JSR restore_fs_context ; Restore FS context to receive block |
| 8F92 | LDY #&76 ; Y=&76: checksum range end |
| 8F94 | LDA #0 ; A=0: checksum accumulator |
| 8F96 | CLC ; Clear carry for addition |
| 8F97 | .loop_checksum_byte←1← 8F9B BPL |
| ADC fcb_count_lo,y ; Add byte from page &10 shadow | |
| 8F9A | DEY ; Decrement index |
| 8F9B | BPL loop_checksum_byte ; Loop until all bytes summed |
| 8F9D | LDY #&77 ; Y=&77: checksum storage offset |
| 8F9F | BPL store_ws_byte ; ALWAYS branch |
| 8FA1 | .loop_copy_to_ws←1← 8FA7 BPL |
| LDA fcb_count_lo,y ; Load byte from page &10 shadow | |
| 8FA4 | .store_ws_byte←1← 8F9F BPL |
| STA (fs_ws_ptr),y ; Copy to FS workspace | |
| 8FA6 | DEY ; Decrement index |
| 8FA7 | BPL loop_copy_to_ws ; Loop until all bytes copied |
| 8FA9 | LDA fs_flags ; Load FS flags |
| 8FAC | AND #&7f ; Clear bit 7 (FS no longer selected) |
| 8FAE | STA fs_flags ; Store updated flags |
| 8FB1 | .return_from_fs_shutdown←1← 8F83 BPL |
| RTS ; Return | |
Verify workspace checksum integritySums bytes 0 to &76 of the workspace page via the zero-page pointer at &CC/&CD and compares with the stored value at offset &77. On mismatch, raises a 'net checksum' error (&AA). The checksummed page holds open file information (preserved when NFS is not the current filing system) and the current printer type. Can only be reset by a control BREAK. Preserves A, Y, and processor flags using PHP/PHA. Called by 5 sites across format_filename_field, adjust_fsopts_4bytes, and start_wipe_pass before workspace access.
|
||||||
| 8FB2 | .verify_ws_checksum←5← 9BAF JSR← 9D42 JSR← 9DE2 JSR← 9E23 JSR← B5EF JSR | |||||
| PHP ; Save processor status | ||||||
| 8FB3 | PHA ; Save A | |||||
| 8FB4 | TYA ; Transfer Y to A | |||||
| 8FB5 | PHA ; Save Y | |||||
| 8FB6 | LDY #&76 ; Y=&76: checksum range end | |||||
| 8FB8 | LDA #0 ; A=0: checksum accumulator | |||||
| 8FBA | CLC ; Clear carry for addition | |||||
| 8FBB | .loop_sum_ws←1← 8FBE BPL | |||||
| ADC (fs_ws_ptr),y ; Add byte from FS workspace | ||||||
| 8FBD | DEY ; Decrement index | |||||
| 8FBE | BPL loop_sum_ws ; Loop until all bytes summed | |||||
| 8FC0 | LDY #&77 ; Y=&77: checksum storage offset | |||||
| 8FC2 | CMP (fs_ws_ptr),y ; Compare with stored checksum | |||||
| 8FC4 | BNE error_net_checksum ; Mismatch: raise checksum error | |||||
| 8FC6 | PLA ; Restore Y | |||||
| 8FC7 | TAY ; Transfer to Y | |||||
| 8FC8 | PLA ; Restore A | |||||
| 8FC9 | PLP ; Restore processor status | |||||
| 8FCA | RTS ; Return (checksum valid) | |||||
| 8FCB | .error_net_checksum←2← 8B25 JMP← 8FC4 BNE | |||||
| LDA #&aa ; Error number &AA | ||||||
| 8FCD | JSR error_bad_inline ; Raise 'net checksum' error | |||||
| 8FD0 | EQUS "net checksum." | |||||
| fall through ↓ | ||||||
Print Econet station number and clock statusUses print_inline to output 'Econet Station ', then reads the station ID from offset 5 of the receive control block and prints it as a decimal number via print_num_no_leading. Tests ADLC status register 2 (&FEA1) to detect the Econet clock; if absent, appends ' No Clock' via a second inline string. Finishes with OSNEWL. Called by print_version_header and svc_3_auto_boot. |
|
| 8FDD | .print_station_id←2← 8CAB JMP← 8CD8 JSR |
| JSR print_inline ; Print 'Econet Station ' via inline | |
| 8FE0 | EQUS "Econet Station " |
| 8FEF | LDY #5 ; Offset 5: station ID |
| 8FF1 | LDA (net_rx_ptr),y ; Load station ID from receive block |
| 8FF3 | JSR print_num_no_leading ; Print station number as decimal |
| 8FF6 | LDA #&20 ; Space character |
| 8FF8 | BIT econet_control23_or_status2 ; Check ADLC status register 2 |
| 8FFB | BEQ done_print_newline ; Clock present: skip warning |
| 8FFD | JSR print_inline ; Print ' No Clock' via inline |
| 9000 | EQUS " No Clock" |
| 9009 | NOP ; NOP (string terminator) |
| 900A | .done_print_newline←1← 8FFB BEQ |
| JSR osnewl ; Write newline (characters 10 and 13) | |
| 900D | .syntax_strings |
| RTS ; Return | |
| ; *HELP command syntax strings | |
| ; 13 null-terminated syntax help strings displayed | |
| ; by *HELP after each command name. Multi-line | |
| ; entries use &0D as a line break. Indexed by | |
| ; cmd_syntax_table via the low 5 bits of each | |
| ; command's syntax descriptor byte. | |
| 900E | .cmd_syntax_strings←1← 8BD6 LDA |
| .syn_opt_dir←1← 8BD6 LDA | |
| EQUS "(<dir>)" ; Syn 1: *Dir, *LCat, *LEx, *Wipe | |
| 9015 | EQUB &00 ; Null terminator |
| 9016 | .syn_iam |
| EQUS "(<stn. id.>) <user id.> " ; Syn 2: *I Am (login) | |
| 902E | EQUB &0D ; Line break |
| 902F | EQUS "((:<CR>)<password>)" ; Syn 2 continued: password clause |
| 9042 | EQUB &00 ; Null terminator |
| 9043 | .syn_object |
| EQUS "<object>" ; Syn 3: *Delete, *FS, *Remove | |
| 904B | EQUB &00 ; Null terminator |
| 904C | .syn_file_offset |
| EQUS "<filename> (<offset> " ; Syn 4: *Dump | |
| 9061 | EQUB &0D ; Line break |
| 9062 | EQUS "(<address>))" ; Syn 4 continued: address clause |
| 906E | EQUB &00 ; Null terminator |
| 906F | .syn_dir |
| EQUS "<dir>" ; Syn 5: *Lib | |
| 9074 | EQUB &00 ; Null terminator |
| 9075 | .syn_dir_num |
| EQUS "<dir> (<number>)" ; Syn 6: *CDir | |
| 9085 | EQUB &00 ; Null terminator |
| 9086 | .syn_password |
| EQUS "(:<CR>) <password> " ; Syn 7: *Pass | |
| 9099 | EQUB &0D ; Line break |
| 909A | EQUS "<new password>" ; Syn 7 continued: new password |
| 90A8 | EQUB &00 ; Null terminator |
| 90A9 | .syn_ps_type |
| EQUS "(<stn. id.>|<ps type>)" ; Syn 8: *PS, *Pollps | |
| 90BF | EQUB &00 ; Null terminator |
| 90C0 | .syn_access |
| EQUS "<object> (L)(W)(R)(/(W)(R))" ; Syn 9: *Access | |
| 90DB | EQUB &00 ; Null terminator |
| 90DC | .syn_rename |
| EQUS "<filename> <new filename>" ; Syn 10: *Rename | |
| 90F5 | EQUB &00 ; Null terminator |
| 90F6 | .syn_opt_stn |
| EQUS "(<stn. id.>)" ; Syn 11: (station id. argument) | |
| 9102 | EQUB &00 ; Null terminator |
| 9103 | .syn_filename |
| EQUS "<filename>" ; Syn 12: *Print, *Type | |
| 910D | EQUB &00 ; Null terminator |
| ; Command syntax string offset table | |
| ; 13 offsets into cmd_syntax_strings (&900E). | |
| ; Indexed by the low 5 bits of each command table | |
| ; syntax descriptor byte. Index &0E is handled | |
| ; separately as a shared-commands list. The print | |
| ; loop at &8BD5 does INY before LDA, so each offset | |
| ; points to the byte before the first character. | |
| 910E | .cmd_syntax_table←1← 8BD1 LDA |
| EQUB syn_iam - cmd_syntax_strings - 2 ; Idx 0: (no synta x) | |
| 910F | EQUB (syn_opt_dir - cmd_syntax_strings - 1) AND &FF ; Idx 1: "(< dir >)" (Y wraps via &FF) |
| 9110 | EQUB syn_iam - cmd_syntax_strings - 1 ; Idx 2: "(< stn. id.>) <user id .>... " |
| 9111 | EQUB syn_object - cmd_syntax_strings - 1 ; Idx 3: "< objec t>" |
| 9112 | EQUB syn_file_offset - cmd_syntax_strings - 1 ; Idx 4: "< filen ame> (< offse t >...) " |
| 9113 | EQUB syn_dir - cmd_syntax_strings - 1 ; Idx 5: "< dir>" |
| 9114 | EQUB syn_dir_num - cmd_syntax_strings - 1 ; Idx 6: "< dir> (< numbe r>)" |
| 9115 | EQUB syn_password - cmd_syntax_strings - 1 ; Idx 7: "(:< CR>) < passw ord >..." |
| 9116 | EQUB syn_ps_type - cmd_syntax_strings - 1 ; Idx 8: "(< stn. id.>| <ps type >)" |
| 9117 | EQUB syn_access - cmd_syntax_strings - 1 ; Idx 9: "< objec t> (L)( W)(R )..." |
| 9118 | EQUB syn_rename - cmd_syntax_strings - 1 ; Idx 10: "< filen ame> <new filen ame>" |
| 9119 | EQUB syn_opt_stn - cmd_syntax_strings - 1 ; Idx 11: "(< stn. id .>)" |
| 911A | EQUB syn_filename - cmd_syntax_strings - 1 ; Idx 12: "< filen ame>" |
Print A as two hexadecimal digitsSaves A on the stack, shifts right four times to isolate the high nybble, calls print_hex_nybble to print it, then restores the full byte and falls through to print_hex_nybble for the low nybble. Called by print_5_hex_bytes, cmd_ex, cmd_dump, and print_dump_header.
|
|||||||
| 911B | .print_hex_byte←5← 9A54 JSR← ADFF JSR← BA55 JSR← BA7D JSR← BAE5 JSR | ||||||
| PHA ; Save full byte | |||||||
| 911C | LSR ; Shift high nybble to low | ||||||
| 911D | LSR ; Continue shifting | ||||||
| 911E | LSR ; Continue shifting | ||||||
| 911F | LSR ; High nybble now in bits 0-3 | ||||||
| 9120 | JSR print_hex_nybble ; Print high nybble as hex digit | ||||||
| 9123 | PLA ; Restore full byte | ||||||
| fall through ↓ | |||||||
Print low nybble of A as hex digitMasks A to the low 4 bits, then converts to ASCII: adds 7 for letters A-F (via ADC #6 with carry set from the CMP), then ADC #&30 for the final '0'-'F' character. Outputs via JMP OSASCI.
|
||||
| 9124 | .print_hex_nybble←1← 9120 JSR | |||
| AND #&0f ; Mask to low nybble | ||||
| 9126 | CMP #&0a ; Digit >= &0A? | |||
| 9128 | BCC add_ascii_base ; No: skip letter adjustment | |||
| 912A | ADC #6 ; Add 7 to get 'A'-'F' (6 + carry) | |||
| 912C | .add_ascii_base←1← 9128 BCC | |||
| ADC #&30 ; Add &30 for ASCII '0'-'9' or 'A'-'F' | ||||
| 912E | JMP osasci ; Write character | |||
Print inline string, high-bit terminatedPops the return address from the stack, prints each byte via OSASCI until a byte with bit 7 set is found, then jumps to that address. The high-bit byte serves as both the string terminator and the opcode of the first instruction after the string. Common terminators are &EA (NOP) for fall-through and &B8 (CLV) followed by BVC for an unconditional forward branch.
|
||||||||
| 9131 | .print_inline←35← 8BAB JSR← 8BED JSR← 8C94 JSR← 8FDD JSR← 8FFD JSR← 9522 JSR← ADAE JSR← ADB8 JSR← ADC6 JSR← ADD1 JSR← ADED JSR← AE02 JSR← AE15 JSR← AE24 JSR← AF33 JSR← B07E JSR← B08A JSR← B0A1 JSR← B0AB JSR← B0B6 JSR← B186 JSR← B228 JSR← B23D JSR← B260 JSR← B26D JSR← B27C JSR← B28C JSR← B29B JSR← B3A8 JSR← B3CD JSR← BA71 JSR← BA8F JSR← BA9C JSR← BAD1 JSR← BAF6 JSR | |||||||
| PLA ; Pop return address (low) — points to last byte of JSR | ||||||||
| 9132 | STA fs_error_ptr ; Store as string pointer low | |||||||
| 9134 | PLA ; Pop return address (high) | |||||||
| 9135 | STA fs_crflag ; Store as string pointer high | |||||||
| 9137 | LDY #0 ; Y=0: index for indirect loads | |||||||
| 9139 | .loop_next_char←1← 9154 JMP | |||||||
| INC fs_error_ptr ; Advance pointer to next character | ||||||||
| 913B | BNE load_char ; No page crossing | |||||||
| 913D | INC fs_crflag ; Carry into high byte | |||||||
| 913F | .load_char←1← 913B BNE | |||||||
| LDA (fs_error_ptr),y ; Load next byte from inline string | ||||||||
| 9141 | BMI resume_caller ; Bit 7 set? Done — this byte is the next opcode | |||||||
| 9143 | LDA fs_error_ptr ; Save string pointer on stack | |||||||
| 9145 | PHA ; (push low byte) | |||||||
| 9146 | LDA fs_crflag ; Save pointer high byte | |||||||
| 9148 | PHA ; (push high byte) | |||||||
| 9149 | LDA (fs_error_ptr),y ; Reload character (pointer may have been clobbered) | |||||||
| 914B | JSR osasci ; Print character via OSASCI Write character | |||||||
| 914E | PLA ; Restore string pointer high | |||||||
| 914F | STA fs_crflag ; Store pointer high | |||||||
| 9151 | PLA ; Restore string pointer low | |||||||
| 9152 | STA fs_error_ptr ; Store pointer low | |||||||
| 9154 | JMP loop_next_char ; Loop for next character | |||||||
| 9157 | .resume_caller←1← 9141 BMI | |||||||
| JMP (fs_error_ptr) ; Jump to address of high-bit byte (resumes code) | ||||||||
Parse decimal or hex station address argumentReads from the command argument at (&BE),Y. Supports '&' prefix for hex, '.' separator for net.station addresses, and plain decimal. Returns result in A. Raises errors for bad digits, overflow, or zero values. |
|
| 915A | .parse_addr_arg←5← 8D8D JSR← 8D99 JSR← A095 JSR← A0AA JSR← AD12 JSR |
| LDA #0 ; Clear accumulator | |
| 915C | STA fs_load_addr_2 ; Initialise result to zero |
| 915E | LDA (fs_crc_lo),y ; Get first character of argument |
| 9160 | CMP #&26 ; Is it '&' (hex prefix)? |
| 9162 | BNE next_dec_char ; No: try decimal path |
| 9164 | INY ; Skip '&' prefix |
| 9165 | LDA (fs_crc_lo),y ; Get first hex digit |
| 9167 | BCS check_digit_range ; C always set from CMP: validate digit |
| 9169 | .next_hex_char←1← 9198 BCC |
| INY ; Advance to next character | |
| 916A | LDA (fs_crc_lo),y ; Get next character |
| 916C | CMP #&2e ; Is it '.' (net.station separator)? |
| 916E | BEQ handle_dot_sep ; Yes: handle dot separator |
| 9170 | CMP #&21 ; Below '!' (space/control)? |
| 9172 | BCC done_parse_num ; Yes: end of number |
| 9174 | .check_digit_range←1← 9167 BCS |
| CMP #&30 ; Below '0'? | |
| 9176 | BCC skip_if_not_hex ; Not a digit: bad hex |
| 9178 | CMP #&3a ; Above '9'? |
| 917A | BCC extract_digit_value ; Decimal digit: extract value |
| 917C | AND #&5f ; Force uppercase |
| 917E | ADC #&b8 ; Map 'A'-'F' to &FA-&FF |
| 9180 | BCS err_bad_hex ; Overflow: not A-F |
| 9182 | CMP #&fa ; Valid hex letter (A-F)? |
| 9184 | .skip_if_not_hex←1← 9176 BCC |
| BCC err_bad_hex ; Below A: bad hex | |
| 9186 | .extract_digit_value←1← 917A BCC |
| AND #&0f ; Extract digit value (0-15) | |
| 9188 | STA fs_load_addr_3 ; Save current digit |
| 918A | LDA fs_load_addr_2 ; Load running result |
| 918C | CMP #&10 ; Would shift overflow a byte? |
| 918E | BCS error_overflow ; Yes: overflow error |
| 9190 | ASL ; Shift result left 4 (x16) |
| 9191 | ASL ; (shift 2) |
| 9192 | ASL ; (shift 3) |
| 9193 | ASL ; (shift 4) |
| 9194 | ADC fs_load_addr_3 ; Add new hex digit |
| 9196 | STA fs_load_addr_2 ; Store updated result |
| 9198 | BCC next_hex_char ; Loop for next hex digit |
| 919A | .next_dec_char←2← 9162 BNE← 91C4 BNE |
| LDA (fs_crc_lo),y ; Get current character | |
| 919C | CMP #&2e ; Is it '.' (net.station separator)? |
| 919E | BEQ handle_dot_sep ; Yes: handle dot separator |
| 91A0 | CMP #&21 ; Below '!' (space/control)? |
| 91A2 | BCC done_parse_num ; Yes: end of number |
| 91A4 | JSR is_dec_digit_only ; Is it a decimal digit? |
| 91A7 | BCC error_bad_number ; No: 'Bad number' error |
| 91A9 | AND #&0f ; Extract digit value (0-9) |
| 91AB | STA fs_load_addr_3 ; Save current digit |
| 91AD | ASL fs_load_addr_2 ; result * 2 |
| 91AF | BCS error_overflow ; Overflow |
| 91B1 | LDA fs_load_addr_2 ; Load result * 2 |
| 91B3 | ASL ; result * 4 |
| 91B4 | BCS error_overflow ; Overflow |
| 91B6 | ASL ; result * 8 |
| 91B7 | BCS error_overflow ; Overflow |
| 91B9 | ADC fs_load_addr_2 ; * 8 + * 2 = result * 10 |
| 91BB | BCS error_overflow ; Overflow |
| 91BD | ADC fs_load_addr_3 ; result * 10 + new digit |
| 91BF | BCS error_overflow ; Overflow |
| 91C1 | STA fs_load_addr_2 ; Store updated result |
| 91C3 | INY ; Advance to next character |
| 91C4 | BNE next_dec_char ; Loop (always branches) |
| 91C6 | .done_parse_num←2← 9172 BCC← 91A2 BCC |
| LDA fs_work_4 ; Check parsing mode | |
| 91C8 | BPL validate_station ; Bit 7 clear: net.station mode |
| 91CA | LDA fs_load_addr_2 ; Decimal-only mode: get result |
| 91CC | BEQ error_bad_param ; Zero: 'Bad parameter' |
| 91CE | RTS ; Return with result in A |
| 91CF | .validate_station←1← 91C8 BPL |
| LDA fs_load_addr_2 ; Get parsed station number | |
| 91D1 | CMP #&ff ; Station 255 is reserved |
| 91D3 | BEQ err_bad_station_num ; 255: 'Bad station number' |
| 91D5 | LDA fs_load_addr_2 ; Reload result |
| 91D7 | BNE return_parsed ; Non-zero: valid station |
| 91D9 | LDA fs_work_4 ; Zero result: check if dot was seen |
| 91DB | BEQ err_bad_station_num ; No dot and zero: 'Bad station number' |
| 91DD | DEY ; Check character before current pos |
| 91DE | LDA (fs_crc_lo),y ; Load previous character |
| 91E0 | INY ; Restore Y |
| 91E1 | EOR #&2e ; Was previous char '.'? |
| 91E3 | BNE err_bad_station_num ; No: 'Bad station number' |
| 91E5 | .return_parsed←1← 91D7 BNE |
| SEC ; C=1: number was parsed | |
| 91E6 | RTS ; Return (result in fs_load_addr_2) |
| 91E7 | .handle_dot_sep←2← 916E BEQ← 919E BEQ |
| LDA fs_work_4 ; Check if dot already seen | |
| 91E9 | BNE error_bad_number ; Already seen: 'Bad number' |
| 91EB | INC fs_work_4 ; Set dot-seen flag |
| 91ED | LDA fs_load_addr_2 ; Get network number (before dot) |
| 91EF | CMP #&ff ; Network 255 is reserved |
| 91F1 | BEQ error_bad_net_num ; 255: 'Bad network number' |
| 91F3 | RTS ; Return to caller with network part |
| 91F4 | .err_bad_hex←3← 9180 BCS← 9184 BCC← BBB2 JMP |
| LDA #&f1 ; Error code &F1 | |
| 91F6 | JSR error_bad_inline ; Generate 'Bad hex' error |
| 91F9 | EQUS "hex." |
| 91FD | .error_overflow←6← 918E BCS← 91AF BCS← 91B4 BCS← 91B7 BCS← 91BB BCS← 91BF BCS |
| BIT fs_work_4 ; Test parsing mode | |
| 91FF | BMI error_bad_param ; Decimal mode: 'Bad parameter' |
| 9201 | .err_bad_station_num←4← 8F29 JMP← 91D3 BEQ← 91DB BEQ← 91E3 BNE |
| LDA #&d0 ; Error code &D0 | |
| 9203 | JSR error_bad_inline ; Generate 'Bad station number' error |
| 9206 | EQUS "station number." |
| 9215 | .error_bad_number←2← 91A7 BCC← 91E9 BNE |
| LDA #&f0 ; Error code &F0 | |
| 9217 | JSR error_bad_inline ; Generate 'Bad number' error |
| 921A | EQUS "number." |
| 9221 | .error_bad_param←2← 91CC BEQ← 91FF BMI |
| LDA #&94 ; Error code &94 | |
| 9223 | JSR error_bad_inline ; Generate 'Bad parameter' error |
| 9226 | EQUS "parameter." |
| 9230 | .error_bad_net_num←1← 91F1 BEQ |
| LDA #&d1 ; Error code &D1 | |
| 9232 | JSR error_bad_inline ; Generate 'Bad network number' error |
| 9235 | EQUS "network number." |
| fall through ↓ | |
Test for digit, '&', or '.' separatorCompares A against '&' and '.' first; if either matches, returns with carry set via the shared return_12 exit. Otherwise falls through to is_dec_digit_only for the '0'-'9' range test. Called by cmd_iam, cmd_ps, and cmd_pollps when parsing station addresses.
|
|||||||
| 9244 | .is_decimal_digit←3← 8D88 JSR← AFE5 JSR← B1B8 JSR | ||||||
| CMP #&26 ; Is it '&' (hex prefix)? | |||||||
| 9246 | BEQ return_from_digit_test ; Yes: return C set (not decimal) | ||||||
| 9248 | CMP #&2e ; Is it '.' (separator)? | ||||||
| 924A | BEQ return_from_digit_test ; Yes: return C set (not decimal) | ||||||
| fall through ↓ | |||||||
Test for decimal digit '0'-'9'Uses two CMPs to bracket-test A against the range &30-&39. CMP #&3A sets carry if A >= ':' (above digits), then CMP #&30 sets carry if A >= '0'. The net effect: carry set only for '0'-'9'. Called by parse_addr_arg.
|
|||||||
| 924C | .is_dec_digit_only←1← 91A4 JSR | ||||||
| CMP #&3a ; Above '9'? | |||||||
| 924E | BCS not_a_digit ; Yes: not a digit | ||||||
| 9250 | CMP #&30 ; Below '0'? C clear if so | ||||||
| 9252 | .return_from_digit_test←2← 9246 BEQ← 924A BEQ | ||||||
| RTS ; Return: C set if '0'-'9' | |||||||
| 9253 | .not_a_digit←1← 924E BCS | ||||||
| CLC ; C=0: not a digit | |||||||
| 9254 | RTS ; Return | ||||||
Read and encode directory entry access byteLoads the access byte from offset &0E of the directory entry via (fs_options),Y, masks to 6 bits (AND #&3F), then sets X=4 and branches to begin_prot_encode to map through the protection bit encode table at &9272. Called by check_and_setup_txcb for owner and public access.
|
||||
| 9255 | .get_access_bits←2← 9B0E JSR← 9B3A JSR | |||
| LDY #&0e ; Offset &0E in directory entry | ||||
| 9257 | LDA (fs_options),y ; Load raw access byte | |||
| 9259 | AND #&3f ; Mask to 6 access bits | |||
| 925B | LDX #4 ; X=4: start encoding at bit 4 | |||
| 925D | BNE begin_prot_encode ; ALWAYS branch to encoder ALWAYS branch | |||
Encode protection bits via lookup tableMasks A to 5 bits (AND #&1F), sets X=&FF to start at table index 0, then enters the shared encoding loop at begin_prot_encode. Shifts out each source bit and ORs in the corresponding value from prot_bit_encode_table (&9272). Called by send_txcb_swap_addrs and check_and_setup_txcb.
|
|||||||
| 925F | .get_prot_bits←2← 9A16 JSR← 9B57 JSR | ||||||
| AND #&1f ; Mask to 5 protection bits | |||||||
| 9261 | LDX #&ff ; X=&FF: start encoding at bit 0 | ||||||
| 9263 | .begin_prot_encode←1← 925D BNE | ||||||
| STA fs_error_ptr ; Save remaining bits | |||||||
| 9265 | LDA #0 ; Clear encoded result | ||||||
| 9267 | .loop_encode_prot←1← 926F BNE | ||||||
| INX ; Advance to next table position | |||||||
| 9268 | LSR fs_error_ptr ; Shift out lowest source bit | ||||||
| 926A | BCC skip_clear_prot ; Bit clear: skip this position | ||||||
| 926C | ORA prot_bit_encode_table,x ; Bit set: OR in encoded value | ||||||
| 926F | .skip_clear_prot←1← 926A BCC | ||||||
| BNE loop_encode_prot ; More bits to process | |||||||
| 9271 | RTS ; Return encoded access in A | ||||||
| ; Protection/access bit encode table | |||||||
| ; 11-entry lookup table used by get_prot_bits and | |||||||
| ; get_access_bits to remap attribute bits between | |||||||
| ; the file server protocol format and the local | |||||||
| ; representation. The encoding loop shifts out each | |||||||
| ; source bit; for each set bit, the corresponding | |||||||
| ; table entry is ORed into the result. | |||||||
| ; Indices 0-4: used by get_prot_bits (5-bit input). | |||||||
| ; Some entries set multiple output bits (expansion). | |||||||
| ; Indices 5-10: used by get_access_bits (6-bit input | |||||||
| ; from directory entry offset &0E). Each entry sets | |||||||
| ; exactly one output bit (pure permutation). | |||||||
| 9272 | .prot_bit_encode_table←1← 926C ORA | ||||||
| EQUB &50 ; Bit 0: &50 = %01010000 (bits 4,6) | |||||||
| 9273 | EQUB &20 ; Bit 1: &20 = %00100000 (bit 5) | ||||||
| 9274 | EQUB &05 ; Bit 2: &05 = %00000101 (bits 0,2) | ||||||
| 9275 | EQUB &02 ; Bit 3: &02 = %00000010 (bit 1) | ||||||
| 9276 | EQUB &88 ; Bit 4: &88 = %10001000 (bits 3,7) | ||||||
| 9277 | EQUB &04 ; Bit 0: &04 = %00000100 (bit 2) | ||||||
| 9278 | EQUB &08 ; Bit 1: &08 = %00001000 (bit 3) | ||||||
| 9279 | EQUB &80 ; Bit 2: &80 = %10000000 (bit 7) | ||||||
| 927A | EQUB &10 ; Bit 3: &10 = %00010000 (bit 4) | ||||||
| 927B | EQUB &01 ; Bit 4: &01 = %00000001 (bit 0) | ||||||
| 927C | EQUB &02 ; Bit 5: &02 = %00000010 (bit 1) | ||||||
Set OS text pointer then transfer parametersStores X/Y into the MOS text pointer at &F2/&F3, then falls through to set_xfer_params and set_options_ptr to configure the full FS transfer context. Called by byte_to_2bit_index.
|
||||||
| 927D | .set_text_and_xfer_ptr←1← A0FC JSR | |||||
| STX os_text_ptr ; Set text pointer low | ||||||
| 927F | STY os_text_ptr_hi ; Set text pointer high | |||||
| fall through ↓ | ||||||
Set FS transfer byte count and source pointerStores A into fs_last_byte_flag (&BD) as the transfer byte count, and X/Y into fs_crc_lo/hi (&BE/&BF) as the source data pointer. Falls through to set_options_ptr to complete the transfer context setup. Called by 5 sites across cmd_ex, format_filename_field, and gsread_to_buf.
|
||||||||
| 9281 | .set_xfer_params←5← 8E1D JSR← 9921 JSR← 9D45 JSR← 9E26 JSR← AD6E JSR | |||||||
| STA fs_last_byte_flag ; Store transfer byte count | ||||||||
| 9283 | STX fs_crc_lo ; Store source pointer low | |||||||
| 9285 | STY fs_crc_hi ; Store source pointer high | |||||||
| fall through ↓ | ||||||||
Set FS options pointer and clear escape flagStores X/Y into fs_options/fs_block_offset (&BB/&BC) as the options block pointer. Then enters clear_escapable which uses PHP/LSR/PLP to clear bit 0 of the escape flag at &97 without disturbing processor flags. Called by format_filename_field and send_and_receive.
|
||||||
| 9287 | .set_options_ptr←2← 9BB4 JSR← B979 JSR | |||||
| STX fs_options ; Store options pointer low | ||||||
| 9289 | STY fs_block_offset ; Store options pointer high | |||||
| 928B | .clear_escapable←1← 9870 JMP | |||||
| PHP ; Save processor flags | ||||||
| 928C | LSR escapable ; Clear bit 0 of escape flag | |||||
| 928E | PLP ; Restore processor flags | |||||
| 928F | RTS ; Return | |||||
Compare 5-byte handle buffers for equalityLoops X from 4 down to 1, comparing each byte of l00af+X with fs_load_addr_3+X using EOR. Returns on the first mismatch (Z=0) or after all 5 bytes match (Z=1). Called by send_txcb_swap_addrs and check_and_setup_txcb to verify station/handle identity.
|
||||
| 9290 | .cmp_5byte_handle←2← 9984 JSR← 9A89 JSR | |||
| LDX #4 ; Compare 5 bytes (indices 4 down to 1) | ||||
| 9292 | .loop_cmp_handle←1← 9299 BNE | |||
| LDA addr_work,x ; Load byte from handle buffer | ||||
| 9294 | EOR fs_load_addr_3,x ; Compare with channel handle | |||
| 9296 | BNE return_from_cmp_handle ; Mismatch: return Z=0 | |||
| 9298 | DEX ; Next byte | |||
| 9299 | BNE loop_cmp_handle ; Loop until all compared | |||
| 929B | .return_from_cmp_handle←1← 9296 BNE | |||
| RTS ; Return: Z=1 if all 5 matched | ||||
| 929C | .fscv_7_read_handles | |||
| LDX #&20 ; Unreachable code | ||||
| 929E | LDY #&2f ; (dead) | |||
| 92A0 | RTS ; (dead) | |||
Set connection-active flag in channel tableSaves registers on the stack, recovers the original A from the stack via TSX/LDA &0102,X, then calls attr_to_chan_index to find the channel slot. ORs bit 6 (&40) into the channel status byte at &1060+X. Preserves A, X, and processor flags via PHP/PHA/PLA/PLP. Called by format_filename_field and adjust_fsopts_4bytes.
|
||||
| 92A1 | .set_conn_active←2← 9C3A JSR← 9E83 JSR | |||
| PHP ; Save processor flags | ||||
| 92A2 | PHA ; Save A | |||
| 92A3 | TXA ; Transfer X to A | |||
| 92A4 | PHA ; Save original X | |||
| 92A5 | TSX ; Get stack pointer | |||
| 92A6 | LDA stack_page_2,x ; Read original A from stack | |||
| 92A9 | JSR attr_to_chan_index ; Convert to channel index | |||
| 92AC | BMI done_conn_flag ; No channel found: skip | |||
| 92AE | LDA #&40 ; Bit 6: connection active flag | |||
| 92B0 | ORA chan_status,x ; Set active flag in channel table | |||
| 92B3 | STA chan_status,x ; Store updated status | |||
| 92B6 | BNE done_conn_flag ; ALWAYS branch to exit | |||
Clear connection-active flag in channel tableMirror of set_conn_active but ANDs the channel status byte with &BF (bit 6 clear mask) instead of ORing. Uses the same register-preservation pattern: PHP/PHA/TSX to recover A, then attr_to_chan_index to find the slot. Shares the done_conn_flag exit with set_conn_active.
|
||||
| 92B8 | .clear_conn_active←2← 9C9B JSR← 9E7E JSR | |||
| PHP ; Save processor flags | ||||
| 92B9 | PHA ; Save A | |||
| 92BA | TXA ; Transfer X to A | |||
| 92BB | PHA ; Save original X | |||
| 92BC | TSX ; Get stack pointer | |||
| 92BD | LDA stack_page_2,x ; Read original A from stack | |||
| 92C0 | JSR attr_to_chan_index ; Convert to channel index | |||
| 92C3 | BMI done_conn_flag ; No channel found: skip | |||
| 92C5 | LDA #&bf ; Bit 6 clear mask (&BF = ~&40) | |||
| 92C7 | AND chan_status,x ; Clear active flag in channel table | |||
| 92CA | STA chan_status,x ; Store updated status | |||
| 92CD | .done_conn_flag←3← 92AC BMI← 92B6 BNE← 92C3 BMI | |||
| PLA ; Restore X | ||||
| 92CE | TAX ; Transfer back to X | |||
| 92CF | PLA ; Restore A | |||
| 92D0 | PLP ; Restore processor flags | |||
| 92D1 | RTS ; Return | |||
Shared *Access/*Delete/*Info/*Lib command handlerCopies the command name to the TX buffer, parses a quoted filename argument via parse_quoted_arg, and checks the access prefix. Validates the filename does not start with '&', then falls through to read_filename_char to copy remaining characters and send the request. Raises 'Bad file name' if a bare CR is found where a filename was expected. |
|
| 92D2 | .cmd_fs_operation |
| JSR copy_fs_cmd_name ; Copy command name to TX buffer | |
| 92D5 | TXA ; Save buffer position |
| 92D6 | PHA ; Push it |
| 92D7 | JSR parse_quoted_arg ; Parse filename (handles quoting) |
| 92DA | JSR parse_access_prefix ; Parse owner/public access prefix |
| 92DD | PLA ; Restore buffer position |
| 92DE | TAX ; Transfer to X |
| 92DF | JSR check_not_ampersand ; Reject '&' character in filename |
| 92E2 | CMP #&0d ; End of line? |
| 92E4 | BNE read_filename_char ; No: copy filename chars to buffer |
| 92E6 | .error_bad_filename←4← 92FA BEQ← 93F5 JMP← AECD JMP← AF02 JMP |
| LDA #&cc ; Error number &CC | |
| 92E8 | JSR error_bad_inline ; Raise 'Bad file name' error |
| 92EB | EQUS "file name." |
| fall through ↓ | |
Reject '&' as filename characterLoads the first character from the parse buffer at &0E30 and compares with '&' (&26). Branches to error_bad_filename if matched, otherwise returns. Also contains read_filename_char which loops reading characters from the command line into the TX buffer at &0F05, calling strip_token_prefix on each byte and terminating on CR. Used by cmd_fs_operation and cmd_rename. |
|
| 92F5 | .check_not_ampersand←2← 92DF JSR← 92FD JSR |
| LDA fs_filename_buf ; Load first parsed character | |
| 92F8 | CMP #&26 ; Is it '&'? |
| 92FA | BEQ error_bad_filename ; Yes: invalid filename |
| 92FC | RTS ; Return |
| 92FD | .read_filename_char←3← 92E4 BNE← 930B JMP← 93C6 JMP |
| JSR check_not_ampersand ; Reject '&' in current char | |
| 9300 | STA fs_cmd_data,x ; Store character in TX buffer |
| 9303 | INX ; Advance buffer pointer |
| 9304 | CMP #&0d ; End of line? |
| 9306 | BEQ send_fs_request ; Yes: send request to file server |
| 9308 | JSR strip_token_prefix ; Strip BASIC token prefix byte |
| 930B | JMP read_filename_char ; Continue reading filename chars |
| 930E | .send_fs_request←2← 9306 BEQ← 93EE JMP |
| LDY #0 ; Y=0: no extra dispatch offset | |
| 9310 | JMP send_cmd_and_dispatch ; Send command and dispatch reply |
Copy matched command name to TX bufferScans backwards in cmd_table_fs from the current position to find the bit-7 flag byte marking the start of the command name. Copies each character forward into the TX buffer at &0F05 until the next bit-7 byte (end of name), then appends a space separator. Called by cmd_fs_operation and cmd_rename.
|
||||||
| 9313 | .copy_fs_cmd_name←2← 92D2 JSR← 9377 JSR | |||||
| TYA ; Save command line offset | ||||||
| 9314 | PHA ; Push it | |||||
| 9315 | .loop_scan_flag←1← 9319 BPL | |||||
| DEX ; Scan backwards in command table | ||||||
| 9316 | LDA cmd_table_fs,x ; Load table byte | |||||
| 9319 | BPL loop_scan_flag ; Bit 7 clear: keep scanning | |||||
| 931B | INX ; Point past flag byte to name start | |||||
| 931C | LDY #0 ; Y=0: TX buffer offset | |||||
| 931E | .loop_copy_name←1← 9328 BNE | |||||
| LDA cmd_table_fs,x ; Load command name character | ||||||
| 9321 | BMI append_space ; Bit 7 set: end of name | |||||
| 9323 | STA fs_cmd_data,y ; Store character in TX buffer | |||||
| 9326 | INX ; Advance table pointer | |||||
| 9327 | INY ; Advance buffer pointer | |||||
| 9328 | BNE loop_copy_name ; Continue copying name | |||||
| 932A | .append_space←1← 9321 BMI | |||||
| LDA #&20 ; Space separator | ||||||
| 932C | STA fs_cmd_data,y ; Append space after command name | |||||
| 932F | INY ; Advance buffer pointer | |||||
| 9330 | TYA ; Transfer length to A | |||||
| 9331 | TAX ; And to X (buffer position) | |||||
| 9332 | PLA ; Restore command line offset | |||||
| 9333 | TAY ; Transfer to Y | |||||
| 9334 | .return_from_copy_cmd_name←1← 9369 BEQ | |||||
| RTS ; Return | ||||||
Parse possibly-quoted filename argumentReads from the command line at (&BE),Y. Handles double-quote delimiters and stores the result in the parse buffer at &0E30. Raises 'Bad string' on unbalanced quotes. |
|
| 9335 | .parse_quoted_arg←2← 92D7 JSR← 937F JSR |
| LDA #0 ; A=0: no quote mode | |
| 9337 | TAX ; X=&00 |
| 9338 | STA quote_mode ; Clear quote tracking flag |
| 933B | .loop_skip_spaces←1← 9342 BNE |
| LDA (fs_crc_lo),y ; Load char from command line | |
| 933D | CMP #&20 ; Space? |
| 933F | BNE check_open_quote ; No: check for opening quote |
| 9341 | INY ; Skip leading space |
| 9342 | BNE loop_skip_spaces ; Continue skipping spaces |
| 9344 | .check_open_quote←1← 933F BNE |
| CMP #&22 ; Double-quote character? | |
| 9346 | BNE loop_copy_arg_char ; No: start reading filename |
| 9348 | INY ; Skip opening quote |
| 9349 | EOR quote_mode ; Toggle quote mode flag |
| 934C | STA quote_mode ; Store updated quote mode |
| 934F | .loop_copy_arg_char←2← 9346 BNE← 9364 BNE |
| LDA (fs_crc_lo),y ; Load char from command line | |
| 9351 | CMP #&22 ; Double-quote? |
| 9353 | BNE store_arg_char ; No: store character as-is |
| 9355 | EOR quote_mode ; Toggle quote mode |
| 9358 | STA quote_mode ; Store updated quote mode |
| 935B | LDA #&20 ; Replace closing quote with space |
| 935D | .store_arg_char←1← 9353 BNE |
| STA fs_filename_buf,x ; Store character in parse buffer | |
| 9360 | INY ; Advance command line pointer |
| 9361 | INX ; Advance buffer pointer |
| 9362 | CMP #&0d ; End of line? |
| 9364 | BNE loop_copy_arg_char ; No: continue parsing |
| 9366 | LDA quote_mode ; Check quote balance flag |
| 9369 | BEQ return_from_copy_cmd_name ; Balanced: return OK |
| 936B | LDA brk_ptr ; Unbalanced: use BRK ptr for error |
| 936D | JSR error_bad_inline ; Raise 'Bad string' error |
| 9370 | EQUS "string." |
| fall through ↓ | |
*Rename command handlerParses two space-separated filenames from the command line, each with its own access prefix. Sets the owner-only access mask before parsing each name. Validates that both names resolve to the same file server by comparing the FS options word — raises 'Bad rename' if they differ. Falls through to read_filename_char to copy the second filename into the TX buffer and send the request. |
|
| 9377 | .cmd_rename |
| JSR copy_fs_cmd_name ; Copy 'Rename ' to TX buffer | |
| 937A | TXA ; Save buffer position |
| 937B | PHA ; Push it |
| 937C | JSR mask_owner_access ; Set owner-only access mask |
| 937F | JSR parse_quoted_arg ; Parse first filename (quoted) |
| 9382 | JSR parse_access_prefix ; Parse access prefix |
| 9385 | PLA ; Restore buffer position |
| 9386 | TAX ; Transfer to X |
| 9387 | .loop_copy_rename←1← 93A5 JMP |
| LDA fs_filename_buf ; Load next parsed character | |
| 938A | CMP #&0d ; End of line? |
| 938C | BNE store_rename_char ; No: store character |
| 938E | .error_bad_rename←1← 93C4 BNE |
| LDA #&b0 ; Error number &B0 | |
| 9390 | JSR error_bad_inline ; Raise 'Bad rename' error |
| 9393 | EQUS "rename." |
| 939A | .store_rename_char←1← 938C BNE |
| STA fs_cmd_data,x ; Store character in TX buffer | |
| 939D | INX ; Advance buffer pointer |
| 939E | CMP #&20 ; Space (name separator)? |
| 93A0 | BEQ skip_rename_spaces ; Yes: first name complete |
| 93A2 | JSR strip_token_prefix ; Strip BASIC token prefix byte |
| 93A5 | JMP loop_copy_rename ; Continue copying first filename |
| 93A8 | .skip_rename_spaces←2← 93A0 BEQ← 93B0 BEQ |
| JSR strip_token_prefix ; Strip token from next char | |
| 93AB | LDA fs_filename_buf ; Load next parsed character |
| 93AE | CMP #&20 ; Still a space? |
| 93B0 | BEQ skip_rename_spaces ; Yes: skip multiple spaces |
| 93B2 | LDA fs_lib_flags ; Save current FS options |
| 93B5 | PHA ; Push them |
| 93B6 | JSR mask_owner_access ; Reset access mask for second name |
| 93B9 | TXA ; Save buffer position |
| 93BA | PHA ; Push it |
| 93BB | JSR parse_access_prefix ; Parse access prefix for second name |
| 93BE | PLA ; Restore buffer position |
| 93BF | TAX ; Transfer to X |
| 93C0 | PLA ; Restore original FS options |
| 93C1 | CMP fs_lib_flags ; Options changed (cross-FS)? |
| 93C4 | BNE error_bad_rename ; Yes: error (can't rename across FS) |
| 93C6 | JMP read_filename_char ; Copy second filename and send |
*Dir command handlerHandles three argument syntaxes: a plain path (delegates to pass_send_cmd), '&' alone for the root directory, and '&N.dir' for cross-filesystem directory changes. The cross-FS form sends a file server selection command (code &12) to locate the target server, raising 'Not found' on failure, then sends the directory change (code 6) and calls find_fs_and_exit to update the active FS context. |
|
| 93C9 | .cmd_dir |
| LDA (fs_crc_lo),y ; Get first char of argument | |
| 93CB | CMP #&26 ; Is it '&' (FS selector prefix)? |
| 93CD | BNE dir_pass_simple ; No: simple dir change |
| 93CF | INY ; Skip '&' |
| 93D0 | LDA (fs_crc_lo),y ; Get char after '&' |
| 93D2 | CMP #&0d ; End of line? |
| 93D4 | BEQ setup_fs_root ; Yes: '&' alone (root directory) |
| 93D6 | CMP #&20 ; Space? |
| 93D8 | BNE check_fs_dot ; No: check for '.' separator |
| 93DA | .setup_fs_root←1← 93D4 BEQ |
| LDY #&ff ; Y=&FF: pre-increment for loop | |
| 93DC | .loop_copy_fs_num←1← 93E4 BNE |
| INY ; Advance index | |
| 93DD | LDA (fs_crc_lo),y ; Load char from command line |
| 93DF | STA fs_cmd_data,y ; Copy to TX buffer |
| 93E2 | CMP #&26 ; Is it '&' (end of FS path)? |
| 93E4 | BNE loop_copy_fs_num ; No: keep copying |
| 93E6 | LDA #&0d ; Replace '&' with CR terminator |
| 93E8 | STA fs_cmd_data,y ; Store CR in buffer |
| 93EB | INY ; Point past CR |
| 93EC | TYA ; Transfer length to A |
| 93ED | TAX ; And to X (byte count) |
| 93EE | JMP send_fs_request ; Send directory request to server |
| 93F1 | .check_fs_dot←1← 93D8 BNE |
| CMP #&2e ; Is char after '&' a dot? | |
| 93F3 | BEQ parse_fs_dot_dir ; Yes: &FS.dir format |
| 93F5 | JMP error_bad_filename ; No: invalid syntax |
| 93F8 | .parse_fs_dot_dir←1← 93F3 BEQ |
| INY ; Skip '.' | |
| 93F9 | STY fs_load_addr ; Save dir path start position |
| 93FB | LDA #4 ; FS command 4: examine directory |
| 93FD | STA fs_cmd_data ; Store in TX buffer |
| 9400 | LDA fs_lib_flags ; Load FS flags |
| 9403 | ORA #&40 ; Set bit 6 (FS selection active) |
| 9405 | STA fs_lib_flags ; Store updated flags |
| 9408 | LDX #1 ; X=1: buffer offset |
| 940A | JSR copy_arg_validated ; Copy FS number to buffer |
| 940D | LDY #&12 ; Y=&12: select FS command code |
| 940F | JSR save_net_tx_cb ; Send FS selection command |
| 9412 | LDA fs_cmd_data ; Load reply status |
| 9415 | CMP #2 ; Status 2 (found)? |
| 9417 | BEQ dir_found_send ; Yes: proceed to dir change |
| 9419 | LDA #&d6 ; Error number &D6 |
| 941B | JSR error_inline_log ; Raise 'Not found' error |
| 941E | EQUS "Not found." |
| 9428 | .dir_found_send←1← 9417 BEQ |
| LDA fs_csd_handle ; Load current FS station byte | |
| 942B | STA fs_cmd_data ; Store in TX buffer |
| 942E | LDX #1 ; X=1: buffer offset |
| 9430 | LDY #7 ; Y=7: change directory command code |
| 9432 | JSR save_net_tx_cb ; Send directory change request |
| 9435 | LDX #1 ; X=1 |
| 9437 | STX fs_cmd_data ; Store start marker in buffer |
| 943A | STX fs_func_code ; Store start marker in buffer+1 |
| 943D | INX ; X=&02 |
| 943E | LDY fs_load_addr ; Restore dir path start position |
| 9440 | JSR copy_arg_validated ; Copy directory path to buffer |
| 9443 | LDY #6 ; Y=6: set directory command code |
| 9445 | JSR save_net_tx_cb ; Send set directory command |
| 9448 | LDY fs_cmd_data ; Load reply handle |
| 944B | JMP fsreply_3_set_csd ; Select FS and return |
| 944E | .dir_pass_simple←1← 93CD BNE |
| JMP pass_send_cmd ; Simple: pass command to FS | |
Initialise TXCB for bye/receive on port &90Loads A=&90 (the FS command port) and falls through to init_txcb_port, which initialises the TXCB from the template, sets the port, data start offset to 3, and decrements the control byte. Called by recv_and_process_reply. |
|
| 9451 | .init_txcb_bye←1← 94DD JSR |
| LDA #&90 ; A=&90: bye command port | |
| fall through ↓ | |
Initialise TXCB with specified port numberCalls init_txcb to copy the 12-byte template into the TXCB workspace at &00C0, then stores A as the transmit port (txcb_port at &C1), sets txcb_start to 3 (data begins at offset 3 in the packet), and decrements txcb_ctrl. Called by check_and_setup_txcb.
|
||||
| 9453 | .init_txcb_port←1← 9ACE JSR | |||
| JSR init_txcb ; Initialise TXCB from template | ||||
| 9456 | STA txcb_port ; Set transmit port | |||
| 9458 | LDA #3 ; A=3: data start offset | |||
| 945A | STA txcb_start ; Set TXCB start offset | |||
| 945C | DEC txcb_ctrl ; Decrement control byte | |||
| 945E | RTS ; Return | |||
Initialise TX control block from ROM templateCopies 12 bytes from txcb_init_template (&9477) into the TXCB workspace at &00C0. For the first two bytes (Y=0,1), also copies the destination station/network from &0E00 into txcb_dest (&C2). Preserves A via PHA/PLA. Called by 4 sites including cmd_pass, init_txcb_port, prep_send_tx_cb, and send_wipe_request. |
|
| 945F | .init_txcb←5← 8DF1 JSR← 9453 JSR← 94CC JSR← A8DE LDA← B92F JSR |
| PHA ; Save A | |
| 9460 | LDY #&0b ; Y=&0B: template size - 1 |
| 9462 | .loop_init_txcb←1← 9473 BPL |
| LDA txcb_init_template,y ; Load byte from TXCB template | |
| 9465 | STA txcb_ctrl,y ; Store to TXCB workspace |
| 9468 | CPY #2 ; Index >= 2? |
| 946A | BPL skip_txcb_dest ; Yes: skip dest station copy |
| 946C | LDA fs_server_stn,y ; Load dest station byte |
| 946F | STA txcb_dest,y ; Store to TXCB destination |
| 9472 | .skip_txcb_dest←1← 946A BPL |
| DEY ; Decrement index | |
| 9473 | BPL loop_init_txcb ; More bytes: continue |
| 9475 | PLA ; Restore A |
| 9476 | RTS ; Return |
| ; TXCB initialisation template (12 bytes) | |
| ; Copied by init_txcb into the TXCB workspace at | |
| ; &00C0. For offsets 0-1, the destination station | |
| ; bytes are also copied from l0e00 into txcb_dest. | |
| ; The &FF byte at offset 6 (bit_test_ff, &947D) | |
| ; serves double duty: it is part of this template | |
| ; AND a BIT target used by 22 callers to set the | |
| ; V and N flags without clobbering A. | |
| 9477 | .txcb_init_template←1← 9462 LDA |
| EQUB &80 ; Offset 0: txcb_ctrl = &80 (transmit) | |
| 9478 | EQUB &99 ; Offset 1: txcb_port = &99 (FS reply) |
| 9479 | EQUB &00 ; Offset 2: txcb_dest lo (overwritten) |
| 947A | EQUB &00 ; Offset 3: txcb_dest hi (overwritten) |
| 947B | EQUB &00 ; Offset 4: txcb_start = 0 |
| 947C | EQUB &0F ; Offset 5: buffer start hi (page &0F) |
| 947D | .bit_test_ff←22← 8C68 BIT← 9638 BIT← 9768 BIT← 9B2F BIT← 9D02 BIT← A086 BIT← A185 BIT← A2FE BIT← A329 BIT← A360 BIT← AA6E BIT← AF5F BIT← AF65 BIT← B005 BIT← B181 BIT← B1E1 BIT← B222 BIT← B2B2 BIT← B54E BIT← B58C BIT← B88B BIT← B988 BIT |
| EQUB &FF ; Offset 6: BIT target / buffer end lo | |
| 947E | EQUB &FF ; Offset 7: txcb_pos = &FF |
| 947F | EQUB &FF ; Offset 8: txcb_end = &FF |
| 9480 | EQUB &0F ; Offset 9: buffer end hi (page &0F) |
| 9481 | EQUB &FF ; Offset 10: extended addr fill (&FF) |
| 9482 | EQUB &FF ; Offset 11: extended addr fill (&FF) |
Send read-only FS request (carry set)Pushes A and sets carry to indicate no-write mode, then branches to txcb_copy_carry_set to enter the common TXCB copy, send, and reply processing path. The carry flag controls whether a disconnect is sent on certain reply codes. Called by setup_transfer_workspace. |
|
| 9483 | .send_request_nowrite←1← 9F02 JSR |
| PHA ; Save A | |
| 9484 | SEC ; Set carry (read-only mode) |
| 9485 | BCS txcb_copy_carry_set ; ALWAYS branch |
Send read-write FS request (V clear)Clears V flag and branches unconditionally to txcb_copy_carry_clr (via BVC, always taken after CLV) to enter the common TXCB copy, send, and reply processing path with carry clear (write mode). Called by do_fs_cmd_iteration and send_txcb_swap_addrs. |
|
| 9487 | .send_request_write←2← 9944 JSR← 99F8 JSR |
| CLV ; Clear V | |
| 9488 | BVC txcb_copy_carry_clr ; ALWAYS branch |
*Bye command handlerCloses all open file control blocks via process_all_fcbs, shuts down any *SPOOL/*EXEC files with OSBYTE &77, and closes all network channels. Falls through to save_net_tx_cb with function code &17 to send the bye request to the file server. |
|
| 948A | .cmd_bye |
| LDY #0 ; Y=0: close all files | |
| 948C | JSR process_all_fcbs ; Process all file control blocks |
| 948F | LDA #osbyte_close_spool_exec ; OSBYTE &77: close spool/exec |
| 9491 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 9494 | JSR close_all_net_chans ; Close all network channels |
| 9497 | LDY #&17 ; Y=&17: *Bye function code |
| fall through ↓ | |
Save FS state and send command to file serverCopies station address and function code (Y) to the TX buffer, builds the TXCB, sends the packet, and waits for the reply. V is clear for standard mode. |
|
| 9499 | .save_net_tx_cb←26← 8E0E JSR← 940F JSR← 9432 JSR← 9445 JSR← 9B4B JSR← 9C34 JSR← 9C44 JSR← 9C92 JSR← 9D2B JSR← 9DA4 JSR← 9DD8 JSR← 9E70 JSR← 9E93 JSR← 9F57 JSR← A012 JSR← A1BE JSR← A1E6 JSR← A52A JSR← AD2F JMP← ADA6 JSR← ADE4 JSR← AE53 JSR← B365 JSR← B406 JSR← B6C9 JSR← B8C8 JSR |
| CLV ; Clear V (standard mode) | |
| fall through ↓ | |
Save and send TXCB with V flag setVariant of save_net_tx_cb for callers that have already set V. Copies the FS station address from &0E02 to &0F02, then falls through to txcb_copy_carry_clr which clears carry and enters the common TXCB copy, send, and reply path. Called by check_and_setup_txcb, format_filename_field, and cmd_remove. |
|
| 949A | .save_net_tx_cb_vset←3← 9B32 JSR← 9D08 JSR← AF62 JMP |
| LDA fs_urd_handle ; Copy FS station to TX control block | |
| 949D | STA fs_cmd_urd ; Store in TXCB |
| 94A0 | .txcb_copy_carry_clr←1← 9488 BVC |
| CLC ; Clear carry | |
| 94A1 | .txcb_copy_carry_set←1← 9485 BCS |
| PHP ; Save flags (carry = mode) | |
| 94A2 | STY fs_cmd_y_param ; Store function code in TXCB |
| 94A5 | LDY #1 ; Copy 2 bytes (indices 0-1) |
| 94A7 | .loop_copy_vset_stn←1← 94AE BPL |
| LDA fs_csd_handle,y ; Load source byte | |
| 94AA | STA fs_cmd_csd,y ; Store to TXCB |
| 94AD | DEY ; Next byte |
| 94AE | BPL loop_copy_vset_stn ; Loop until all copied |
| 94B0 | BIT fs_lib_flags ; Test library flag bits 6-7 |
| 94B3 | BVS use_lib_station ; Bit 6 set: use station as port |
| 94B5 | BPL done_vset_station ; Bit 7 clear: skip port override |
| 94B7 | LDA fs_lib_handle ; Bit 7 set: load alternative port |
| 94BA | STA fs_cmd_csd ; Override TXCB port byte |
| 94BD | BVC done_vset_station ; ALWAYS branch |
| 94BF | .use_lib_station←1← 94B3 BVS |
| LDA fs_urd_handle ; Bit 6: load station byte | |
| 94C2 | STA fs_cmd_csd ; Use station as TXCB port |
| 94C5 | .done_vset_station←2← 94B5 BPL← 94BD BVC |
| PLP ; Restore flags (carry = mode) | |
| fall through ↓ | |
Build TXCB from scratch, send, and receive replyFull send/receive cycle: saves flags, sets reply port &90, calls init_txcb to load the template, computes txcb_end from X+5, then dispatches based on carry: C set sends a disconnect via handle_disconnect, C clear calls init_tx_ptr_and_send and falls through to recv_and_process_reply. Called by setup_transfer_workspace. |
|
| 94C6 | .prep_send_tx_cb←1← 9FB0 JSR |
| PHP ; Save flags | |
| 94C7 | LDA #&90 ; Port &90: FS command port |
| 94C9 | STA txcb_reply_port ; Set reply port in TXCB |
| 94CC | JSR init_txcb ; Initialise TXCB workspace |
| 94CF | TXA ; Get TXCB data end offset |
| 94D0 | ADC #5 ; Add 5 for header size |
| 94D2 | STA txcb_end ; Set TXCB end pointer |
| 94D4 | PLP ; Restore flags |
| 94D5 | BCS handle_disconnect ; C set: send disconnect instead |
| 94D7 | PHP ; Save flags |
| 94D8 | JSR init_tx_ptr_and_send ; Initialise TX pointer and send |
| 94DB | PLP ; Restore flags |
| fall through ↓ | |
Receive FS reply and dispatch on status codesCalls init_txcb_bye to set up a receive TXCB on port &90, then wait_net_tx_ack to wait for the acknowledgment. Iterates over reply bytes: zero terminates, V-set codes are adjusted by +&2B, and non-zero codes dispatch to store_reply_status. Handles disconnect requests (C set from prep_send_tx_cb) and 'Data Lost' warnings when channel status bits indicate pending writes were interrupted. |
|
| 94DC | .recv_and_process_reply←2← 9A0B JSR← 9F43 JSR |
| PHP ; Save flags | |
| 94DD | JSR init_txcb_bye ; Set up receive TXCB |
| 94E0 | JSR wait_net_tx_ack ; Wait for TX acknowledgment |
| 94E3 | PLP ; Restore flags |
| 94E4 | .loop_next_reply←1← 94F8 BCC |
| INY ; Advance to next reply byte | |
| 94E5 | LDA (txcb_start),y ; Load reply byte |
| 94E7 | TAX ; Save in X |
| 94E8 | BEQ return_from_recv_reply ; Zero: no more replies, return |
| 94EA | BVC process_reply_code ; V clear: use code directly |
| 94EC | ADC #&2a ; V set: adjust reply code (+&2B) |
| 94EE | .process_reply_code←1← 94EA BVC |
| BNE store_reply_status ; Non-zero: process reply | |
| 94F0 | .return_from_recv_reply←2← 94E8 BEQ← 955E BPL |
| RTS ; Return | |
| 94F1 | .handle_disconnect←1← 94D5 BCS |
| PLA ; Discard saved flags | |
| 94F2 | LDX #&c0 ; X=&C0: disconnect command |
| 94F4 | INY ; Advance reply offset |
| 94F5 | JSR send_disconnect_reply ; Send disconnect reply |
| 94F8 | BCC loop_next_reply ; Successful: process next reply |
| 94FA | .store_reply_status←1← 94EE BNE |
| STX fs_last_error ; Store reply status code | |
| 94FD | LDA fs_eof_flags ; Load pending operation marker |
| 9500 | BNE check_data_loss ; Pending: go to data loss check |
| 9502 | CPX #&bf ; Reply &BF (normal bye response)? |
| 9504 | BNE build_error_block ; No: build error from reply |
| 9506 | .check_data_loss←1← 9500 BNE |
| LDA #&40 ; A=&40: initial data-loss flag | |
| 9508 | PHA ; Push data-loss accumulator |
| 9509 | LDX #&0f ; Scan 16 channel entries (15 to 0) |
| 950B | .loop_scan_channels←1← 9519 BPL |
| PLA ; Pop accumulator | |
| 950C | ORA fcb_status,x ; OR in channel status bits |
| 950F | PHA ; Push updated accumulator |
| 9510 | LDA fcb_status,x ; Load channel status |
| 9513 | AND #&c0 ; Keep only bits 6-7 (close flags) |
| 9515 | STA fcb_status,x ; Clear data bits, keep state flags |
| 9518 | DEX ; Next channel |
| 9519 | BPL loop_scan_channels ; Loop all 16 channels |
| 951B | JSR close_all_net_chans ; Close all network channels |
| 951E | PLA ; Pop data-loss accumulator |
| 951F | ROR ; Bit 0 to carry (data lost?) |
| 9520 | BCC reload_reply_status ; No data lost: skip message |
| 9522 | JSR print_inline ; Print 'Data Lost' + CR |
| 9525 | EQUS "Data Lost." |
| 952F | .reload_reply_status←1← 9520 BCC |
| LDX fs_last_error ; Reload reply status code | |
| 9532 | LDA fs_eof_flags ; Check pending operation |
| 9535 | BEQ build_error_block ; No pending: build error from reply |
| 9537 | PLA ; Pending: clean up stack (3 bytes) |
| 9538 | PLA ; (second byte) |
| 9539 | PLA ; (third byte) |
| 953A | RTS ; Return to pending operation caller |
| 953B | .build_error_block←2← 9504 BNE← 9535 BEQ |
| LDY #1 ; Y=1: error code offset in reply | |
| 953D | CPX #&a8 ; Reply code >= &A8? |
| 953F | BCS setup_error_copy ; Yes: keep server error code |
| 9541 | LDA #&a8 ; No: use minimum error code &A8 |
| 9543 | STA (txcb_start),y ; Overwrite error code in reply |
| 9545 | .setup_error_copy←1← 953F BCS |
| LDY #&ff ; Y=&FF: pre-increment index | |
| 9547 | .loop_copy_error←1← 954F BNE |
| INY ; Advance to next byte | |
| 9548 | LDA (txcb_start),y ; Load reply byte |
| 954A | STA error_block,y ; Copy to error block |
| 954D | EOR #&0d ; Is it CR (end of message)? |
| 954F | BNE loop_copy_error ; No: copy next byte |
| 9551 | STA error_block,y ; Store null terminator (A=0 from EOR) |
| 9554 | DEY ; Get message length |
| 9555 | TYA ; Transfer to A |
| 9556 | TAX ; Length in X |
| 9557 | JMP check_net_error_code ; Go to error dispatch |
Check for pending escape conditionANDs the MOS escape flag (&FF) with the escapable flag at &97. If bit 7 of the result is clear (no escape or escape disabled), returns immediately. Otherwise enters raise_escape_error: acknowledges the escape via OSBYTE &7E, then jumps to classify_reply_error with A=6 to raise the Escape error. Called by cmd_pass and send_net_packet. |
|
| 955A | .check_escape←2← 8DCB JSR← 9846 JSR |
| LDA escape_flag ; Load MOS escape flag | |
| 955C | AND escapable ; Mask with escape-enabled flag |
| 955E | BPL return_from_recv_reply ; No escape: return |
| 9560 | .raise_escape_error←1← B42B JMP |
| LDA #osbyte_acknowledge_escape ; OSBYTE &7E: acknowledge escape | |
| 9562 | JSR osbyte ; Clear escape condition and perform escape effects |
| 9565 | LDA #6 ; Error class 6: Escape |
| 9567 | JMP classify_reply_error ; Classify as network error |
| 956A | .lang_1_remote_boot |
| LDY #4 ; Offset 4: remote state byte | |
| 956C | LDA (net_rx_ptr),y ; Load remote state |
| 956E | BEQ init_remote_session ; Zero: initialise remote session |
| 9570 | .done_commit_state←1← 95B6 BNE |
| JMP commit_state_byte ; Non-zero: commit state and return | |
| 9573 | .init_remote_session←2← 956E BEQ← 95AC BEQ |
| ORA #9 ; Set bits 0,3: remote active flags | |
| 9575 | STA (net_rx_ptr),y ; Store updated remote state |
| 9577 | LDX #&80 ; X=&80: flag for vector setup |
| 9579 | LDY #&80 ; Offset &80 in RX buffer |
| 957B | LDA (net_rx_ptr),y ; Load remote station low |
| 957D | PHA ; Save on stack |
| 957E | INY ; Y=&81 |
| 957F | LDA (net_rx_ptr),y ; Load remote station high |
| 9581 | LDY #&0f ; Workspace offset &0F |
| 9583 | STA (nfs_workspace),y ; Store remote station high |
| 9585 | DEY ; Y=&0E Y=&0e |
| 9586 | PLA ; Restore remote station low |
| 9587 | STA (nfs_workspace),y ; Store remote station low |
| 9589 | JSR scan_remote_keys ; Set up remote keyboard scanning |
| 958C | JSR init_ws_copy_narrow ; Initialise workspace copy |
| 958F | LDX #1 ; X=1: disable keyboard |
| 9591 | LDY #0 ; Y=0 |
| 9593 | LDA #osbyte_read_write_econet_keyboard_disable ; OSBYTE &C9: Econet keyboard disable |
| 9595 | JSR osbyte ; Disable keyboard (for Econet) |
| 9598 | .lang_3_execute_at_0100 |
| JSR commit_state_byte ; Commit state change | |
| 959B | LDA #0 ; Error code 0 |
| 959D | JSR error_inline_log ; Generate 'Remoted' error |
| 95A0 | EQUS "Remoted." |
| 95A8 | .lang_4_remote_validated |
| LDY #4 ; Offset 4: remote state byte | |
| 95AA | LDA (net_rx_ptr),y ; Load remote state |
| 95AC | BEQ init_remote_session ; Zero: reinitialise session |
| 95AE | LDY #&80 ; Offset &80: station low |
| 95B0 | LDA (net_rx_ptr),y ; Load station low from RX |
| 95B2 | LDY #&0e ; Workspace offset &0E |
| 95B4 | CMP (nfs_workspace),y ; Compare with stored station |
| 95B6 | BNE done_commit_state ; Different station: commit state |
| 95B8 | .lang_0_insert_remote_key |
| LDY #&82 ; Offset &82: keypress byte | |
| 95BA | LDA (net_rx_ptr),y ; Load remote keypress |
| 95BC | TAY ; Key code to Y |
| 95BD | LDX #0 ; X=0: keyboard buffer |
| 95BF | JSR commit_state_byte ; Commit state change |
| 95C2 | LDA #osbyte_insert_input_buffer ; OSBYTE &99: insert into buffer |
| 95C4 | JMP osbyte ; Insert character Y into input buffer X |
Wait for Econet TX completion with timeoutSaves the timeout counter from &0D6F and the TX control state from &0D61, then polls net_tx_ptr_hi (&9B) for completion. Uses a three-level nested loop: the outer counter comes from the configured timeout at &0D6F. On completion, restores both saved values. On timeout (all loops exhausted), branches to build_no_reply_error to raise 'No reply'. Called by 6 sites across the protocol stack. |
|
| 95C7 | .wait_net_tx_ack←6← 94E0 JSR← 999E JSR← 9AD8 JSR← A923 JMP← ABBF JSR← AC61 JSR |
| LDA rx_poll_count ; Save TX timeout counter | |
| 95CA | PHA ; Push (used as outer loop counter) |
| 95CB | LDA econet_flags ; Save TX control state |
| 95CE | PHA ; Push (preserved during wait) |
| 95CF | LDA net_tx_ptr_hi ; Check if TX in progress |
| 95D1 | BNE init_poll_counters ; Non-zero: skip force-wait |
| 95D3 | ORA #&80 ; Set bit 7 to force wait mode |
| 95D5 | STA econet_flags ; Store updated control state |
| 95D8 | .init_poll_counters←1← 95D1 BNE |
| LDA #0 ; A=0: initial counter values | |
| 95DA | PHA ; Push inner loop counter |
| 95DB | PHA ; Push middle loop counter |
| 95DC | TAY ; Y=&00 |
| 95DD | TSX ; X=SP for stack-relative DECs |
| 95DE | .loop_poll_tx←3← 95E5 BNE← 95EA BNE← 95EF BNE |
| LDA (net_tx_ptr),y ; Poll TX completion status | |
| 95E0 | BMI done_poll_tx ; Bit 7 set: TX complete |
| 95E2 | DEC error_text,x ; Decrement inner counter |
| 95E5 | BNE loop_poll_tx ; Not zero: keep polling |
| 95E7 | DEC stack_page_2,x ; Decrement middle counter |
| 95EA | BNE loop_poll_tx ; Not zero: keep polling |
| 95EC | DEC stack_page_4,x ; Decrement outer counter |
| 95EF | BNE loop_poll_tx ; Not zero: keep polling |
| 95F1 | .done_poll_tx←1← 95E0 BMI |
| PLA ; Discard inner counter | |
| 95F2 | PLA ; Discard middle counter |
| 95F3 | PLA ; Restore l0d61 control state |
| 95F4 | STA econet_flags ; Write back TX control state |
| 95F7 | PLA ; Pop outer counter (0 if timed out) |
| 95F8 | BEQ build_no_reply_error ; Zero: TX timed out |
| 95FA | RTS ; Return (TX acknowledged) |
Conditionally store error code to workspaceTests bit 7 of &0D6C (FS selected flag). If clear, returns immediately. If set, stores A into &0E09 as the last error code. This guards against writing error state when no filing system is active. Called internally by the error classification chain and by error_inline_log.
|
||||
| 95FB | .cond_save_error_code←6← 9611 JSR← 964A JSR← 9666 JSR← 9690 JSR← 96A2 JSR← 96BB JSR | |||
| BIT fs_flags ; Test error logging flag | ||||
| 95FE | BPL return_from_cond_save_err ; Bit 7 clear: skip save | |||
| 9600 | STA fs_last_error ; Save error code to workspace | |||
| 9603 | .return_from_cond_save_err←1← 95FE BPL | |||
| RTS ; Return | ||||
| 9604 | .build_no_reply_error←1← 95F8 BEQ | |||
| LDX #8 ; X=8: 'No reply' error index | ||||
| 9606 | LDY net_error_lookup_data,x ; Look up message table offset | |||
| 9609 | LDX #0 ; X=0: error text start | |||
| 960B | STX error_block ; Clear BRK byte in error block | |||
| 960E | LDA error_msg_table,y ; Load error number from table | |||
| 9611 | JSR cond_save_error_code ; Conditionally save error code | |||
| 9614 | .loop_copy_no_reply_msg←1← 961E BNE | |||
| LDA error_msg_table,y ; Load message byte | ||||
| 9617 | STA error_text,x ; Store in error text buffer | |||
| 961A | BEQ done_no_reply_msg ; Null terminator? | |||
| 961C | INX ; Advance destination | |||
| 961D | INY ; Advance source | |||
| 961E | BNE loop_copy_no_reply_msg ; Loop until end of message | |||
| 9620 | .done_no_reply_msg←1← 961A BEQ | |||
| JSR append_drv_dot_num ; Append ' net.station' to message | ||||
| 9623 | LDA #0 ; A=0: null terminator | |||
| 9625 | STA error_text,x ; Terminate error text | |||
| 9628 | JMP check_net_error_code ; Check and raise network error | |||
| 962B | .fixup_reply_status_a←1← 98B1 JMP | |||
| LDA (net_tx_ptr,x) ; Load first reply byte | ||||
| 962D | CMP #&41 ; Is it 'A' (status &41)? | |||
| 962F | BNE skip_if_not_a ; No: keep original | |||
| 9631 | LDA #&42 ; Yes: change to 'B' (&42) | |||
| 9633 | .skip_if_not_a←1← 962F BNE | |||
| CLV ; Clear V flag | ||||
| 9634 | BVC mask_error_class ; ALWAYS branch | |||
| 9636 | .load_reply_and_classify←1← 986A JMP | |||
| LDA (net_tx_ptr,x) ; Load first reply byte | ||||
| 9638 | .classify_reply_error←2← 9567 JMP← 9DD0 JMP | |||
| BIT bit_test_ff ; Set V flag (via BIT &FF) | ||||
| 963B | .mask_error_class←1← 9634 BVC | |||
| AND #7 ; Mask to error class (0-7) | ||||
| 963D | PHA ; Save error class on stack | |||
| 963E | CMP #2 ; Class 2 (station error)? | |||
| 9640 | BNE build_simple_error ; No: build simple error message | |||
| 9642 | PHP ; Save flags (V state for suffix) | |||
| 9643 | TAX ; Error class to X | |||
| 9644 | LDY net_error_lookup_data,x ; Look up message table offset | |||
| 9647 | LDA error_msg_table,y ; Load error number from table | |||
| 964A | JSR cond_save_error_code ; Conditionally save error code | |||
| 964D | LDX #0 ; X=0: error text start | |||
| 964F | STX error_block ; Clear BRK byte | |||
| 9652 | .loop_copy_station_msg←1← 965C BNE | |||
| LDA error_msg_table,y ; Load message byte | ||||
| 9655 | STA error_text,x ; Store in error text | |||
| 9658 | BEQ done_station_msg ; Null terminator? | |||
| 965A | INY ; Advance source | |||
| 965B | INX ; Advance destination | |||
| 965C | BNE loop_copy_station_msg ; Loop until end of message | |||
| 965E | .done_station_msg←1← 9658 BEQ | |||
| JSR append_drv_dot_num ; Append ' net.station' suffix | ||||
| 9661 | PLP ; Restore flags | |||
| 9662 | BVS suffix_not_listening ; V set: append 'not listening' | |||
| 9664 | LDA #&a4 ; Error code &A4 | |||
| 9666 | JSR cond_save_error_code ; Conditionally save error code | |||
| 9669 | STA error_text ; Replace error number in block | |||
| 966C | LDY #&0b ; Y=&0B: 'not present' suffix index | |||
| 966E | BNE load_suffix_offset ; ALWAYS branch | |||
| 9670 | .suffix_not_listening←1← 9662 BVS | |||
| LDY #9 ; Y=9: 'not listening' suffix index | ||||
| 9672 | .load_suffix_offset←1← 966E BNE | |||
| LDA net_error_lookup_data,y ; Look up suffix table offset | ||||
| 9675 | TAY ; Offset to Y for indexing | |||
| 9676 | .loop_copy_suffix←1← 9680 BNE | |||
| LDA error_msg_table,y ; Load suffix byte | ||||
| 9679 | STA error_text,x ; Append to error text | |||
| 967C | BEQ done_suffix ; Null terminator? | |||
| 967E | INY ; Advance source | |||
| 967F | INX ; Advance destination | |||
| 9680 | BNE loop_copy_suffix ; Loop until end of suffix | |||
| 9682 | .done_suffix←1← 967C BEQ | |||
| BEQ check_msg_terminator ; ALWAYS branch to error dispatch | ||||
| 9684 | .build_simple_error←1← 9640 BNE | |||
| TAX ; Error class to X | ||||
| 9685 | LDY net_error_lookup_data,x ; Look up message table offset | |||
| 9688 | LDX #0 ; X=0: error text start | |||
| 968A | STX error_block ; Clear BRK byte | |||
| 968D | LDA error_msg_table,y ; Load error number from table | |||
| 9690 | JSR cond_save_error_code ; Conditionally save error code | |||
| 9693 | .loop_copy_error_msg←1← 969D BNE | |||
| LDA error_msg_table,y ; Load message byte | ||||
| 9696 | STA error_text,x ; Store in error text | |||
| 9699 | .check_msg_terminator←1← 9682 BEQ | |||
| BEQ check_net_error_code ; Null terminator? Go to error | ||||
| 969B | INY ; Advance source | |||
| 969C | INX ; Advance destination | |||
| 969D | .bad_str_anchor | |||
| BNE loop_copy_error_msg ; Loop until end of message | ||||
| 969F | EQUS "Bad" | |||
| fall through ↓ | ||||
Generate 'Bad ...' BRK error from inline stringLike error_inline, but prepends 'Bad ' to the error message. Copies the prefix from a lookup table, then appends the null-terminated inline string. The error number is passed in A. Never returns.
|
||||
| 96A2 | .error_bad_inline←11← 8FCD JSR← 91F6 JSR← 9203 JSR← 9217 JSR← 9223 JSR← 9232 JSR← 92E8 JSR← 936D JSR← 9390 JSR← A247 JSR← BC5B JSR | |||
| JSR cond_save_error_code ; Conditionally log error code to workspace | ||||
| 96A5 | TAY ; Save error number in Y | |||
| 96A6 | PLA ; Pop return address (low) — points to last byte of JSR | |||
| 96A7 | STA fs_load_addr ; Store return address low | |||
| 96A9 | PLA ; Pop return address (high) | |||
| 96AA | STA fs_load_addr_hi ; Store return address high | |||
| 96AC | LDX #0 ; X=0: start of prefix string | |||
| 96AE | .loop_copy_bad_prefix←1← 96B7 BNE | |||
| INX ; Copy 'Bad ' prefix from lookup table | ||||
| 96AF | LDA bad_prefix,x ; Get next prefix character | |||
| 96B2 | STA error_text,x ; Store in error text buffer | |||
| 96B5 | CMP #&20 ; Is it space (end of 'Bad ')? | |||
| 96B7 | BNE loop_copy_bad_prefix ; No: copy next prefix character | |||
| 96B9 | BEQ write_error_num_and_str ; ALWAYS branch | |||
Generate BRK error from inline string (with logging)Like error_inline, but first conditionally logs the error code to workspace via sub_c95fb before building the error block.
|
||||
| 96BB | .error_inline_log←11← 941B JSR← 959D JSR← A25E JSR← ABEE JSR← AC00 JSR← B475 JSR← B4EC JSR← B538 JSR← B7E0 JSR← B81B JSR← B866 JSR | |||
| JSR cond_save_error_code ; Conditionally log error code to workspace | ||||
| fall through ↓ | ||||
Generate BRK error from inline stringPops the return address from the stack and copies the null-terminated inline string into the error block at &0100. The error number is passed in A. Never returns — triggers the error via JMP error_block.
|
||||
| 96BE | .error_inline←4← A111 JSR← B9FC JSR← BB2C JSR← BBEE JSR | |||
| TAY ; Save error number in Y | ||||
| 96BF | PLA ; Pop return address (low) — points to last byte of JSR | |||
| 96C0 | STA fs_load_addr ; Store return address low | |||
| 96C2 | PLA ; Pop return address (high) | |||
| 96C3 | STA fs_load_addr_hi ; Store return address high | |||
| 96C5 | LDX #0 ; X=0: error text index | |||
| 96C7 | .write_error_num_and_str←1← 96B9 BEQ | |||
| STY error_text ; Store error number in error block | ||||
| 96CA | TYA ; Copy error number to A | |||
| 96CB | PHA ; Push error number on stack | |||
| 96CC | LDY #0 ; Y=0: inline string index | |||
| 96CE | STY error_block ; Zero the BRK byte at &0100 | |||
| 96D1 | .loop_copy_inline_str←1← 96D8 BNE | |||
| INX ; Copy inline string into error block | ||||
| 96D2 | INY ; Advance string index | |||
| 96D3 | LDA (fs_load_addr),y ; Read next byte from inline string | |||
| 96D5 | STA error_text,x ; Store byte in error block | |||
| 96D8 | BNE loop_copy_inline_str ; Loop until null terminator | |||
| 96DA | .check_net_error_code←4← 9557 JMP← 9628 JMP← 9699 BEQ← B966 JMP | |||
| LDY #&0e ; Y=&0E: offset to error code in RX buffer | ||||
| 96DC | LDA (net_rx_ptr),y ; Load network error code from reply | |||
| 96DE | BNE handle_net_error ; Non-zero: network returned an error | |||
| 96E0 | PLA ; Pop saved error number | |||
| 96E1 | CMP #&de ; Was it &DE (file server error)? | |||
| 96E3 | BEQ append_error_number ; Yes: append error number and trigger BRK | |||
| 96E5 | .trigger_brk←1← 9736 BEQ | |||
| JMP error_block ; Jump to BRK via error block | ||||
| 96E8 | .handle_net_error←1← 96DE BNE | |||
| STA net_context ; Store error code in workspace | ||||
| 96EB | PHA ; Push error code | |||
| 96EC | TXA ; Save X (error text index) | |||
| 96ED | PHA ; Push X | |||
| 96EE | LDY #&0e ; Y=&0E: error code offset | |||
| 96F0 | LDA (net_rx_ptr),y ; Load error code from RX buffer | |||
| 96F2 | STA fs_load_addr ; Save to fs_load_addr as spool handle | |||
| 96F4 | LDA #0 ; A=0: clear error code in RX buffer | |||
| 96F6 | STA (net_rx_ptr),y ; Zero the error code byte in buffer | |||
| 96F8 | LDA #&c6 ; A=&C6: OSBYTE read spool handle | |||
| 96FA | JSR osbyte_x0 ; Read current spool file handle | |||
| 96FD | CPY fs_load_addr ; Compare Y result with saved handle | |||
| 96FF | BEQ close_exec_file ; Match: close the spool file | |||
| 9701 | CPX fs_load_addr ; Compare X result with saved handle | |||
| 9703 | BNE done_close_files ; No match: skip spool close | |||
| 9705 | PHA ; Push A (preserved) | |||
| 9706 | LDA #&c6 ; A=&C6: disable spool with OSBYTE | |||
| 9708 | BNE close_spool_exec ; ALWAYS branch to close spool ALWAYS branch | |||
| 970A | .close_exec_file←1← 96FF BEQ | |||
| PHA ; Push A (preserved) | ||||
| 970B | LDA #&c7 ; A=&C7: disable exec with OSBYTE | |||
| 970D | .close_spool_exec←1← 9708 BNE | |||
| JSR osbyte_x0_y0 ; OSBYTE with X=0, Y=0 to close | ||||
| 9710 | PLA ; Pull saved handle | |||
| 9711 | TAY ; Transfer to Y for OSFIND | |||
| 9712 | LDA #osfind_close ; A=0: close file | |||
| 9714 | JSR osfind ; Close the spool/exec file Close one or all files | |||
| 9717 | .done_close_files←1← 9703 BNE | |||
| PLA ; Pull saved X (error text index) | ||||
| 9718 | TAX ; Restore X | |||
| 9719 | LDY #&0a ; Y=&0A: lookup index for 'on channel' | |||
| 971B | LDA net_error_lookup_data,y ; Load message offset from lookup table | |||
| 971E | TAY ; Transfer offset to Y | |||
| 971F | .loop_copy_channel_msg←1← 9729 BNE | |||
| LDA error_msg_table,y ; Load error message byte | ||||
| 9722 | STA error_text,x ; Append to error text buffer | |||
| 9725 | BEQ append_error_number ; Null terminator: done copying | |||
| 9727 | INX ; Advance error text index | |||
| 9728 | INY ; Advance message index | |||
| 9729 | BNE loop_copy_channel_msg ; Loop until full message copied | |||
| 972B | .append_error_number←2← 96E3 BEQ← 9725 BEQ | |||
| STX fs_load_addr_2 ; Save error text end position | ||||
| 972D | PLA ; Pull saved error number | |||
| 972E | JSR append_space_and_num ; Append ' nnn' error number suffix | |||
| 9731 | LDA #0 ; A=0: null terminator | |||
| 9733 | STA stack_page_2,x ; Terminate error text string | |||
| 9736 | BEQ trigger_brk ; ALWAYS branch to trigger BRK error ALWAYS branch | |||
Append 'net.station' decimal string to error textReads network and station numbers from the TX control block at offsets 3 and 2. Writes a space separator then the network number (if non-zero), a dot, and the station number as decimal digits into the error text buffer at the current position.
|
|||||||
| 9738 | .append_drv_dot_num←2← 9620 JSR← 965E JSR | ||||||
| LDA #&20 ; A=' ': space separator | |||||||
| 973A | STA error_text,x ; Append space to error text | ||||||
| 973D | INX ; Advance error text index | ||||||
| 973E | STX fs_load_addr_2 ; Save position for number formatting | ||||||
| 9740 | LDY #3 ; Y=3: offset to network number in TX CB | ||||||
| 9742 | LDA (net_tx_ptr),y ; Load network number | ||||||
| 9744 | BEQ append_station_num ; Zero: skip network part (local) | ||||||
| 9746 | JSR append_decimal_num ; Append network number as decimal | ||||||
| 9749 | LDX fs_load_addr_2 ; Reload error text position | ||||||
| 974B | LDA #&2e ; A='.': dot separator | ||||||
| 974D | STA error_text,x ; Append dot to error text | ||||||
| 9750 | INC fs_load_addr_2 ; Advance past dot | ||||||
| 9752 | .append_station_num←1← 9744 BEQ | ||||||
| LDY #2 ; Y=2: offset to station number in TX CB | |||||||
| 9754 | LDA (net_tx_ptr),y ; Load station number | ||||||
| 9756 | JSR append_decimal_num ; Append station number as decimal | ||||||
| 9759 | LDX fs_load_addr_2 ; Reload error text position | ||||||
| 975B | RTS ; Return | ||||||
Append space and decimal number to error textWrites a space character to the error text buffer at the current position (fs_load_addr_2), then falls through to append_decimal_num to convert the value in A to decimal digits with leading zero suppression.
|
||||
| 975C | .append_space_and_num←2← 972E JSR← B4CA JSR | |||
| TAY ; Save number in Y | ||||
| 975D | LDA #&20 ; A=' ': space prefix | |||
| 975F | LDX fs_load_addr_2 ; Load current error text position | |||
| 9761 | STA error_text,x ; Append space to error text | |||
| 9764 | INC fs_load_addr_2 ; Advance position past space | |||
| 9766 | TYA ; Restore number to A | |||
| fall through ↓ | ||||
Convert byte to decimal and append to error textExtracts hundreds, tens and units digits by three successive calls to append_decimal_digit. Uses the V flag to suppress leading zeros — hundreds and tens are skipped when zero, but the units digit is always emitted.
|
||||
| 9767 | .append_decimal_num←2← 9746 JSR← 9756 JSR | |||
| TAY ; Save number in Y for division | ||||
| 9768 | BIT bit_test_ff ; Set V: suppress leading zeros | |||
| 976B | LDA #&64 ; A=100: hundreds digit divisor | |||
| 976D | JSR append_decimal_digit ; Extract and append hundreds digit | |||
| 9770 | LDA #&0a ; A=10: tens digit divisor | |||
| 9772 | JSR append_decimal_digit ; Extract and append tens digit | |||
| 9775 | LDA #1 ; A=1: units digit (remainder) | |||
| 9777 | CLV ; Clear V: always print units digit | |||
| fall through ↓ | ||||
Extract and append one decimal digitDivides Y by A using repeated subtraction to extract a single decimal digit. Stores the ASCII digit in the error text buffer at fs_load_addr_2 unless V is set and the quotient is zero (leading zero suppression). Returns the remainder in Y for subsequent digit extraction.
|
|||||||||||||
| 9778 | .append_decimal_digit←2← 976D JSR← 9772 JSR | ||||||||||||
| STA fs_load_addr_3 ; Store divisor | |||||||||||||
| 977A | TYA ; Copy number to A for division | ||||||||||||
| 977B | LDX #&2f ; X='0'-1: digit counter (ASCII offset) | ||||||||||||
| 977D | PHP ; Save V flag (leading zero suppression) | ||||||||||||
| 977E | SEC ; Set carry for subtraction | ||||||||||||
| 977F | .loop_count_digit←1← 9782 BCS | ||||||||||||
| INX ; Increment digit counter | |||||||||||||
| 9780 | SBC fs_load_addr_3 ; Subtract divisor | ||||||||||||
| 9782 | BCS loop_count_digit ; Not negative yet: continue counting | ||||||||||||
| 9784 | ADC fs_load_addr_3 ; Add back divisor (restore remainder) | ||||||||||||
| 9786 | PLP ; Restore V flag | ||||||||||||
| 9787 | TAY ; Save remainder back to Y | ||||||||||||
| 9788 | TXA ; Digit counter to A (ASCII digit) | ||||||||||||
| 9789 | CMP #&30 ; Is digit '0'? | ||||||||||||
| 978B | BNE store_digit ; Non-zero: always print | ||||||||||||
| 978D | BVS return_from_store_digit ; V set (suppress leading zeros): skip | ||||||||||||
| 978F | .store_digit←1← 978B BNE | ||||||||||||
| CLV ; Clear V: first non-zero digit seen | |||||||||||||
| 9790 | LDX fs_load_addr_2 ; Load current text position | ||||||||||||
| 9792 | STA error_text,x ; Store ASCII digit in error text | ||||||||||||
| 9795 | INC fs_load_addr_2 ; Advance text position | ||||||||||||
| 9797 | .return_from_store_digit←1← 978D BVS | ||||||||||||
| RTS ; Return | |||||||||||||
| ; Network error lookup table (12 bytes) | |||||||||||||
| ; Each byte is an offset into error_msg_table. | |||||||||||||
| ; Indices 0-7 are keyed by error class (reply AND 7). | |||||||||||||
| ; Index 8 is used by build_no_reply_error. | |||||||||||||
| ; Indices 9-11 point to suffix strings appended | |||||||||||||
| ; after the station address in compound errors. | |||||||||||||
| 9798 | .net_error_lookup_data←5← 9606 LDY← 9644 LDY← 9672 LDA← 9685 LDY← 971B LDA | ||||||||||||
| EQUB error_msg_table - error_msg_table ; Class 0: &A0 "Line jammed" | |||||||||||||
| 9799 | EQUB msg_net_error - error_msg_table ; Class 1: &A1 "Net error" | ||||||||||||
| 979A | EQUB msg_station - error_msg_table ; Class 2: &A2 "Station" | ||||||||||||
| 979B | EQUB msg_no_clock - error_msg_table ; Class 3: &A3 "No clock" | ||||||||||||
| 979C | EQUB msg_escape - error_msg_table ; Class 4: &11 "Escape" | ||||||||||||
| 979D | EQUB msg_escape - error_msg_table ; Class 5: &11 "Escape" (duplicate) | ||||||||||||
| 979E | EQUB msg_escape - error_msg_table ; Class 6: &11 "Escape" (duplicate) | ||||||||||||
| 979F | EQUB msg_bad_option - error_msg_table ; Class 7: &CB "Bad option" | ||||||||||||
| 97A0 | EQUB msg_no_reply - error_msg_table ; Index 8: &A5 "No reply from station" | ||||||||||||
| 97A1 | EQUB msg_not_listening - error_msg_table ; Index 9: " not listening" suffix | ||||||||||||
| 97A2 | EQUB msg_on_channel - error_msg_table ; Index 10: " on channel" suffix | ||||||||||||
| 97A3 | EQUB msg_not_present - error_msg_table ; Index 11: " not present" suffix | ||||||||||||
| ; Network error message table | |||||||||||||
| ; Each entry is [error_number][string...][null]. | |||||||||||||
| ; The error number is the BRK error code stored in | |||||||||||||
| ; the error block at &0100. Entries 0-6 are complete | |||||||||||||
| ; error messages. The last 3 are suffix strings | |||||||||||||
| ; (no error number) appended to class 2 "Station" | |||||||||||||
| ; errors to form compound messages like | |||||||||||||
| ; "Station 1.254 not listening". | |||||||||||||
| 97A4 | .error_msg_table←8← 960E LDA← 9614 LDA← 9647 LDA← 9652 LDA← 9676 LDA← 968D LDA← 9693 LDA← 971F LDA | ||||||||||||
| EQUB &A0 ; Error &A0: Line jammed | |||||||||||||
| 97A5 | EQUS "Line jammed" | ||||||||||||
| 97B0 | EQUB &00 ; Null terminator | ||||||||||||
| 97B1 | .msg_net_error | ||||||||||||
| EQUB &A1 ; Error &A1: Net error | |||||||||||||
| 97B2 | EQUS "Net error" | ||||||||||||
| 97BB | EQUB &00 ; Null terminator | ||||||||||||
| 97BC | .msg_station | ||||||||||||
| EQUB &A2 ; Error &A2: Station | |||||||||||||
| 97BD | EQUS "Station" | ||||||||||||
| 97C4 | EQUB &00 ; Null terminator | ||||||||||||
| 97C5 | .msg_no_clock | ||||||||||||
| EQUB &A3 ; Error &A3: No clock | |||||||||||||
| 97C6 | EQUS "No clock" | ||||||||||||
| 97CE | EQUB &00 ; Null terminator | ||||||||||||
| 97CF | .msg_escape | ||||||||||||
| EQUB &11 ; Error &11: Escape | |||||||||||||
| 97D0 | EQUS "Escape" | ||||||||||||
| 97D6 | EQUB &00 ; Null terminator | ||||||||||||
| 97D7 | .msg_bad_option | ||||||||||||
| EQUB &CB ; Error &CB: Bad option | |||||||||||||
| 97D8 | EQUS "Bad option" | ||||||||||||
| 97E2 | EQUB &00 ; Null terminator | ||||||||||||
| 97E3 | .msg_no_reply | ||||||||||||
| EQUB &A5 ; Error &A5: No reply from station | |||||||||||||
| 97E4 | EQUS "No reply from station" | ||||||||||||
| 97F9 | EQUB &00 ; Null terminator | ||||||||||||
| 97FA | .msg_not_listening | ||||||||||||
| EQUS " not listening" ; Suffix: " not listening" | |||||||||||||
| 9808 | EQUB &00 ; Null terminator | ||||||||||||
| 9809 | .msg_on_channel | ||||||||||||
| EQUS " on channel" ; Suffix: " on channel" | |||||||||||||
| 9814 | EQUB &00 ; Null terminator | ||||||||||||
| 9815 | .msg_not_present | ||||||||||||
| EQUS " not present" ; Suffix: " not present" | |||||||||||||
| 9821 | EQUB &00 ; Null terminator | ||||||||||||
Point TX at zero-page TXCB and sendSets net_tx_ptr/net_tx_ptr_hi to &00C0 (the standard TXCB location in zero page), then falls through to send_net_packet for transmission with retry logic. |
|
| 9822 | .init_tx_ptr_and_send←2← 94D8 JSR← 9AC9 JSR |
| LDX #&c0 ; X=&C0: TX control block base (low) | |
| 9824 | STX net_tx_ptr ; Set TX pointer low |
| 9826 | LDX #0 ; X=0: TX control block base (high) |
| 9828 | STX net_tx_ptr_hi ; Set TX pointer high (page 0) |
| fall through ↓ | |
Transmit Econet packet with retryPolls for line idle, starts transmission via the ADLC, and retries on failure with a configurable count and delay. Enables escape handling after the first retry phase exhausts its count. |
|
| 982A | .send_net_packet←7← A90C JSR← A965 JMP← A9C2 JSR← ABB4 JSR← AC3F JSR← B03B JSR← B216 JSR |
| LDA tx_retry_count ; Load retry count from workspace | |
| 982D | BNE set_timeout ; Non-zero: use configured retry count |
| 982F | LDA #&ff ; A=&FF: default retry count (255) |
| 9831 | .set_timeout←1← 982D BNE |
| LDY #&60 ; Y=&60: timeout value | |
| 9833 | PHA ; Push retry count |
| 9834 | TYA ; A=&60: copy timeout to A A=&60 |
| 9835 | PHA ; Push timeout |
| 9836 | LDX #0 ; X=0: TX pointer index |
| 9838 | LDA (net_tx_ptr,x) ; Load first byte of TX control block |
| 983A | .start_tx_attempt←1← 985C BEQ |
| STA (net_tx_ptr,x) ; Write control byte back to CB | |
| 983C | PHA ; Push control byte |
| 983D | JSR poll_adlc_tx_status ; Poll ADLC until line idle |
| 9840 | ASL ; Shift left: check bit 6 (success) |
| 9841 | BPL tx_success ; Bit 6 clear: transmission complete |
| 9843 | ASL ; Shift left: check bit 5 (fatal) |
| 9844 | BEQ tx_send_error ; Zero (bit 5 clear): fatal error |
| 9846 | JSR check_escape ; Check for escape condition |
| 9849 | PLA ; Pull control byte |
| 984A | TAX ; Restore to X |
| 984B | PLA ; Pull timeout |
| 984C | TAY ; Restore to Y |
| 984D | PLA ; Pull retry count |
| 984E | BEQ try_alternate_phase ; Zero retries remaining: try alternate |
| 9850 | .loop_retry_tx←1← 9867 BNE |
| SBC #1 ; Decrement retry counter | |
| 9852 | PHA ; Push updated retry count |
| 9853 | TYA ; Copy timeout to A |
| 9854 | PHA ; Push timeout for delay loop |
| 9855 | TXA ; Copy control byte to A |
| 9856 | .loop_tx_delay←2← 9857 BNE← 985A BNE |
| DEX ; Inner delay: decrement X | |
| 9857 | BNE loop_tx_delay ; Loop until X=0 |
| 9859 | DEY ; Decrement outer counter Y |
| 985A | BNE loop_tx_delay ; Loop until Y=0 |
| 985C | BEQ start_tx_attempt ; ALWAYS branch: retry transmission ALWAYS branch |
| 985E | .try_alternate_phase←1← 984E BEQ |
| CMP tx_retry_count ; Compare retry count with alternate | |
| 9861 | BNE tx_send_error ; Different: go to error handling |
| 9863 | LDA #&80 ; A=&80: set escapable flag |
| 9865 | STA escapable ; Mark as escapable for second phase |
| 9867 | BNE loop_retry_tx ; ALWAYS branch: retry with escapable ALWAYS branch |
| 9869 | .tx_send_error←2← 9844 BEQ← 9861 BNE |
| TAX ; Result code to X | |
| 986A | JMP load_reply_and_classify ; Jump to classify reply and return |
| 986D | .tx_success←1← 9841 BPL |
| PLA ; Pull control byte | |
| 986E | PLA ; Pull timeout |
| 986F | PLA ; Pull retry count |
| 9870 | JMP clear_escapable ; Clear escapable flag and return |
| ; Pass-through TX buffer template (12 bytes) | |
| ; Overlaid onto the TX control block by | |
| ; setup_pass_txbuf for pass-through operations. | |
| ; Offsets marked &FD are skipped, preserving the | |
| ; existing destination station and network. Buffer | |
| ; addresses point to &0D3A-&0D3E in NMI workspace. | |
| ; Original TX buffer values are pushed on the stack | |
| ; and restored after transmission. | |
| 9873 | .pass_txbuf_init_table←2← 9889 LDX← 98E3 LDX |
| EQUB &88 ; Offset 0: ctrl = &88 (immediate TX) | |
| 9874 | EQUB &00 ; Offset 1: port = &00 (immediate op) |
| 9875 | EQUB &FD ; Offset 2: &FD skip (preserve dest stn) |
| 9876 | EQUB &FD ; Offset 3: &FD skip (preserve dest net) |
| 9877 | EQUB &3A ; Offset 4: buf start lo (&3A) |
| 9878 | EQUB &0D ; Offset 5: buf start hi (&0D) -> &0D3A |
| 9879 | EQUB &FF ; Offset 6: extended addr fill (&FF) |
| 987A | EQUB &FF ; Offset 7: extended addr fill (&FF) |
| 987B | EQUB &3E ; Offset 8: buf end lo (&3E) |
| 987C | EQUB &0D ; Offset 9: buf end hi (&0D) -> &0D3E |
| 987D | EQUB &FF ; Offset 10: extended addr fill (&FF) |
| 987E | EQUB &FF ; Offset 11: extended addr fill (&FF) |
Set up TX pointer and send pass-through packetCopies the template into the TX buffer (skipping &FD markers), saves original values on stack, then polls the ADLC and retries until complete. |
|
| 987F | .init_tx_ptr_for_pass←1← 8DF4 JSR |
| LDY #&c0 ; Y=&C0: TX control block base (low) | |
| 9881 | STY net_tx_ptr ; Set TX pointer low byte |
| 9883 | LDY #0 ; Y=0: TX control block base (high) |
| 9885 | STY net_tx_ptr_hi ; Set TX pointer high byte |
| fall through ↓ | |
Initialise TX buffer from pass-through templateCopies 12 bytes from pass_txbuf_init_table into the TX control block, pushing the original values on the stack for later restoration. Skips offsets marked &FD in the template. Starts transmission via poll_adlc_tx_status and retries on failure, restoring the original TX buffer contents when done. |
|
| 9887 | .setup_pass_txbuf←1← ABB1 JSR |
| LDY #&0b ; Y=&0B: 12 bytes to process (0-11) | |
| 9889 | .loop_copy_template←1← 9897 BPL |
| LDX pass_txbuf_init_table,y ; Load template byte for this offset | |
| 988C | CPX #&fd ; Is it &FD (skip marker)? |
| 988E | BEQ skip_template_byte ; Yes: skip this offset, don't modify |
| 9890 | LDA (net_tx_ptr),y ; Load existing TX buffer byte |
| 9892 | PHA ; Save original value on stack |
| 9893 | TXA ; Copy template value to A |
| 9894 | STA (net_tx_ptr),y ; Store template value to TX buffer |
| 9896 | .skip_template_byte←1← 988E BEQ |
| DEY ; Next offset (descending) | |
| 9897 | BPL loop_copy_template ; Loop until all 12 bytes processed |
| 9899 | LDA peek_retry_count ; Load pass-through control value |
| 989C | PHA ; Push control value |
| 989D | TYA ; A=&FF (Y is &FF after loop) |
| 989E | PHA ; Push &FF as timeout |
| 989F | LDX #0 ; X=0: TX pointer index |
| 98A1 | LDA (net_tx_ptr,x) ; Load control byte from TX CB |
| 98A3 | .start_pass_tx←1← 98DC BEQ |
| STA (net_tx_ptr,x) ; Write control byte to start TX | |
| 98A5 | PHA ; Save control byte on stack |
| 98A6 | JSR poll_adlc_tx_status ; Poll ADLC until line idle |
| 98A9 | ASL ; Shift result: check bit 6 (success) |
| 98AA | BPL pass_tx_success ; Bit 6 clear: transmission complete |
| 98AC | ASL ; Shift result: check bit 5 (fatal) |
| 98AD | BNE restore_retry_state ; Non-zero (not fatal): retry |
| 98AF | .done_pass_retries←1← 98CE BEQ |
| LDX #0 ; X=0: clear error status | |
| 98B1 | JMP fixup_reply_status_a ; Jump to fix up reply status |
Poll ADLC and start frame transmissionShifts the workspace status byte left in a loop, then copies the TX pointer into the NMI TX block and calls tx_begin to start frame transmission. Returns the TX completion status in A. |
|
| 98B4 | .poll_adlc_tx_status←3← 983D JSR← 98A6 JSR← 98B7 BCC |
| ASL ws_0d60 ; Shift ws_0d60 left to poll ADLC | |
| 98B7 | BCC poll_adlc_tx_status ; Bit not set: keep polling |
| 98B9 | LDA net_tx_ptr ; Copy TX pointer low to NMI TX block |
| 98BB | STA nmi_tx_block ; Store in NMI TX block low |
| 98BD | LDA net_tx_ptr_hi ; Copy TX pointer high |
| 98BF | STA nmi_tx_block_hi ; Store in NMI TX block high |
| 98C1 | JSR tx_begin ; Begin Econet frame transmission |
| 98C4 | .loop_poll_pass_tx←1← 98C6 BMI |
| LDA (net_tx_ptr,x) ; Read TX status byte | |
| 98C6 | BMI loop_poll_pass_tx ; Bit 7 set: still transmitting |
| 98C8 | RTS ; Return with result in A |
| 98C9 | .restore_retry_state←1← 98AD BNE |
| PLA ; Pull control byte | |
| 98CA | TAX ; Restore to X |
| 98CB | PLA ; Pull timeout |
| 98CC | TAY ; Restore to Y |
| 98CD | PLA ; Pull retry count |
| 98CE | BEQ done_pass_retries ; Zero retries: go to error handling |
| 98D0 | SBC #1 ; Decrement retry counter |
| 98D2 | PHA ; Push updated retry count |
| 98D3 | TYA ; Copy timeout to A |
| 98D4 | PHA ; Push timeout |
| 98D5 | TXA ; Copy control byte to A |
| 98D6 | .loop_pass_tx_delay←2← 98D7 BNE← 98DA BNE |
| DEX ; Inner delay loop: decrement X | |
| 98D7 | BNE loop_pass_tx_delay ; Loop until X=0 |
| 98D9 | DEY ; Decrement outer counter Y |
| 98DA | BNE loop_pass_tx_delay ; Loop until Y=0 |
| 98DC | BEQ start_pass_tx ; ALWAYS branch: retry transmission ALWAYS branch |
| 98DE | .pass_tx_success←1← 98AA BPL |
| PLA ; Pull control byte (discard) | |
| 98DF | PLA ; Pull timeout (discard) |
| 98E0 | PLA ; Pull retry count (discard) |
| 98E1 | LDY #0 ; Y=0: start restoring from offset 0 |
| 98E3 | .loop_restore_txbuf←1← 98F0 BNE |
| LDX pass_txbuf_init_table,y ; Load template byte for this offset | |
| 98E6 | CPX #&fd ; Is it &FD (skip marker)? |
| 98E8 | BEQ skip_restore_byte ; Yes: don't restore this offset |
| 98EA | PLA ; Pull original value from stack |
| 98EB | STA (net_tx_ptr),y ; Restore original TX buffer byte |
| 98ED | .skip_restore_byte←1← 98E8 BEQ |
| INY ; Next offset (ascending) | |
| 98EE | CPY #&0c ; Processed all 12 bytes? |
| 98F0 | BNE loop_restore_txbuf ; No: continue restoring |
| 98F2 | RTS ; Return with TX buffer restored |
| 98F3 | .load_text_ptr_and_parse←1← 9924 JSR |
| LDY #1 ; Y=1: start at second byte of pointer | |
| 98F5 | .loop_copy_text_ptr←1← 98FB BPL |
| LDA (fs_options),y ; Load pointer byte from FS options | |
| 98F7 | STA os_text_ptr,y ; Store in OS text pointer |
| 98FA | DEY ; Decrement index |
| 98FB | BPL loop_copy_text_ptr ; Loop until both bytes copied |
| 98FD | LDY #0 ; Y=0: reset command line offset |
| fall through ↓ | |
Parse command line via GSINIT/GSREAD into &0E30Calls GSINIT to initialise string reading, then loops calling GSREAD to copy characters into the l0e30 buffer until end-of-string. Appends a CR terminator and sets fs_crc_lo/hi to point at &0E30 for subsequent parsing routines. |
|
| 98FF | .gsread_to_buf←1← AE82 JSR |
| LDX #&ff ; X=&FF: pre-increment for buffer index | |
| 9901 | CLC ; C=0: initialise for string input |
| 9902 | JSR gsinit ; GSINIT: initialise string reading |
| 9905 | BEQ terminate_buf ; Z set (empty string): store terminator |
| 9907 | .loop_gsread_char←1← 9910 BCC |
| JSR gsread ; GSREAD: read next character | |
| 990A | BCS terminate_buf ; C set: end of string reached |
| 990C | INX ; Advance buffer index |
| 990D | STA fs_filename_buf,x ; Store character in l0e30 buffer |
| 9910 | BCC loop_gsread_char ; ALWAYS branch: read next character ALWAYS branch |
| 9912 | .terminate_buf←2← 9905 BEQ← 990A BCS |
| INX ; Advance past last character | |
| 9913 | LDA #&0d ; A=CR: terminate filename |
| 9915 | STA fs_filename_buf,x ; Store CR terminator in buffer |
| 9918 | LDA #&30 ; A=&30: low byte of l0e30 buffer |
| 991A | STA fs_crc_lo ; Set command text pointer low |
| 991C | LDA #&0e ; A=&0E: high byte of l0e30 buffer |
| 991E | STA fs_crc_hi ; Set command text pointer high |
| 9920 | RTS ; Return with buffer filled |
| 9921 | JSR set_xfer_params ; Set up transfer parameters |
| 9924 | JSR load_text_ptr_and_parse ; Load text pointer and parse filename |
| 9927 | JSR mask_owner_access ; Set owner-only access mask |
| 992A | JSR parse_access_prefix ; Parse access prefix from filename |
| 992D | LDA fs_last_byte_flag ; Load last byte flag |
| 992F | BPL check_display_type ; Positive (not last): display file info |
| 9931 | CMP #&ff ; Is it &FF (last entry)? |
| 9933 | BEQ copy_arg_and_enum ; Yes: copy arg and iterate |
| 9935 | JMP return_with_last_flag ; Other value: return with flag |
| 9938 | .copy_arg_and_enum←1← 9933 BEQ |
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer at X=0 | |
| 993B | LDY #2 ; Y=2: enumerate directory command |
| fall through ↓ | |
Execute one iteration of a multi-step FS commandCalled by match_fs_cmd for commands that enumerate directory entries. Sets port &92, sends the initial request via send_request_write, then synchronises the FS options and workspace state (order depends on the cycle flag at offset 6). Copies 4 address bytes, formats the filename field, sends via send_txcb_swap_addrs, and receives the reply. |
|
| 993D | .do_fs_cmd_iteration←1← A2B9 JSR |
| LDA #&92 ; A=&92: FS port number | |
| 993F | STA escapable ; Set escapable flag to &92 |
| 9941 | STA fs_cmd_urd ; Store port number in TX buffer |
| 9944 | JSR send_request_write ; Send request to file server |
| 9947 | LDY #6 ; Y=6: offset to response cycle flag |
| 9949 | LDA (fs_options),y ; Load cycle flag from FS options |
| 994B | BNE copy_ws_then_fsopts ; Non-zero: already initialised |
| 994D | JSR copy_fsopts_to_zp ; Copy FS options to zero page first |
| 9950 | JSR copy_workspace_to_fsopts ; Then copy workspace to FS options |
| 9953 | BCC setup_txcb_addrs ; Branch to continue (C clear from JSR) |
| 9955 | .copy_ws_then_fsopts←1← 994B BNE |
| JSR copy_workspace_to_fsopts ; Copy workspace to FS options first | |
| 9958 | JSR copy_fsopts_to_zp ; Then copy FS options to zero page |
| 995B | .setup_txcb_addrs←1← 9953 BCC |
| LDY #4 ; Y=4: loop counter | |
| 995D | .loop_copy_addrs←1← 9968 BNE |
| LDA fs_load_addr,x ; Load address byte from zero page | |
| 995F | STA txcb_end,x ; Save to TXCB end pointer |
| 9961 | ADC fs_file_len,x ; Add offset from buffer |
| 9964 | STA fs_work_4,x ; Store sum in fs_work area |
| 9966 | INX ; Advance to next byte |
| 9967 | DEY ; Decrement counter |
| 9968 | BNE loop_copy_addrs ; Loop for all 4 bytes |
| 996A | SEC ; Set carry for subtraction |
| 996B | SBC fs_file_len_3 ; Subtract high offset |
| 996E | STA fs_work_7 ; Store result in fs_work_7 |
| 9970 | JSR format_filename_field ; Format filename for display |
| 9973 | JSR send_txcb_swap_addrs ; Send TXCB and swap addresses |
| 9976 | LDX #2 ; X=2: copy 3 offset bytes |
| 9978 | .loop_copy_offsets←1← 997F BPL |
| LDA fs_file_len_3,x ; Load offset byte from l0f10 | |
| 997B | STA fs_cmd_data,x ; Store in l0f05 for next iteration |
| 997E | DEX ; Decrement counter |
| 997F | BPL loop_copy_offsets ; Loop until all bytes copied |
| 9981 | JMP recv_reply ; Jump to receive and process reply |
Send TXCB and swap start/end addressesIf the 5-byte handle matches, returns immediately. Otherwise sets port &92, copies addresses, sends, waits for acknowledgment, and retries on address mismatch. |
|
| 9984 | .send_txcb_swap_addrs←2← 9973 JSR← 9F40 JSR |
| JSR cmp_5byte_handle ; Compare 5-byte handle with current | |
| 9987 | BEQ return_from_txcb_swap ; Match: no need to send, return |
| 9989 | LDA #&92 ; A=&92: FS reply port number |
| 998B | STA txcb_port ; Set TXCB port |
| 998D | .loop_swap_and_send←1← 99A9 BNE |
| LDX #3 ; X=3: copy 4 bytes | |
| 998F | .loop_copy_start_end←1← 9998 BPL |
| LDA txcb_end,x ; Load TXCB end pointer byte | |
| 9991 | STA txcb_start,x ; Store in TXCB start pointer |
| 9993 | LDA fs_work_4,x ; Load new end address from fs_work |
| 9995 | STA txcb_end,x ; Store in TXCB end pointer |
| 9997 | DEX ; Decrement counter |
| 9998 | BPL loop_copy_start_end ; Loop for all 4 bytes |
| 999A | LDA #&7f ; A=&7F: control byte for data transfer |
| 999C | STA txcb_ctrl ; Set TXCB control byte |
| 999E | JSR wait_net_tx_ack ; Wait for network TX acknowledgement |
| 99A1 | LDY #3 ; Y=3: compare 4 bytes |
| 99A3 | .loop_verify_addrs←1← 99AC BPL |
| LDA txcb_end,y ; Load TXCB end byte | |
| 99A6 | EOR fs_work_4,y ; Compare with expected end address |
| 99A9 | BNE loop_swap_and_send ; Mismatch: resend from start |
| 99AB | DEY ; Decrement counter |
| 99AC | BPL loop_verify_addrs ; Loop until all 4 bytes match |
| 99AE | .return_from_txcb_swap←1← 9987 BEQ |
| RTS ; Return (all bytes match) | |
| 99AF | .check_display_type←1← 992F BPL |
| BEQ setup_dir_display ; Z set: directory entry display | |
| 99B1 | JMP dispatch_osword_op ; Non-zero: jump to OSWORD dispatch |
| 99B4 | .setup_dir_display←2← 99AF BEQ← 9AE6 JMP |
| LDX #4 ; X=4: loop counter for 4 iterations | |
| 99B6 | LDY #&0e ; Y=&0E: FS options offset for addresses |
| 99B8 | SEC ; Set carry for subtraction |
| 99B9 | .loop_compute_diffs←1← 99D3 BNE |
| LDA (fs_options),y ; Load address byte from FS options | |
| 99BB | STA port_ws_offset,y ; Save to workspace (port_ws_offset) |
| 99BE | JSR retreat_y_by_4 ; Y -= 4 to point to paired offset |
| 99C1 | SBC (fs_options),y ; Subtract paired value |
| 99C3 | STA fs_cmd_csd,y ; Store difference in l0f03 buffer |
| 99C6 | PHA ; Push difference |
| 99C7 | LDA (fs_options),y ; Load paired value from FS options |
| 99C9 | STA port_ws_offset,y ; Save to workspace |
| 99CC | PLA ; Pull difference back |
| 99CD | STA (fs_options),y ; Store in FS options for display |
| 99CF | JSR skip_one_and_advance5 ; Advance Y by 5 for next field |
| 99D2 | DEX ; Decrement loop counter |
| 99D3 | BNE loop_compute_diffs ; Loop for all 4 address pairs |
| 99D5 | LDY #9 ; Y=9: copy 9 bytes of options data |
| 99D7 | .loop_copy_fs_options←1← 99DD BNE |
| LDA (fs_options),y ; Load FS options byte | |
| 99D9 | STA fs_cmd_csd,y ; Store in l0f03 buffer |
| 99DC | DEY ; Decrement index |
| 99DD | BNE loop_copy_fs_options ; Loop until all 9 bytes copied |
| 99DF | LDA #&91 ; A=&91: FS port for info request |
| 99E1 | STA escapable ; Set escapable flag |
| 99E3 | STA fs_cmd_urd ; Store port in TX buffer |
| 99E6 | STA fs_error_ptr ; Store in fs_error_ptr |
| 99E8 | LDX #&0b ; X=&0B: copy argument at offset 11 |
| 99EA | JSR copy_arg_to_buf ; Copy argument to TX buffer |
| 99ED | LDY #1 ; Y=1: info sub-command |
| 99EF | LDA fs_last_byte_flag ; Load last byte flag |
| 99F1 | CMP #7 ; Is it 7 (catalogue info)? |
| 99F3 | PHP ; Save comparison result |
| 99F4 | BNE send_info_request ; Not 7: keep Y=1 |
| 99F6 | LDY #&1d ; Y=&1D: extended info command |
| 99F8 | .send_info_request←1← 99F4 BNE |
| JSR send_request_write ; Send request to file server | |
| 99FB | JSR format_filename_field ; Format filename for display |
| 99FE | PLP ; Restore comparison flags |
| 99FF | BNE setup_txcb_transfer ; Not catalogue info: show short format |
| 9A01 | LDX #0 ; X=0: start at first byte |
| 9A03 | BEQ store_result ; ALWAYS branch to store and display ALWAYS branch |
| 9A05 | .setup_txcb_transfer←1← 99FF BNE |
| LDA fs_cmd_data ; Load file handle from l0f05 | |
| 9A08 | JSR check_and_setup_txcb ; Check and set up TXCB for transfer |
| 9A0B | .recv_reply←1← 9981 JMP |
| JSR recv_and_process_reply ; Receive and process reply | |
| 9A0E | .store_result←1← 9A03 BEQ |
| STX fs_reply_cmd ; Store result byte in l0f08 | |
| 9A11 | LDY #&0e ; Y=&0E: protection bits offset |
| 9A13 | LDA fs_cmd_data ; Load access byte from l0f05 |
| 9A16 | JSR get_prot_bits ; Extract protection bit flags |
| 9A19 | BEQ store_prot_byte ; Zero: use reply buffer data |
| 9A1B | .loop_copy_file_info←1← 9A23 BNE |
| LDA fs_reply_data,y ; Load file info byte from l0ef7 | |
| 9A1E | .store_prot_byte←1← 9A19 BEQ |
| STA (fs_options),y ; Store in FS options at offset Y | |
| 9A20 | INY ; Advance to next byte |
| 9A21 | CPY #&12 ; Y=&12: end of protection fields? |
| 9A23 | BNE loop_copy_file_info ; No: copy next byte |
| 9A25 | LDY fs_messages_flag ; Load display flag from l0e06 |
| 9A28 | BEQ return_from_advance_y ; Zero: skip display, return |
| 9A2A | LDY #0 ; Y=0: filename character index |
| 9A2C | .loop_print_filename←1← 9A35 BNE |
| LDA filename_buf,y ; Load filename character from l10f3 | |
| 9A2F | JSR osasci ; Print character via OSASCI Write character |
| 9A32 | INY ; Advance to next character |
| 9A33 | CPY #&0c ; Printed all 12 characters? |
| 9A35 | BNE loop_print_filename ; No: print next character |
| 9A37 | LDY #5 ; Y=5: offset for access string |
| 9A39 | JSR print_5_hex_bytes ; Print 5 hex bytes (access info) |
| 9A3C | JSR print_load_exec_addrs ; Print load and exec addresses |
| 9A3F | JSR osnewl ; Print newline Write newline (characters 10 and 13) |
| 9A42 | JMP return_with_last_flag ; Jump to return with last flag |
Print exec address and file length in hexPrints the exec address as 5 hex bytes from (fs_options) offset 9 downwards, then the file length as 3 hex bytes from offset &0C. Each group is followed by a space separator via OSASCI. |
|
| 9A45 | .print_load_exec_addrs←1← 9A3C JSR |
| LDY #9 ; Y=9: offset for exec address | |
| 9A47 | JSR print_5_hex_bytes ; Print 5 hex bytes (exec address) |
| 9A4A | LDY #&0c ; Y=&0C: offset for length (3 bytes) |
| 9A4C | LDX #3 ; X=3: print 3 bytes only |
| 9A4E | BNE loop_print_hex_byte ; ALWAYS branch to print routine ALWAYS branch |
Print hex byte sequence from FS optionsOutputs X+1 bytes from (fs_options) starting at offset Y, decrementing Y for each byte (big-endian display order). Each byte is printed as two hex digits via print_hex_byte. Finishes with a trailing space via OSASCI. The default entry with X=4 prints 5 bytes (a full 32-bit address plus extent).
|
||||||
| 9A50 | .print_5_hex_bytes←2← 9A39 JSR← 9A47 JSR | |||||
| LDX #4 ; X=4: print 5 bytes (4 to 0) | ||||||
| 9A52 | .loop_print_hex_byte←2← 9A4E BNE← 9A59 BNE | |||||
| LDA (fs_options),y ; Load byte from FS options at offset Y | ||||||
| 9A54 | JSR print_hex_byte ; Print as 2-digit hex | |||||
| 9A57 | DEY ; Decrement byte offset | |||||
| 9A58 | DEX ; Decrement byte count | |||||
| 9A59 | BNE loop_print_hex_byte ; Loop until all bytes printed | |||||
| 9A5B | LDA #&20 ; A=' ': space separator | |||||
| 9A5D | JMP osasci ; Print space via OSASCI and return Write character 32 | |||||
Copy FS options address bytes to zero pageCopies 4 bytes from (fs_options) at offsets 2-5 into zero page at &00AE+Y. Used by do_fs_cmd_iteration to preserve the current address state. Falls through to skip_one_and_advance5 to advance Y past the copied region. |
|
| 9A60 | .copy_fsopts_to_zp←2← 994D JSR← 9958 JSR |
| LDY #5 ; Y=5: copy 4 bytes (offsets 2-5) | |
| 9A62 | .loop_copy_fsopts_byte←1← 9A6A BCS |
| LDA (fs_options),y ; Load byte from FS options | |
| 9A64 | STA work_ae,y ; Store in zero page at l00ae+Y |
| 9A67 | DEY ; Decrement index |
| 9A68 | CPY #2 ; Below offset 2? |
| 9A6A | BCS loop_copy_fsopts_byte ; No: copy next byte |
| fall through ↓ | |
Advance Y by 5Entry point one INY before advance_y_by_4, giving a total Y increment of 5. Used to skip past a 5-byte address/length structure in the FS options block. |
|
| 9A6C | .skip_one_and_advance5←1← 99CF JSR |
| INY ; Y += 5 | |
| fall through ↓ | |
Advance Y by 4Four consecutive INY instructions. Used as a subroutine to step Y past a 4-byte address field in the FS options or workspace structure.
|
|||||||
| 9A6D | .advance_y_by_4←2← 9F1D JSR← B054 JSR | ||||||
| INY ; Y += 4 | |||||||
| 9A6E | INY ; (continued) | ||||||
| 9A6F | INY ; (continued) | ||||||
| 9A70 | INY ; (continued) | ||||||
| 9A71 | .return_from_advance_y←1← 9A28 BEQ | ||||||
| RTS ; Return | |||||||
Copy workspace reply data to FS optionsCopies bytes from the reply buffer at &0F02+Y into (fs_options) at offsets &0D down to 2. Used to update the FS options block with data returned from the file server. Falls through to retreat_y_by_4. |
|
| 9A72 | .copy_workspace_to_fsopts←2← 9950 JSR← 9955 JSR |
| LDY #&0d ; Y=&0D: copy bytes from offset &0D down | |
| 9A74 | TXA ; Transfer X to A |
| 9A75 | .loop_copy_ws_byte←1← 9A7D BCS |
| STA (fs_options),y ; Store byte in FS options at offset Y | |
| 9A77 | LDA fs_cmd_urd,y ; Load next workspace byte from l0f02+Y |
| 9A7A | DEY ; Decrement index |
| 9A7B | CPY #2 ; Below offset 2? |
| 9A7D | BCS loop_copy_ws_byte ; No: copy next byte |
| fall through ↓ | |
Retreat Y by 4Four consecutive DEY instructions. Companion to advance_y_by_4 for reverse traversal of address structures.
|
|||||||
| 9A7F | .retreat_y_by_4←1← 99BE JSR | ||||||
| DEY ; Y -= 4 | |||||||
| fall through ↓ | |||||||
Retreat Y by 3Three consecutive DEY instructions. Used by setup_transfer_workspace to step back through interleaved address pairs in the FS options block.
|
|||||||
| 9A80 | .retreat_y_by_3←2← 9AFC JSR← 9F25 JSR | ||||||
| DEY ; Y -= 3 | |||||||
| 9A81 | DEY ; (continued) | ||||||
| 9A82 | DEY ; (continued) | ||||||
| 9A83 | RTS ; Return | ||||||
| 9A84 | .discard_handle_match←2← 9A8C BEQ← 9AD2 BCS | ||||||
| PLA ; Discard stacked value | |||||||
| 9A85 | LDY fs_block_offset ; Restore Y from fs_block_offset | ||||||
| 9A87 | RTS ; Return (handle already matches) | ||||||
Set up data transfer TXCB and dispatch replyCompares the 5-byte handle; if unchanged, returns. Otherwise computes start/end addresses with overflow clamping, sets the port and control byte, sends the packet, and dispatches on the reply sub-operation code. |
|
| 9A88 | .check_and_setup_txcb←2← 9A08 JSR← 9F3B JSR |
| PHA ; Save port/sub-function on stack | |
| 9A89 | JSR cmp_5byte_handle ; Compare 5-byte handle with current |
| 9A8C | BEQ discard_handle_match ; Match: discard port and return |
| 9A8E | .init_transfer_addrs←1← 9ADB BNE |
| LDX #0 ; X=0: loop start | |
| 9A90 | LDY #4 ; Y=4: copy 4 bytes |
| 9A92 | STX fs_reply_cmd ; Clear l0f08 (transfer size low) |
| 9A95 | STX fs_load_vector ; Clear l0f09 (transfer size high) |
| 9A98 | CLC ; Clear carry for addition |
| 9A99 | .loop_copy_addr_offset←1← 9AA6 BNE |
| LDA fs_load_addr,x ; Load address byte from zero page | |
| 9A9B | STA txcb_start,x ; Store in TXCB start pointer |
| 9A9D | ADC fs_func_code,x ; Add offset from l0f06 |
| 9AA0 | STA txcb_end,x ; Store sum in TXCB end pointer |
| 9AA2 | STA fs_load_addr,x ; Also update load address |
| 9AA4 | INX ; Advance to next byte |
| 9AA5 | DEY ; Decrement counter |
| 9AA6 | BNE loop_copy_addr_offset ; Loop for all 4 bytes |
| 9AA8 | BCS clamp_end_to_limit ; Carry set: overflow, use limit |
| 9AAA | SEC ; Set carry for subtraction |
| 9AAB | .loop_check_vs_limit←1← 9AB3 BNE |
| LDA fs_load_addr,y ; Load computed end address | |
| 9AAE | SBC fs_work_4,y ; Subtract maximum from fs_work_4 |
| 9AB1 | INY ; Advance to next byte |
| 9AB2 | DEX ; Decrement counter |
| 9AB3 | BNE loop_check_vs_limit ; Loop for all bytes |
| 9AB5 | BCC set_port_and_ctrl ; Below limit: keep computed end |
| 9AB7 | .clamp_end_to_limit←1← 9AA8 BCS |
| LDX #3 ; X=3: copy 4 bytes of limit | |
| 9AB9 | .loop_copy_limit←1← 9ABE BPL |
| LDA fs_work_4,x ; Load limit from fs_work_4 | |
| 9ABB | STA txcb_end,x ; Store as TXCB end |
| 9ABD | DEX ; Decrement counter |
| 9ABE | BPL loop_copy_limit ; Loop for all 4 bytes |
| 9AC0 | .set_port_and_ctrl←1← 9AB5 BCC |
| PLA ; Pull port from stack | |
| 9AC1 | PHA ; Push back (keep for later) |
| 9AC2 | PHP ; Save flags (carry = overflow state) |
| 9AC3 | STA txcb_port ; Set TXCB port number |
| 9AC5 | LDA #&80 ; A=&80: control byte for data request |
| 9AC7 | STA txcb_ctrl ; Set TXCB control byte |
| 9AC9 | JSR init_tx_ptr_and_send ; Init TX pointer and send packet |
| 9ACC | LDA fs_error_ptr ; Load error pointer |
| 9ACE | JSR init_txcb_port ; Init TXCB port from error pointer |
| 9AD1 | PLP ; Restore overflow flags |
| 9AD2 | BCS discard_handle_match ; Carry set: discard and return |
| 9AD4 | LDA #&91 ; A=&91: FS reply port |
| 9AD6 | STA txcb_port ; Set TXCB port for reply |
| 9AD8 | JSR wait_net_tx_ack ; Wait for TX acknowledgement |
| 9ADB | BNE init_transfer_addrs ; Non-zero (not done): retry send |
| 9ADD | .dispatch_osword_op←1← 99B1 JMP |
| STA fs_cmd_data ; Store sub-operation code | |
| 9AE0 | CMP #7 ; Compare with 7 |
| 9AE2 | BCC dispatch_ops_1_to_6 ; Below 7: handle operations 1-6 |
| 9AE4 | BNE skip_if_error ; Above 7: jump to handle via finalise |
| 9AE6 | JMP setup_dir_display ; Equal to 7: jump to directory display |
| 9AE9 | .dispatch_ops_1_to_6←1← 9AE2 BCC |
| CMP #6 ; Compare with 6 | |
| 9AEB | BEQ send_delete_request ; 6: delete file operation |
| 9AED | CMP #5 ; Compare with 5 |
| 9AEF | BEQ read_cat_info ; 5: read catalogue info |
| 9AF1 | CMP #4 ; Compare with 4 |
| 9AF3 | BEQ setup_write_access ; 4: write file attributes |
| 9AF5 | CMP #1 ; Compare with 1 |
| 9AF7 | BEQ setup_save_access ; 1: read file info |
| 9AF9 | ASL ; Shift left twice: A*4 |
| 9AFA | ASL ; A*4 |
| 9AFB | TAY ; Copy to Y as index |
| 9AFC | JSR retreat_y_by_3 ; Y -= 3 to get FS options offset |
| 9AFF | LDX #3 ; X=3: copy 4 bytes |
| 9B01 | .loop_copy_fsopts_4←1← 9B08 BPL |
| LDA (fs_options),y ; Load byte from FS options at offset Y | |
| 9B03 | STA fs_func_code,x ; Store in l0f06 buffer |
| 9B06 | DEY ; Decrement source offset |
| 9B07 | DEX ; Decrement byte count |
| 9B08 | BPL loop_copy_fsopts_4 ; Loop for all 4 bytes |
| 9B0A | LDX #5 ; X=5: copy arg to buffer at offset 5 |
| 9B0C | BNE send_save_or_access ; ALWAYS branch to copy and send ALWAYS branch |
| 9B0E | .setup_save_access←1← 9AF7 BEQ |
| JSR get_access_bits ; Get access bits for file | |
| 9B11 | STA fs_file_attrs ; Store access byte in l0f0e |
| 9B14 | LDY #9 ; Y=9: source offset in FS options |
| 9B16 | LDX #8 ; X=8: copy 8 bytes to buffer |
| 9B18 | .loop_copy_fsopts_8←1← 9B1F BNE |
| LDA (fs_options),y ; Load FS options byte | |
| 9B1A | STA fs_cmd_data,x ; Store in l0f05 buffer |
| 9B1D | DEY ; Decrement source offset |
| 9B1E | DEX ; Decrement byte count |
| 9B1F | BNE loop_copy_fsopts_8 ; Loop for all 8 bytes |
| 9B21 | LDX #&0a ; X=&0A: buffer offset for argument |
| 9B23 | .send_save_or_access←2← 9B0C BNE← 9B42 BNE |
| JSR copy_arg_to_buf ; Copy argument to buffer | |
| 9B26 | LDY #&13 ; Y=&13: OSWORD &13 (NFS operation) |
| 9B28 | BNE send_request_vset ; ALWAYS branch to send request ALWAYS branch |
| 9B2A | .send_delete_request←1← 9AEB BEQ |
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer at X=0 | |
| 9B2D | LDY #&14 ; Y=&14: delete file command |
| 9B2F | .send_request_vset←1← 9B28 BNE |
| BIT bit_test_ff ; Set V flag (no directory check) | |
| 9B32 | JSR save_net_tx_cb_vset ; Send request with V set |
| 9B35 | .skip_if_error←1← 9AE4 BNE |
| BCS done_osword_op ; Carry set: error, jump to finalise | |
| 9B37 | JMP return_with_last_flag ; No error: return with last flag |
| 9B3A | .setup_write_access←1← 9AF3 BEQ |
| JSR get_access_bits ; Get access bits for file | |
| 9B3D | STA fs_func_code ; Store in l0f06 |
| 9B40 | LDX #2 ; X=2: buffer offset |
| 9B42 | BNE send_save_or_access ; ALWAYS branch to copy and send ALWAYS branch |
| 9B44 | .read_cat_info←1← 9AEF BEQ |
| LDX #1 ; X=1: buffer offset | |
| 9B46 | JSR copy_arg_to_buf ; Copy argument to buffer |
| 9B49 | LDY #&12 ; Y=&12: open file command |
| 9B4B | JSR save_net_tx_cb ; Send open file request |
| 9B4E | LDA fs_obj_type ; Load reply handle from l0f11 |
| 9B51 | STX fs_obj_type ; Clear l0f11 |
| 9B54 | STX fs_len_clear ; Clear l0f14 |
| 9B57 | JSR get_prot_bits ; Get protection bits |
| 9B5A | LDX fs_cmd_data ; Load file handle from l0f05 |
| 9B5D | BEQ return_with_handle ; Zero: file not found, return |
| 9B5F | LDY #&0e ; Y=&0E: store access bits |
| 9B61 | STA (fs_options),y ; Store access byte in FS options |
| 9B63 | DEY ; Y=&0D Y=&0d |
| 9B64 | LDX #&0c ; X=&0C: copy 12 bytes of file info |
| 9B66 | .loop_copy_cat_info←1← 9B6D BNE |
| LDA fs_cmd_data,x ; Load reply byte from l0f05+X | |
| 9B69 | STA (fs_options),y ; Store in FS options at offset Y |
| 9B6B | DEY ; Decrement destination offset |
| 9B6C | DEX ; Decrement source counter |
| 9B6D | BNE loop_copy_cat_info ; Loop for all 12 bytes |
| 9B6F | INX ; X=1 (INX from 0) |
| 9B70 | INX ; X=2 |
| 9B71 | LDY #&11 ; Y=&11: FS options offset |
| 9B73 | .loop_copy_ext_info←1← 9B7A BPL |
| LDA fs_access_level,x ; Load extended info byte from l0f12 | |
| 9B76 | STA (fs_options),y ; Store in FS options |
| 9B78 | DEY ; Decrement destination offset |
| 9B79 | DEX ; Decrement source counter |
| 9B7A | BPL loop_copy_ext_info ; Loop until all copied |
| 9B7C | LDX fs_cmd_data ; Reload file handle |
| 9B7F | .return_with_handle←1← 9B5D BEQ |
| TXA ; Transfer to A | |
| 9B80 | .done_osword_op←1← 9B35 BCS |
| JMP finalise_and_return ; Jump to finalise and return | |
| ; Unreachable dead code (3 bytes) | |
| ; Duplicate of the JMP at &9B80 immediately above. | |
| ; Unreachable after the unconditional JMP and | |
| ; unreferenced. Likely a development remnant. | |
| 9B83 | JMP finalise_and_return ; Dead: duplicate JMP finalise_and_return |
Format filename into fixed-width display fieldBuilds a 12-character space-padded filename at &10F3 for directory listing output. Sources the name from either the command line or the l0f05 reply buffer depending on the value in l0f03. Truncates or pads to exactly 12 characters. |
|
| 9B86 | .format_filename_field←2← 9970 JSR← 99FB JSR |
| LDY #0 ; Y=0: destination index | |
| 9B88 | LDX fs_cmd_csd ; Load source offset from l0f03 |
| 9B8B | BNE copy_from_buf_entry ; Non-zero: copy from l0f05 buffer |
| 9B8D | .loop_copy_cmdline_char←1← 9B97 BNE |
| LDA (fs_crc_lo),y ; Load character from command line | |
| 9B8F | CMP #&21 ; Below '!' (control/space)? |
| 9B91 | BCC pad_with_spaces ; Yes: pad with spaces |
| 9B93 | STA filename_buf,y ; Store printable character in l10f3 |
| 9B96 | INY ; Advance to next character |
| 9B97 | BNE loop_copy_cmdline_char ; Loop for more characters |
| 9B99 | .pad_with_spaces←2← 9B91 BCC← 9BA1 BCC |
| LDA #&20 ; A=' ': space for padding | |
| 9B9B | STA filename_buf,y ; Store space in display buffer |
| 9B9E | INY ; Advance index |
| 9B9F | CPY #&0c ; Filled all 12 characters? |
| 9BA1 | BCC pad_with_spaces ; No: pad more spaces |
| 9BA3 | RTS ; Return with field formatted |
| 9BA4 | .loop_copy_buf_char←1← 9BAC BPL |
| INX ; Advance source and destination | |
| 9BA5 | INY ; INY |
| 9BA6 | .copy_from_buf_entry←1← 9B8B BNE |
| LDA fs_cmd_data,x ; Load byte from l0f05 buffer | |
| 9BA9 | STA filename_buf,y ; Store in display buffer l10f3 |
| 9BAC | BPL loop_copy_buf_char ; Bit 7 clear: more characters |
| 9BAE | RTS ; Return (bit 7 set = terminator) |
| 9BAF | JSR verify_ws_checksum ; Verify workspace checksum |
| 9BB2 | STA fs_last_byte_flag ; Store result as last byte flag |
| 9BB4 | JSR set_options_ptr ; Set FS options pointer |
| 9BB7 | ORA #0 ; OR with 0 to set flags |
| 9BB9 | BPL dispatch_osfind_op ; Positive: handle sub-operations |
| 9BBB | ASL ; Shift left to check bit 6 |
| 9BBC | BEQ validate_chan_close ; Zero (was &80): close channel |
| 9BBE | JMP close_all_fcbs ; Other: process all FCBs first |
| 9BC1 | .validate_chan_close←1← 9BBC BEQ |
| TYA ; Transfer Y to A | |
| 9BC2 | CMP #&20 ; Compare with &20 (space) |
| 9BC4 | BCS check_chan_range ; Above &20: check further |
| 9BC6 | .error_invalid_chan←1← 9BCB BCS |
| JMP err_net_chan_invalid ; Below &20: invalid channel char | |
| 9BC9 | .check_chan_range←1← 9BC4 BCS |
| CMP #&30 ; Compare with '0' | |
| 9BCB | BCS error_invalid_chan ; Above '0': invalid channel char |
| 9BCD | JSR process_all_fcbs ; Process all matching FCBs |
| 9BD0 | TYA ; Transfer Y to A (FCB index) |
| 9BD1 | PHA ; Push FCB index |
| 9BD2 | TAX ; Copy to X |
| 9BD3 | LDY #0 ; Y=0: clear counter |
| 9BD5 | STY fs_last_byte_flag ; Clear last byte flag |
| 9BD7 | STY fs_block_offset ; Clear block offset |
| 9BD9 | .loop_copy_fcb_fields←1← 9BE4 BNE |
| LDA fcb_attr_or_count_mid,x ; Load channel data from l1010+X | |
| 9BDC | STA (fs_options),y ; Store in FS options at Y |
| 9BDE | JSR advance_x_by_8 ; Advance X by 8 (next FCB field) |
| 9BE1 | INY ; Advance destination index |
| 9BE2 | CPY #4 ; Copied all 4 channel fields? |
| 9BE4 | BNE loop_copy_fcb_fields ; No: copy next field |
| 9BE6 | PLA ; Pull saved FCB index |
| 9BE7 | STA fs_block_offset ; Restore to fs_block_offset |
| 9BE9 | .dispatch_osfind_op←1← 9BB9 BPL |
| CMP #5 ; Compare with 5 | |
| 9BEB | BCS done_return_flag ; 5 or above: return with last flag |
| 9BED | CPY #0 ; Compare Y with 0 |
| 9BEF | BNE osfind_with_channel ; Non-zero: handle OSFIND with channel |
| 9BF1 | JMP osfind_close_or_open ; Y=0 (close): jump to OSFIND open |
| 9BF4 | .osfind_with_channel←1← 9BEF BNE |
| PHA ; Push sub-function | |
| 9BF5 | TXA ; Transfer X to A |
| 9BF6 | PHA ; Push X (FCB slot) |
| 9BF7 | TYA ; Transfer Y to A |
| 9BF8 | PHA ; Push Y (channel char) |
| 9BF9 | JSR check_not_dir ; Check file is not a directory |
| 9BFC | PLA ; Pull channel char |
| 9BFD | LDY #&0e ; Y=&0E: error code offset |
| 9BFF | STA (net_rx_ptr),y ; Store channel char in RX buffer |
| 9C01 | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 |
| 9C04 | STA fs_cmd_data ; Store in l0f05 |
| 9C07 | PLA ; Pull X (FCB slot) |
| 9C08 | TAX ; Restore X |
| 9C09 | PLA ; Pull sub-function |
| 9C0A | LSR ; Shift right: check bit 0 |
| 9C0B | BEQ osargs_ptr_dispatch ; Zero (OSFIND close): handle close |
| 9C0D | PHP ; Save flags (carry from LSR) |
| 9C0E | PHA ; Push sub-function |
| 9C0F | LDX fs_options ; Load FS options pointer low |
| 9C11 | LDY fs_block_offset ; Load block offset |
| 9C13 | JSR process_all_fcbs ; Process all matching FCBs |
| 9C16 | LDA fcb_attr_or_count_mid,y ; Load updated data from l1010 |
| 9C19 | STA fs_cmd_data ; Store in l0f05 |
| 9C1C | PLA ; Pull sub-function |
| 9C1D | STA fs_func_code ; Store in l0f06 |
| 9C20 | PLP ; Restore flags |
| 9C21 | TYA ; Transfer Y to A |
| 9C22 | PHA ; Push Y (offset) |
| 9C23 | BCC osargs_read_op ; Carry clear: read operation |
| 9C25 | LDY #3 ; Y=3: copy 4 bytes |
| 9C27 | .loop_copy_zp_to_buf←1← 9C2E BPL |
| LDA zp_work_3,x ; Load zero page data | |
| 9C29 | STA fs_data_count,y ; Store in l0f07 buffer |
| 9C2C | DEX ; Decrement source |
| 9C2D | DEY ; Decrement counter |
| 9C2E | BPL loop_copy_zp_to_buf ; Loop for all 4 bytes |
| 9C30 | LDY #&0d ; Y=&0D: TX buffer size |
| 9C32 | LDX #5 ; X=5: argument offset |
| 9C34 | JSR save_net_tx_cb ; Send TX control block to server |
| 9C37 | STX fs_last_byte_flag ; Store X in last byte flag |
| 9C39 | PLA ; Pull saved offset |
| 9C3A | JSR set_conn_active ; Set connection active flag |
| 9C3D | .done_return_flag←1← 9BEB BCS |
| JMP return_with_last_flag ; Return with last flag | |
| 9C40 | .osargs_read_op←1← 9C23 BCC |
| LDY #&0c ; Y=&0C: TX buffer size (smaller) | |
| 9C42 | LDX #2 ; X=2: argument offset |
| 9C44 | JSR save_net_tx_cb ; Send TX control block |
| 9C47 | STA fs_last_byte_flag ; Store A in last byte flag |
| 9C49 | LDX fs_options ; Load FS options pointer low |
| 9C4B | LDY #2 ; Y=2: zero page offset |
| 9C4D | STA zp_work_3,x ; Store A in zero page |
| 9C4F | .loop_copy_reply_to_zp←1← 9C56 BPL |
| LDA fs_cmd_data,y ; Load buffer byte from l0f05+Y | |
| 9C52 | STA zp_work_2,x ; Store in zero page at offset |
| 9C54 | DEX ; Decrement source X |
| 9C55 | DEY ; Decrement counter Y |
| 9C56 | BPL loop_copy_reply_to_zp ; Loop until all bytes copied |
| 9C58 | PLA ; Pull saved offset |
| 9C59 | JMP return_with_last_flag ; Return with last flag |
| 9C5C | .osargs_ptr_dispatch←1← 9C0B BEQ |
| BCS osargs_write_ptr ; Carry set: write file pointer | |
| 9C5E | LDA fs_block_offset ; Load block offset |
| 9C60 | JSR attr_to_chan_index ; Convert attribute to channel index |
| 9C63 | LDY fs_options ; Load FS options pointer |
| 9C65 | LDA fcb_count_lo,x ; Load FCB low byte from l1000 |
| 9C68 | STA zp_ptr_lo,y ; Store in zero page pointer low |
| 9C6B | LDA fcb_attr_or_count_mid,x ; Load FCB high byte from l1010 |
| 9C6E | STA zp_ptr_hi,y ; Store in zero page pointer high |
| 9C71 | LDA fcb_station_or_count_hi,x ; Load FCB extent from l1020 |
| 9C74 | STA zp_work_2,y ; Store in zero page work area |
| 9C77 | LDA #0 ; A=0: clear high byte |
| 9C79 | STA zp_work_3,y ; Store zero in work area high |
| 9C7C | BEQ return_with_last_flag ; ALWAYS branch to return with flag ALWAYS branch |
| 9C7E | .osargs_write_ptr←1← 9C5C BCS |
| STA fs_func_code ; Store write value in l0f06 | |
| 9C81 | TXA ; Transfer X to A |
| 9C82 | PHA ; Push X (zero page offset) |
| 9C83 | LDY #3 ; Y=3: copy 4 bytes |
| 9C85 | .loop_copy_ptr_to_buf←1← 9C8C BPL |
| LDA zp_work_3,x ; Load zero page data at offset | |
| 9C87 | STA fs_data_count,y ; Store in l0f07 buffer |
| 9C8A | DEX ; Decrement source |
| 9C8B | DEY ; Decrement counter |
| 9C8C | BPL loop_copy_ptr_to_buf ; Loop for all 4 bytes |
| 9C8E | LDY #&0d ; Y=&0D: TX buffer size |
| 9C90 | LDX #5 ; X=5: argument offset |
| 9C92 | JSR save_net_tx_cb ; Send TX control block |
| 9C95 | STX fs_last_byte_flag ; Store X in last byte flag |
| 9C97 | PLA ; Pull saved zero page offset |
| 9C98 | TAY ; Transfer to Y |
| 9C99 | LDA fs_block_offset ; Load block offset (attribute) |
| 9C9B | JSR clear_conn_active ; Clear connection active flag |
| 9C9E | JSR attr_to_chan_index ; Convert attribute to channel index |
| 9CA1 | LDA zp_ptr_lo,y ; Load zero page pointer low |
| 9CA4 | STA fcb_count_lo,x ; Store back to FCB l1000 |
| 9CA7 | LDA zp_ptr_hi,y ; Load zero page pointer high |
| 9CAA | STA fcb_attr_or_count_mid,x ; Store back to FCB l1010 |
| 9CAD | LDA zp_work_2,y ; Load zero page work byte |
| 9CB0 | STA fcb_station_or_count_hi,x ; Store back to FCB l1020 |
| 9CB3 | JMP return_with_last_flag ; Return with last flag |
| 9CB6 | .close_all_fcbs←1← 9BBE JMP |
| JSR process_all_fcbs ; Process all matching FCBs first | |
| 9CB9 | .return_with_last_flag←12← 9935 JMP← 9A42 JMP← 9B37 JMP← 9C3D JMP← 9C59 JMP← 9C7C BEQ← 9CB3 JMP← 9DAF JMP← 9E36 JMP← A2DF JMP← A2E5 JMP← A399 JMP |
| LDA fs_last_byte_flag ; Load last byte flag | |
| 9CBB | .finalise_and_return←7← 9B80 JMP← 9B83 JMP← 9CD1 BNE← 9CE4 BPL← 9D7B JMP← 9EBD JMP← A1EE JMP |
| PHA ; Push result on stack | |
| 9CBC | LDA #0 ; A=0: clear error flag |
| 9CBE | LDY #&0e ; Y=&0E: error code offset |
| 9CC0 | STA (net_rx_ptr),y ; Clear error code in RX buffer |
| 9CC2 | PLA ; Pull result back |
| 9CC3 | LDX fs_options ; Restore X from FS options pointer |
| 9CC5 | LDY fs_block_offset ; Restore Y from block offset |
| 9CC7 | RTS ; Return to caller |
| 9CC8 | .osfind_close_or_open←1← 9BF1 JMP |
| CMP #2 ; Compare with 2 (open for output) | |
| 9CCA | BCS done_file_open ; 2 or above: handle file open |
| 9CCC | TAY ; Transfer to Y (Y=0 or 1) |
| 9CCD | BNE loop_copy_reply_data ; Non-zero (1 = read pointer): copy data |
| 9CCF | LDA #5 ; A=5: return code for close-all |
| 9CD1 | BNE finalise_and_return ; ALWAYS branch to finalise ALWAYS branch |
| 9CD3 | .loop_copy_reply_data←2← 9CCD BNE← 9CD9 BPL |
| LDA fs_cmd_context,y ; Load reply data byte at Y | |
| 9CD6 | STA (fs_options),y ; Store in FS options |
| 9CD8 | DEY ; Decrement index |
| 9CD9 | BPL loop_copy_reply_data ; Loop until all bytes copied |
| 9CDB | STY zp_work_2,x ; Clear zero page work low |
| 9CDD | STY zp_work_3,x ; Clear zero page work high |
| 9CDF | .done_file_open←1← 9CCA BCS |
| BEQ shift_and_finalise ; Z set: jump to clear A and return | |
| 9CE1 | .clear_result←2← 9CE8 BNE← 9FF4 JMP |
| LDA #0 ; A=0: clear result | |
| 9CE3 | .shift_and_finalise←1← 9CDF BEQ |
| LSR ; Shift right (always positive) | |
| 9CE4 | BPL finalise_and_return ; Positive: jump to finalise |
| 9CE6 | .alloc_fcb_for_open←1← 9D55 BEQ |
| AND #&3f ; Mask to 6-bit access value | |
| 9CE8 | BNE clear_result ; Non-zero: clear A and finalise |
| 9CEA | TXA ; Transfer X to A (options pointer) |
| 9CEB | JSR alloc_fcb_or_error ; Allocate FCB slot or raise error |
| 9CEE | EOR #&80 ; Toggle bit 7 |
| 9CF0 | ASL ; Shift left: build open mode |
| 9CF1 | STA fs_cmd_data ; Store open mode in l0f05 |
| 9CF4 | ROL ; Rotate to complete mode byte |
| 9CF5 | STA fs_func_code ; Store in l0f06 |
| 9CF8 | JSR parse_cmd_arg_y0 ; Parse command argument (Y=0) |
| 9CFB | LDX #2 ; X=2: buffer offset |
| 9CFD | JSR copy_arg_to_buf ; Copy argument to TX buffer |
| 9D00 | LDY #6 ; Y=6: open file command |
| 9D02 | BIT bit_test_ff ; Set V flag (skip directory check) |
| 9D05 | SEC ; Set carry |
| 9D06 | ROR escapable ; Rotate carry into escapable flag bit 7 |
| 9D08 | JSR save_net_tx_cb_vset ; Send open request with V set |
| 9D0B | BCS done_osfind ; Carry set (error): jump to finalise |
| 9D0D | LDA #&ff ; A=&FF: mark as newly opened |
| 9D0F | LDY #&0e ; Y=&0E: error code offset |
| 9D11 | STA (net_rx_ptr),y ; Store &FF as error flag in RX buffer |
| 9D13 | LDA fs_cmd_data ; Load handle from l0f05 |
| 9D16 | PHA ; Push handle |
| 9D17 | LDA #4 ; A=4: file info sub-command |
| 9D19 | STA fs_cmd_data ; Store sub-command |
| 9D1C | LDX #1 ; X=1: shift filename |
| 9D1E | .loop_shift_filename←1← 9D27 BNE |
| LDA fs_func_code,x ; Load filename byte from l0f06+X | |
| 9D21 | STA fs_cmd_data,x ; Shift down to l0f05+X |
| 9D24 | INX ; Advance source index |
| 9D25 | CMP #&0d ; Is it CR (end of filename)? |
| 9D27 | BNE loop_shift_filename ; No: continue shifting |
| 9D29 | LDY #&12 ; Y=&12: file info request |
| 9D2B | JSR save_net_tx_cb ; Send file info request |
| 9D2E | LDA fs_last_byte_flag ; Load last byte flag |
| 9D30 | AND #&bf ; Clear bit 6 (read/write bits) |
| 9D32 | ORA fs_cmd_data ; OR with reply access byte |
| 9D35 | ORA #1 ; Set bit 0 (file is open) |
| 9D37 | TAY ; Transfer to Y (access flags) |
| 9D38 | AND #2 ; Check bit 1 (write access) |
| 9D3A | BEQ check_open_mode ; No write access: check read-only |
| 9D3C | PLA ; Pull handle from stack |
| 9D3D | JSR alloc_fcb_slot ; Allocate FCB slot for channel |
| 9D40 | BNE store_fcb_flags ; Non-zero: FCB allocated, store flags |
| 9D42 | JSR verify_ws_checksum ; Verify workspace checksum |
| 9D45 | JSR set_xfer_params ; Set up transfer parameters |
| 9D48 | TAX ; Transfer A to X |
| 9D49 | JSR mask_owner_access ; Set owner-only access mask |
| 9D4C | TXA ; Transfer X back to A |
| 9D4D | BEQ close_all_channels ; Zero: close file, process FCBs |
| 9D4F | JSR save_ptr_to_os_text ; Save text pointer for OS |
| 9D52 | LDY cur_dir_handle ; Load current directory handle |
| 9D55 | BEQ alloc_fcb_for_open ; Zero: allocate new FCB |
| 9D57 | TYA ; Transfer Y to A |
| 9D58 | LDX #0 ; X=0: clear directory handle |
| 9D5A | STX cur_dir_handle ; Store zero (clear handle) |
| 9D5D | BEQ done_osfind ; ALWAYS branch to finalise ALWAYS branch |
| 9D5F | .check_open_mode←1← 9D3A BEQ |
| LDA fs_func_code ; Load access/open mode byte | |
| 9D62 | ROR ; Rotate right: check bit 0 |
| 9D63 | BCS alloc_fcb_with_flags ; Carry set (bit 0): check read permission |
| 9D65 | ROR ; Rotate right: check bit 1 |
| 9D66 | BCC alloc_fcb_with_flags ; Carry clear (no write): skip |
| 9D68 | BIT fs_data_count ; Test bit 7 of l0f07 (lock flag) |
| 9D6B | BPL alloc_fcb_with_flags ; Not locked: skip |
| 9D6D | TYA ; Transfer Y to A (flags) |
| 9D6E | ORA #&20 ; Set bit 5 (locked file flag) |
| 9D70 | TAY ; Transfer back to Y |
| 9D71 | .alloc_fcb_with_flags←3← 9D63 BCS← 9D66 BCC← 9D6B BPL |
| PLA ; Pull handle from stack | |
| 9D72 | JSR alloc_fcb_slot ; Allocate FCB slot for channel |
| 9D75 | .store_fcb_flags←1← 9D40 BNE |
| TAX ; Transfer to X | |
| 9D76 | TYA ; Transfer Y to A (flags) |
| 9D77 | STA fcb_flags,x ; Store flags in FCB table l1040 |
| 9D7A | TXA ; Transfer X back to A (handle) |
| 9D7B | .done_osfind←2← 9D0B BCS← 9D5D BEQ |
| JMP finalise_and_return ; Jump to finalise and return | |
| 9D7E | .close_all_channels←1← 9D4D BEQ |
| JSR process_all_fcbs ; Process all matching FCBs | |
| 9D81 | TYA ; Transfer Y to A |
| 9D82 | BNE close_specific_chan ; Non-zero channel: close specific |
| 9D84 | LDA fs_options ; Load FS options pointer low |
| 9D86 | PHA ; Push (save for restore) |
| 9D87 | LDA #osbyte_close_spool_exec ; A=&77: OSBYTE close spool/exec files |
| 9D89 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 9D8C | PLA ; Pull saved options pointer |
| 9D8D | STA fs_options ; Restore FS options pointer |
| 9D8F | LDA #0 ; A=0: clear flags |
| 9D91 | STA fs_last_byte_flag ; Clear last byte flag |
| 9D93 | STA fs_block_offset ; Clear block offset |
| 9D95 | BEQ send_close_request ; ALWAYS branch to send close request ALWAYS branch |
| 9D97 | .close_specific_chan←1← 9D82 BNE |
| JSR check_chan_char ; Validate channel character | |
| 9D9A | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 |
| 9D9D | .send_close_request←1← 9D95 BEQ |
| STA fs_cmd_data ; Store as l0f05 (file handle) | |
| 9DA0 | LDX #1 ; X=1: argument size |
| 9DA2 | LDY #7 ; Y=7: close file command |
| 9DA4 | JSR save_net_tx_cb ; Send close file request |
| 9DA7 | LDY fs_block_offset ; Load block offset |
| 9DA9 | BNE clear_single_fcb ; Non-zero: clear single FCB |
| 9DAB | CLV ; Clear V flag |
| 9DAC | JSR scan_fcb_flags ; Scan and clear all FCB flags |
| 9DAF | .done_close←3← 9DBA BEQ← 9DCC BCC← 9DE0 BPL |
| JMP return_with_last_flag ; Return with last flag | |
| 9DB2 | .clear_single_fcb←1← 9DA9 BNE |
| LDA #0 ; A=0: clear FCB entry | |
| 9DB4 | STA fcb_attr_or_count_mid,y ; Clear l1010 (FCB high byte) |
| 9DB7 | STA fcb_flags,y ; Clear l1040 (FCB flags) |
| 9DBA | BEQ done_close ; ALWAYS branch to return ALWAYS branch |
| 9DBC | .fscv_0_opt_entry |
| BEQ store_display_flag ; Z set: handle OSARGS 0 | |
| 9DBE | CPX #4 ; Compare X with 4 (number of args) |
| 9DC0 | BNE osargs_dispatch ; Not 4: check for error |
| 9DC2 | CPY #4 ; Compare Y with 4 |
| 9DC4 | BCC send_osargs_request ; Below 4: handle special OSARGS |
| 9DC6 | .osargs_dispatch←1← 9DC0 BNE |
| DEX ; Decrement X | |
| 9DC7 | BNE error_osargs ; X was 1: store display flag |
| 9DC9 | .store_display_flag←1← 9DBC BEQ |
| STY fs_messages_flag ; Store Y in display control flag l0e06 | |
| 9DCC | BCC done_close ; Carry clear: return with flag |
| 9DCE | .error_osargs←1← 9DC7 BNE |
| LDA #7 ; A=7: error code | |
| 9DD0 | JMP classify_reply_error ; Jump to classify reply error |
| 9DD3 | .send_osargs_request←1← 9DC4 BCC |
| STY fs_cmd_data ; Store Y in l0f05 | |
| 9DD6 | LDY #&16 ; Y=&16: OSARGS save command |
| 9DD8 | JSR save_net_tx_cb ; Send OSARGS request |
| 9DDB | LDY fs_block_offset ; Reload block offset |
| 9DDD | STY fs_boot_option ; Store in l0e05 |
| 9DE0 | BPL done_close ; Positive: return with flag |
| 9DE2 | .fscv_1_eof |
| JSR verify_ws_checksum ; Verify workspace checksum | |
| 9DE5 | PHA ; Push result on stack |
| 9DE6 | LDA fs_block_offset ; Load block offset |
| 9DE8 | PHA ; Push block offset |
| 9DE9 | STX cur_chan_attr ; Store X in l10c9 |
| 9DEC | JSR find_matching_fcb ; Find matching FCB entry |
| 9DEF | BEQ mark_not_found ; Zero: no match found |
| 9DF1 | LDA fcb_count_lo,y ; Load FCB low byte from l1000 |
| 9DF4 | CMP fcb_buf_offset,x ; Compare with stored offset l1098 |
| 9DF7 | BCC mark_not_found ; Below stored: no match |
| 9DF9 | LDX #&ff ; X=&FF: mark as found (all bits set) |
| 9DFB | BMI restore_and_return ; ALWAYS branch (negative) ALWAYS branch |
| 9DFD | .mark_not_found←2← 9DEF BEQ← 9DF7 BCC |
| LDX #0 ; X=0: mark as not found | |
| 9DFF | .restore_and_return←1← 9DFB BMI |
| PLA ; Restore block offset from stack | |
| 9E00 | TAY ; Transfer to Y |
| 9E01 | PLA ; Restore result from stack |
| 9E02 | RTS ; Return |
Update both address fields in FS optionsCalls add_workspace_to_fsopts for offset 9 (the high address / exec address field), then falls through to update_addr_from_offset1 to process offset 1 (the low address / load address field). |
|
| 9E03 | .update_addr_from_offset9←1← 9F48 JSR |
| LDY #9 ; Y=9: FS options offset for high address | |
| 9E05 | JSR add_workspace_to_fsopts ; Add workspace values to FS options |
| fall through ↓ | |
Update low address field in FS optionsSets Y=1 and falls through to add_workspace_to_fsopts to add the workspace adjustment bytes to the load address field at offset 1 in the FS options block.
|
||||
| 9E08 | .update_addr_from_offset1←1← A03B JSR | |||
| LDY #1 ; Y=1: FS options offset for low address | ||||
| fall through ↓ | ||||
Add workspace bytes to FS options with clear carryClears carry and falls through to adjust_fsopts_4bytes. Provides a convenient entry point when the caller needs addition without a preset carry.
|
||||
| 9E0A | .add_workspace_to_fsopts←1← 9E05 JSR | |||
| CLC ; Clear carry for addition | ||||
| fall through ↓ | ||||
Add or subtract 4 workspace bytes from FS optionsProcesses 4 consecutive bytes at (fs_options)+Y, adding or subtracting the corresponding workspace bytes from &0E0A-&0E0D. The direction is controlled by bit 7 of fs_load_addr_2: set for subtraction, clear for addition. Carry propagates across all 4 bytes for correct multi-byte arithmetic.
|
||||||
| 9E0B | .adjust_fsopts_4bytes←2← 9F4E JSR← A047 JSR | |||||
| LDX #&fc ; X=&FC: loop counter (-4 to -1) | ||||||
| 9E0D | .loop_adjust_byte←1← 9E20 BNE | |||||
| LDA (fs_options),y ; Load FS options byte at offset Y | ||||||
| 9E0F | BIT fs_load_addr_2 ; Test fs_load_addr_2 bit 7 (add/subtract) | |||||
| 9E11 | BMI subtract_ws_byte ; Bit 7 set: subtract instead | |||||
| 9E13 | ADC fs_cmd_context,x ; Add workspace byte to FS options | |||||
| 9E16 | JMP store_adjusted_byte ; Jump to store result | |||||
| 9E19 | .subtract_ws_byte←1← 9E11 BMI | |||||
| SBC fs_cmd_context,x ; Subtract workspace byte from FS options | ||||||
| 9E1C | .store_adjusted_byte←1← 9E16 JMP | |||||
| STA (fs_options),y ; Store result back to FS options | ||||||
| 9E1E | INY ; Advance to next byte | |||||
| 9E1F | INX ; Advance counter | |||||
| 9E20 | BNE loop_adjust_byte ; Loop until 4 bytes processed | |||||
| 9E22 | RTS ; Return | |||||
| 9E23 | JSR verify_ws_checksum ; Verify workspace checksum | |||||
| 9E26 | JSR set_xfer_params ; Set up transfer parameters | |||||
| 9E29 | PHA ; Push transfer type on stack | |||||
| 9E2A | JSR mask_owner_access ; Set owner-only access mask | |||||
| 9E2D | PLA ; Pull transfer type | |||||
| 9E2E | TAX ; Transfer to X | |||||
| 9E2F | BEQ skip_if_out_of_range ; Zero: no valid operation, return | |||||
| 9E31 | DEX ; Decrement (convert 1-based to 0-based) | |||||
| 9E32 | CPX #8 ; Compare with 8 (max operation) | |||||
| 9E34 | BCC valid_osgbpb_op ; Below 8: valid operation | |||||
| 9E36 | .skip_if_out_of_range←1← 9E2F BEQ | |||||
| JMP return_with_last_flag ; Out of range: return with flag | ||||||
| 9E39 | .valid_osgbpb_op←1← 9E34 BCC | |||||
| TXA ; Transfer operation code to A | ||||||
| 9E3A | LDY #0 ; Y=0: buffer offset | |||||
| 9E3C | PHA ; Push operation code | |||||
| 9E3D | CMP #4 ; Compare with 4 (write operations) | |||||
| 9E3F | BCC load_chan_handle ; Below 4: read operation | |||||
| 9E41 | JMP write_block_entry ; 4 or above: write data block | |||||
| 9E44 | .load_chan_handle←1← 9E3F BCC | |||||
| LDA (fs_options),y ; Load channel handle from FS options | ||||||
| 9E46 | PHA ; Push handle | |||||
| 9E47 | JSR check_not_dir ; Check file is not a directory | |||||
| 9E4A | PLA ; Pull handle | |||||
| 9E4B | TAY ; Transfer to Y | |||||
| 9E4C | JSR process_all_fcbs ; Process all matching FCBs | |||||
| 9E4F | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 | |||||
| 9E52 | STA fs_cmd_data ; Store file handle in l0f05 | |||||
| 9E55 | LDA #0 ; A=0: clear direction flag | |||||
| 9E57 | STA fs_func_code ; Store in l0f06 | |||||
| 9E5A | LDA fcb_count_lo,x ; Load FCB low byte (position) | |||||
| 9E5D | STA fs_data_count ; Store in l0f07 | |||||
| 9E60 | LDA fcb_attr_or_count_mid,x ; Load FCB high byte | |||||
| 9E63 | STA fs_reply_cmd ; Store in l0f08 | |||||
| 9E66 | LDA fcb_station_or_count_hi,x ; Load FCB extent byte | |||||
| 9E69 | STA fs_load_vector ; Store in l0f09 | |||||
| 9E6C | LDY #&0d ; Y=&0D: TX buffer size | |||||
| 9E6E | LDX #5 ; X=5: argument count | |||||
| 9E70 | JSR save_net_tx_cb ; Send TX control block to server | |||||
| 9E73 | PLA ; Pull operation code | |||||
| 9E74 | JSR setup_transfer_workspace ; Set up transfer workspace | |||||
| 9E77 | PHP ; Save flags (carry from setup) | |||||
| 9E78 | LDY #0 ; Y=0: index for channel handle | |||||
| 9E7A | LDA (fs_options),y ; Load channel handle from FS options | |||||
| 9E7C | BCS set_write_active ; Carry set (write): set active | |||||
| 9E7E | JSR clear_conn_active ; Read: clear connection active | |||||
| 9E81 | BPL setup_gbpb_request ; Branch to continue (always positive) | |||||
| 9E83 | .set_write_active←1← 9E7C BCS | |||||
| JSR set_conn_active ; Write: set connection active | ||||||
| 9E86 | .setup_gbpb_request←1← 9E81 BPL | |||||
| STY fs_func_code ; Clear l0f06 (Y=0) | ||||||
| 9E89 | JSR lookup_cat_slot_data ; Look up channel slot data | |||||
| 9E8C | STA fs_cmd_data ; Store flag byte in l0f05 | |||||
| 9E8F | LDY #&0c ; Y=&0C: TX buffer size (short) | |||||
| 9E91 | LDX #2 ; X=2: argument count | |||||
| 9E93 | JSR save_net_tx_cb ; Send TX control block | |||||
| 9E96 | JSR lookup_cat_entry_0 ; Look up channel entry at Y=0 | |||||
| 9E99 | LDY #9 ; Y=9: FS options offset for position | |||||
| 9E9B | LDA fs_cmd_data ; Load new position low from l0f05 | |||||
| 9E9E | STA fcb_count_lo,x ; Update FCB low byte in l1000 | |||||
| 9EA1 | STA (fs_options),y ; Store in FS options at Y=9 | |||||
| 9EA3 | INY ; Y=&0A Y=&0a | |||||
| 9EA4 | LDA fs_func_code ; Load new position high from l0f06 | |||||
| 9EA7 | STA fcb_attr_or_count_mid,x ; Update FCB high byte in l1010 | |||||
| 9EAA | STA (fs_options),y ; Store in FS options at Y=&0A | |||||
| 9EAC | INY ; Y=&0B Y=&0b | |||||
| 9EAD | LDA fs_data_count ; Load new extent from l0f07 | |||||
| 9EB0 | STA fcb_station_or_count_hi,x ; Update FCB extent in l1020 | |||||
| 9EB3 | STA (fs_options),y ; Store in FS options at Y=&0B | |||||
| 9EB5 | LDA #0 ; A=0: clear high byte of extent | |||||
| 9EB7 | INY ; Y=&0C Y=&0c | |||||
| 9EB8 | STA (fs_options),y ; Store zero in FS options at Y=&0C | |||||
| 9EBA | PLP ; Restore flags | |||||
| 9EBB | LDA #0 ; A=0: success | |||||
| 9EBD | JMP finalise_and_return ; Jump to finalise and return | |||||
Look up channel from FS options offset 0Loads the channel handle from (fs_options) at offset 0, then falls through to lookup_cat_slot_data to find the corresponding FCB entry.
|
||||||
| 9EC0 | .lookup_cat_entry_0←2← 9E96 JSR← 9ECC JSR | |||||
| LDY #0 ; Y=0: offset for channel handle | ||||||
| 9EC2 | LDA (fs_options),y ; Load channel handle from FS options | |||||
| fall through ↓ | ||||||
Look up channel and return FCB flag byteCalls lookup_chan_by_char to find the channel slot for handle A in the channel table, then loads the FCB flag byte from &1030+X.
|
|||||||||
| 9EC4 | .lookup_cat_slot_data←1← 9E89 JSR | ||||||||
| JSR lookup_chan_by_char ; Look up channel by character | |||||||||
| 9EC7 | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 | ||||||||
| 9ECA | RTS ; Return with flag in A | ||||||||
Prepare workspace for OSGBPB data transferOrchestrates the setup for OSGBPB (get/put multiple bytes) operations. Looks up the channel, copies the 6-byte address structure from FS options (skipping the hole at offset 8), determines transfer direction from the operation code (even=read, odd=write), selects port &91 or &92 accordingly, and sends the FS request. Then configures the TXCB address pairs for the actual data transfer phase and dispatches to the appropriate handler. |
|
| 9ECB | .setup_transfer_workspace←2← 9E74 JSR← B97C JMP |
| PHA ; Push operation code on stack | |
| 9ECC | JSR lookup_cat_entry_0 ; Look up channel entry at Y=0 |
| 9ECF | STA fs_cmd_data ; Store flag byte in l0f05 |
| 9ED2 | LDY #&0b ; Y=&0B: source offset in FS options |
| 9ED4 | LDX #6 ; X=6: copy 6 bytes |
| 9ED6 | .loop_copy_opts_to_buf←1← 9EE2 BNE |
| LDA (fs_options),y ; Load FS options byte | |
| 9ED8 | STA fs_func_code,x ; Store in l0f06 buffer |
| 9EDB | DEY ; Decrement source index |
| 9EDC | CPY #8 ; Skip offset 8? |
| 9EDE | BNE skip_struct_hole ; No: continue copy |
| 9EE0 | DEY ; Skip offset 8 (hole in structure) |
| 9EE1 | .skip_struct_hole←1← 9EDE BNE |
| DEX ; Decrement destination counter | |
| 9EE2 | BNE loop_copy_opts_to_buf ; Loop until all 6 bytes copied |
| 9EE4 | PLA ; Pull operation code |
| 9EE5 | LSR ; Shift right: check bit 0 (direction) |
| 9EE6 | PHA ; Push updated code |
| 9EE7 | BCC store_direction_flag ; Carry clear: OSBGET (read) |
| 9EE9 | INX ; Carry set: OSBPUT (write), X=1 |
| 9EEA | .store_direction_flag←1← 9EE7 BCC |
| STX fs_func_code ; Store direction flag in l0f06 | |
| 9EED | LDY #&0b ; Y=&0B: TX buffer size |
| 9EEF | LDX #&91 ; X=&91: port for OSBGET |
| 9EF1 | PLA ; Pull operation code |
| 9EF2 | PHA ; Push back (keep on stack) |
| 9EF3 | BEQ store_port_and_send ; Zero (OSBGET): keep port &91 |
| 9EF5 | LDX #&92 ; X=&92: port for OSBPUT |
| 9EF7 | DEY ; Y=&0A: adjusted buffer size Y=&0a |
| 9EF8 | .store_port_and_send←1← 9EF3 BEQ |
| STX fs_cmd_urd ; Store port in l0f02 | |
| 9EFB | STX fs_error_ptr ; Store port in fs_error_ptr |
| 9EFD | LDX #8 ; X=8: argument count |
| 9EFF | LDA fs_cmd_data ; Load file handle from l0f05 |
| 9F02 | JSR send_request_nowrite ; Send request (no write data) |
| 9F05 | LDX #0 ; X=0: index |
| 9F07 | LDA (fs_options,x) ; Load channel handle from FS options |
| 9F09 | TAX ; Transfer to X as index |
| 9F0A | LDA fcb_flags,x ; Load FCB flags from l1040 |
| 9F0D | EOR #1 ; Toggle bit 0 (transfer direction) |
| 9F0F | STA fcb_flags,x ; Store updated flags |
| 9F12 | CLC ; Clear carry for addition |
| 9F13 | LDX #4 ; X=4: process 4 address bytes |
| 9F15 | .loop_setup_addr_bytes←1← 9F29 BNE |
| LDA (fs_options),y ; Load FS options address byte | |
| 9F17 | STA addr_work,y ; Store in zero page address area |
| 9F1A | STA txcb_pos,y ; Store in TXCB position |
| 9F1D | JSR advance_y_by_4 ; Advance Y by 4 |
| 9F20 | ADC (fs_options),y ; Add offset from FS options |
| 9F22 | STA addr_work,y ; Store computed end address |
| 9F25 | JSR retreat_y_by_3 ; Retreat Y by 3 for next pair |
| 9F28 | DEX ; Decrement byte counter |
| 9F29 | BNE loop_setup_addr_bytes ; Loop for all 4 address bytes |
| 9F2B | INX ; X=1 (INX from 0) |
| 9F2C | .loop_copy_offset←1← 9F33 BPL |
| LDA fs_cmd_csd,x ; Load offset from l0f03 | |
| 9F2F | STA fs_func_code,x ; Copy to l0f06 |
| 9F32 | DEX ; Decrement counter |
| 9F33 | BPL loop_copy_offset ; Loop until both bytes copied |
| 9F35 | PLA ; Pull operation code |
| 9F36 | BNE send_with_swap ; Non-zero (OSBPUT): swap addresses |
| 9F38 | LDA fs_cmd_urd ; Load port from l0f02 |
| 9F3B | JSR check_and_setup_txcb ; Check and set up TXCB |
| 9F3E | BCS recv_and_update ; Carry set: skip swap |
| 9F40 | .send_with_swap←1← 9F36 BNE |
| JSR send_txcb_swap_addrs ; Send TXCB and swap start/end addresses | |
| 9F43 | .recv_and_update←1← 9F3E BCS |
| JSR recv_and_process_reply ; Receive and process reply | |
| 9F46 | STX fs_load_addr_2 ; Store result in fs_load_addr_2 |
| 9F48 | JSR update_addr_from_offset9 ; Update addresses from offset 9 |
| 9F4B | DEC fs_load_addr_2 ; Decrement fs_load_addr_2 |
| 9F4D | SEC ; Set carry for subtraction |
| 9F4E | JSR adjust_fsopts_4bytes ; Adjust FS options by 4 bytes |
| 9F51 | ASL fs_cmd_data ; Shift l0f05 left (update status) |
| 9F54 | RTS ; Return |
| 9F55 | .send_osbput_data←1← 9F85 BEQ |
| LDY #&15 ; Y=&15: TX buffer size for OSBPUT data | |
| 9F57 | JSR save_net_tx_cb ; Send TX control block |
| 9F5A | LDA fs_boot_option ; Load display flag from l0e05 |
| 9F5D | STA fs_boot_data ; Store in l0f16 |
| 9F60 | STX fs_load_addr ; Clear fs_load_addr (X=0) |
| 9F62 | STX fs_load_addr_hi ; Clear fs_load_addr_hi |
| 9F64 | LDA #&12 ; A=&12: byte count for data block |
| 9F66 | STA fs_load_addr_2 ; Store in fs_load_addr_2 |
| 9F68 | BNE write_data_block ; ALWAYS branch to write data block ALWAYS branch |
| 9F6A | .write_block_entry←1← 9E41 JMP |
| LDY #4 ; Y=4: offset for station comparison | |
| 9F6C | LDA tube_present ; Load stored station from l0d63 |
| 9F6F | BEQ store_station_result ; Zero: skip station check |
| 9F71 | CMP (fs_options),y ; Compare with FS options station |
| 9F73 | BNE store_station_result ; Mismatch: skip subtraction |
| 9F75 | DEY ; Y=3 Y=&03 |
| 9F76 | SBC (fs_options),y ; Subtract FS options value |
| 9F78 | .store_station_result←2← 9F6F BEQ← 9F73 BNE |
| STA svc_state ; Store result in svc_state | |
| 9F7A | .loop_copy_opts_to_ws←1← 9F80 BNE |
| LDA (fs_options),y ; Load FS options byte at Y | |
| 9F7C | STA fs_last_byte_flag,y ; Store in workspace at fs_last_byte_flag+Y |
| 9F7F | DEY ; Decrement index |
| 9F80 | BNE loop_copy_opts_to_ws ; Loop until all bytes copied |
| 9F82 | PLA ; Pull operation code |
| 9F83 | AND #3 ; Mask to 2-bit sub-operation |
| 9F85 | BEQ send_osbput_data ; Zero: send OSBPUT data |
| 9F87 | LSR ; Shift right: check bit 0 |
| 9F88 | BEQ handle_cat_update ; Zero (bit 0 clear): handle read |
| 9F8A | BCS update_cat_position ; Carry set: handle catalogue update |
| 9F8C | .handle_cat_update←1← 9F88 BEQ |
| TAY ; Transfer to Y (Y=0) | |
| 9F8D | LDA fs_csd_handle,y ; Load data byte from l0e03 |
| 9F90 | STA fs_cmd_csd ; Store in l0f03 |
| 9F93 | LDA fs_lib_handle ; Load high data byte from l0e04 |
| 9F96 | STA fs_cmd_lib ; Store in l0f04 |
| 9F99 | LDA fs_urd_handle ; Load port from l0e02 |
| 9F9C | STA fs_cmd_urd ; Store in l0f02 |
| 9F9F | LDX #&12 ; X=&12: buffer size marker |
| 9FA1 | STX fs_cmd_y_param ; Store in l0f01 |
| 9FA4 | LDA #&0d ; A=&0D: count value |
| 9FA6 | STA fs_func_code ; Store in l0f06 |
| 9FA9 | STA fs_load_addr_2 ; Store in fs_load_addr_2 |
| 9FAB | LSR ; Shift right (A=6) |
| 9FAC | STA fs_cmd_data ; Store in l0f05 |
| 9FAF | CLC ; Clear carry for addition |
| 9FB0 | JSR prep_send_tx_cb ; Prepare and send TX control block |
| 9FB3 | STX fs_load_addr_hi ; Store X in fs_load_addr_hi (X=0) |
| 9FB5 | INX ; X=1 (INX) |
| 9FB6 | STX fs_load_addr ; Store X in fs_load_addr |
| fall through ↓ | |
Write data block to destination or TubeIf no Tube present, copies directly from the l0f05 buffer via (fs_crc_lo). If Tube is active, claims the Tube, sets up the transfer address, and writes via R3. |
|
| 9FB8 | .write_data_block←2← 9F68 BNE← A030 JSR |
| LDA svc_state ; Load svc_state (tube flag) | |
| 9FBA | BNE tube_write_setup ; Non-zero: write via tube |
| 9FBC | LDX fs_load_addr ; Load source index from fs_load_addr |
| 9FBE | LDY fs_load_addr_hi ; Load destination index from fs_load_addr_hi |
| 9FC0 | .loop_copy_to_host←1← 9FC9 BNE |
| LDA fs_cmd_data,x ; Load data byte from l0f05 buffer | |
| 9FC3 | STA (fs_crc_lo),y ; Store to destination via fs_crc pointer |
| 9FC5 | INX ; Advance source index |
| 9FC6 | INY ; Advance destination index |
| 9FC7 | DEC fs_load_addr_2 ; Decrement byte counter |
| 9FC9 | BNE loop_copy_to_host ; Loop until all bytes transferred |
| 9FCB | BEQ tail_update_catalogue ; ALWAYS branch to update catalogue ALWAYS branch |
| 9FCD | .tube_write_setup←1← 9FBA BNE |
| JSR tube_claim_c3 ; Claim tube with call &C3 | |
| 9FD0 | LDA #1 ; A=1: tube transfer type (write) |
| 9FD2 | LDX fs_options ; Load destination low from fs_options |
| 9FD4 | LDY fs_block_offset ; Load destination high from fs_block_offset |
| 9FD6 | INX ; Increment low byte |
| 9FD7 | BNE set_tube_addr ; No wrap: skip high increment |
| 9FD9 | INY ; Carry: increment high byte |
| 9FDA | .set_tube_addr←1← 9FD7 BNE |
| JSR tube_addr_data_dispatch ; Set up tube transfer address | |
| 9FDD | LDX fs_load_addr ; Load source index |
| 9FDF | .loop_write_to_tube←1← 9FED BNE |
| LDA fs_cmd_data,x ; Load data byte from buffer | |
| 9FE2 | STA tube_data_register_3 ; Write to tube data register 3 |
| 9FE5 | INX ; Advance source index |
| 9FE6 | LDY #6 ; Y=6: tube write delay |
| 9FE8 | .loop_tube_delay←1← 9FE9 BNE |
| DEY ; Delay loop: decrement Y | |
| 9FE9 | BNE loop_tube_delay ; Loop until delay complete |
| 9FEB | DEC fs_load_addr_2 ; Decrement byte counter |
| 9FED | BNE loop_write_to_tube ; Loop until all bytes written to tube |
| 9FEF | LDA #&83 ; A=&83: release tube claim |
| 9FF1 | JSR tube_addr_data_dispatch ; Release tube |
| 9FF4 | .tail_update_catalogue←2← 9FCB BEQ← A058 JMP |
| JMP clear_result ; Jump to clear A and finalise return | |
| 9FF7 | .update_cat_position←1← 9F8A BCS |
| LDY #9 ; Y=9: offset for position byte | |
| 9FF9 | LDA (fs_options),y ; Load position from FS options |
| 9FFB | STA fs_func_code ; Store in l0f06 |
| 9FFE | LDY #5 ; Y=5: offset for extent byte |
| A000 | LDA (fs_options),y ; Load extent byte from FS options |
| A002 | STA fs_data_count ; Store in l0f07 |
| A005 | LDX #&0d ; X=&0D: byte count |
| A007 | STX fs_reply_cmd ; Store in l0f08 |
| A00A | LDY #2 ; Y=2: command sub-type |
| A00C | STY fs_load_addr ; Store in fs_load_addr |
| A00E | STY fs_cmd_data ; Store in l0f05 |
| A011 | INY ; Y=3: TX buffer command byte Y=&03 |
| A012 | JSR save_net_tx_cb ; Send TX control block |
| A015 | STX fs_load_addr_hi ; Store X (0) in fs_load_addr_hi |
| A017 | LDA fs_func_code ; Load data offset from l0f06 |
| A01A | STA (fs_options,x) ; Store as first byte of FS options |
| A01C | LDA fs_cmd_data ; Load data count from l0f05 |
| A01F | LDY #9 ; Y=9: position offset in FS options |
| A021 | ADC (fs_options),y ; Add to current position |
| A023 | STA (fs_options),y ; Store updated position |
| A025 | LDA txcb_end ; Load TXCB end byte |
| A027 | SBC #7 ; Subtract 7 (header overhead) |
| A029 | STA fs_func_code ; Store remaining data size |
| A02C | STA fs_load_addr_2 ; Store in fs_load_addr_2 (byte count) |
| A02E | BEQ clear_buf_after_write ; Zero bytes: skip write |
| A030 | JSR write_data_block ; Write data block to host/tube |
| A033 | .clear_buf_after_write←1← A02E BEQ |
| LDX #2 ; X=2: clear 3 bytes (indices 0-2) | |
| A035 | .loop_clear_buf←1← A039 BPL |
| STA fs_data_count,x ; Clear l0f07+X | |
| A038 | DEX ; Decrement index |
| A039 | BPL loop_clear_buf ; Loop until all cleared |
| A03B | JSR update_addr_from_offset1 ; Update addresses from offset 1 |
| A03E | SEC ; Set carry for subtraction |
| A03F | DEC fs_load_addr_2 ; Decrement fs_load_addr_2 |
| A041 | LDA fs_cmd_data ; Load data count from l0f05 |
| A044 | STA fs_func_code ; Copy to l0f06 |
| A047 | JSR adjust_fsopts_4bytes ; Adjust FS options by 4 bytes (subtract) |
| A04A | LDX #3 ; X=3: check 4 bytes |
| A04C | LDY #5 ; Y=5: starting offset |
| A04E | SEC ; Set carry for comparison |
| A04F | .loop_check_remaining←1← A055 BPL |
| LDA (fs_options),y ; Load FS options byte | |
| A051 | BNE done_write_block ; Non-zero: more data remaining |
| A053 | INY ; Advance to next byte |
| A054 | DEX ; Decrement counter |
| A055 | BPL loop_check_remaining ; Loop until all bytes checked |
| A057 | CLC ; All zero: clear carry (transfer complete) |
| A058 | .done_write_block←1← A051 BNE |
| JMP tail_update_catalogue ; Jump to update catalogue and return | |
Claim the Tube via protocol &C3Loops calling tube_addr_data_dispatch with protocol byte &C3 until the claim succeeds (carry set on return). Used before Tube data transfers to ensure exclusive access to the Tube co-processor interface. |
|
| A05B | .tube_claim_c3←3← 9FCD JSR← A060 BCC← A2CB JSR |
| LDA #&c3 ; A=&C3: tube claim protocol | |
| A05D | JSR tube_addr_data_dispatch ; Dispatch tube address/data claim |
| A060 | BCC tube_claim_c3 ; Carry clear: claim failed, retry |
| A062 | RTS ; Return (tube claimed) |
*FS command handlerSaves the current file server station address, then checks for a command-line argument. With no argument, falls through to print_current_fs to display the active server. With an argument, parses the station number via parse_fs_ps_args and issues OSWORD &13 (sub-function 1) to select the new file server.
|
||||
| A063 | .cmd_fs | |||
| LDA fs_server_stn ; Load current FS station high | ||||
| A066 | STA fs_work_5 ; Save to fs_work_5 | |||
| A068 | LDA fs_server_net ; Load current FS station low | |||
| A06B | STA fs_work_6 ; Save to l00b6 | |||
| A06D | LDA (fs_crc_lo),y ; Get first character of argument | |||
| A06F | CMP #&0d ; Is it CR (no argument)? | |||
| A071 | BEQ print_current_fs ; No arg: print current FS info | |||
| A073 | JSR parse_fs_ps_args ; Parse FS/PS station arguments | |||
| A076 | LDA #1 ; A=1: write NFS info | |||
| A078 | STA fs_work_4 ; Store OSWORD sub-function | |||
| A07A | LDA #&13 ; OSWORD &13: NFS information | |||
| A07C | LDX #<(fs_work_4) ; Parameter block low | |||
| A07E | LDY #>(fs_work_4) ; Parameter block high | |||
| A080 | JMP osword ; Read/Write NFS information (see https://beebwiki.mdfs.net/OSWORDs) | |||
| A083 | .print_current_fs←1← A071 BEQ | |||
| JSR print_file_server_is ; Print 'File server ' | ||||
| fall through ↓ | ||||
Print station address and newlineSets V (suppressing leading-zero padding on the network number) then prints the station address followed by a newline via OSNEWL. Used by *FS and *PS output formatting. |
|
| A086 | .print_fs_info_newline←1← B092 JSR |
| BIT bit_test_ff ; Set V (suppress padding) | |
| A089 | JSR print_station_addr ; Print station address |
| A08C | JMP osnewl ; Write newline (characters 10 and 13) |
Parse station address from *FS/*PS argumentsReads a station address in 'net.station' format from the command line, with the network number optional (defaults to local network). Calls init_bridge_poll to ensure the bridge routing table is populated, then validates the parsed address against known stations. |
|
| A08F | .parse_fs_ps_args←3← A073 JSR← AFF1 JSR← B1C4 JSR |
| TXA ; Save X on stack | |
| A090 | PHA ; Push X |
| A091 | LDA #0 ; A=0: initialise dot-seen flag |
| A093 | STA fs_work_4 ; Clear dot-seen flag |
| A095 | JSR parse_addr_arg ; Parse first number (network) |
| A098 | BCS skip_if_no_station ; C set: number found, check for dot |
| A09A | TYA ; Save Y (command line offset) |
| A09B | PHA ; Push Y |
| A09C | JSR init_bridge_poll ; Initialise bridge polling |
| A09F | EOR fs_load_addr_2 ; Compare bridge result with parsed value |
| A0A1 | BEQ store_station_lo ; Same: keep bridge result |
| A0A3 | LDA fs_load_addr_2 ; Different: use parsed value |
| A0A5 | .store_station_lo←1← A0A1 BEQ |
| STA fs_work_6 ; Store station low byte | |
| A0A7 | PLA ; Restore Y |
| A0A8 | TAY ; Transfer back to Y |
| A0A9 | INY ; Skip dot separator |
| A0AA | JSR parse_addr_arg ; Parse second number (station) |
| A0AD | .skip_if_no_station←1← A098 BCS |
| BEQ done_parse_fs_ps ; Zero result: skip store | |
| A0AF | STA fs_work_5 ; Store station high byte |
| A0B1 | .done_parse_fs_ps←1← A0AD BEQ |
| PLA ; Restore X | |
| A0B2 | TAX ; Transfer back to X |
| A0B3 | RTS ; Return |
Convert parameter block pointer to table indexReads the first byte from the OSWORD parameter block pointer and falls through to byte_to_2bit_index to produce a 12-byte-aligned table index in Y. |
|
| A0B4 | .get_pb_ptr_as_index←2← A0D2 JSR← A0E2 JSR |
| LDA osword_pb_ptr ; Load parameter block pointer | |
| fall through ↓ | |
Convert byte to 12-byte-aligned table indexComputes Y = A * 6 (via A*12/2) for indexing into the OSWORD handler workspace tables. Clamps Y to zero if the result exceeds &48, preventing out-of-bounds access.
|
|||||||
| A0B6 | .byte_to_2bit_index←4← 8F13 JSR← A5B9 JSR← A5D2 JSR← B0E4 JSR | ||||||
| ASL ; Shift left (A * 2) | |||||||
| A0B7 | ASL ; Shift left (A * 4) | ||||||
| A0B8 | PHA ; Save A * 4 on stack | ||||||
| A0B9 | ASL ; Shift left (A * 8) | ||||||
| A0BA | TSX ; Get stack pointer | ||||||
| A0BB | PHP ; Save flags (carry from shift) | ||||||
| A0BC | ADC error_text,x ; A*8 + A*4 (from stack) = A*12 | ||||||
| A0BF | ROR ; Divide by 2 with carry | ||||||
| A0C0 | PLP ; Restore original flags | ||||||
| A0C1 | ASL ; Shift left again | ||||||
| A0C2 | TAY ; Result to Y as index | ||||||
| A0C3 | PLA ; Pop saved A * 4 | ||||||
| A0C4 | CMP #&48 ; A * 4 >= &48 (out of range)? | ||||||
| A0C6 | BCC return_from_2bit_index ; In range: return | ||||||
| A0C8 | LDY #0 ; Out of range: Y=0 | ||||||
| A0CA | TYA ; A=&00 | ||||||
| A0CB | .return_from_2bit_index←1← A0C6 BCC | ||||||
| RTS ; Return with A=index, Y=index | |||||||
| A0CC | .net_1_read_handle | ||||||
| LDY #&6f ; Y=&6F: source offset | |||||||
| A0CE | LDA (net_rx_ptr),y ; Load byte from RX buffer | ||||||
| A0D0 | BCC store_pb_result ; C clear: store directly | ||||||
| A0D2 | .net_2_read_handle_entry | ||||||
| JSR get_pb_ptr_as_index ; Get index from PB pointer | |||||||
| A0D5 | BCS return_zero_uninit ; C set (out of range): clear value | ||||||
| A0D7 | LDA (nfs_workspace),y ; Load workspace byte at index | ||||||
| A0D9 | CMP #&3f ; Is it '?' (uninitialised)? | ||||||
| A0DB | BNE store_pb_result ; No: use value from RX buffer | ||||||
| A0DD | .return_zero_uninit←1← A0D5 BCS | ||||||
| LDA #0 ; A=0: return zero for uninitialised | |||||||
| A0DF | .store_pb_result←2← A0D0 BCC← A0DB BNE | ||||||
| STA osword_pb_ptr ; Store result to PB pointer | |||||||
| A0E1 | RTS ; Return | ||||||
| A0E2 | .net_3_close_handle | ||||||
| JSR get_pb_ptr_as_index ; Get index from PB pointer | |||||||
| A0E5 | BCC mark_ws_uninit ; C clear: store to workspace | ||||||
| A0E7 | ROR fs_flags ; Save carry to l0d6c bit 7 | ||||||
| A0EA | LDA osword_pb_ptr ; Load PB pointer value | ||||||
| A0EC | ROL ; Shift carry back in | ||||||
| A0ED | ROL fs_flags ; Restore l0d6c bit 7 | ||||||
| A0F0 | RTS ; Return | ||||||
| A0F1 | .mark_ws_uninit←1← A0E5 BCC | ||||||
| ROR econet_flags ; Save carry to l0d61 bit 7 | |||||||
| A0F4 | LDA #&3f ; A='?': mark as uninitialised | ||||||
| A0F6 | STA (nfs_workspace),y ; Store '?' to workspace | ||||||
| A0F8 | ROL econet_flags ; Restore l0d61 bit 7 | ||||||
| A0FB | RTS ; Return | ||||||
| A0FC | .fscv_3_star_cmd←1← 8CEE JMP | ||||||
| JSR set_text_and_xfer_ptr ; Set text and transfer pointers | |||||||
| A0FF | LDY #&ff ; Y=&FF: prepare for INY to 0 | ||||||
| A101 | STY fs_spool_handle ; Clear spool handle (no spool active) | ||||||
| A103 | STY escapable ; Set escapable flag (&FF) | ||||||
| A105 | INY ; Y=&00 | ||||||
| A106 | LDX #&4a ; X=&4A: FS command table offset | ||||||
| A108 | JSR match_fs_cmd ; Match command in FS table | ||||||
| A10B | BCS dispatch_fs_cmd ; C set: command found | ||||||
| A10D | .cmd_fs_reentry←1← 8C4F JMP | ||||||
| BVC dispatch_fs_cmd ; V clear: syntax error | |||||||
| A10F | .error_syntax←1← AF4F JMP | ||||||
| LDA #&dc ; Error code &DC | |||||||
| A111 | JSR error_inline ; Generate 'Syntax' error | ||||||
| A114 | EQUS "Syntax." | ||||||
| A11B | .dispatch_fs_cmd←2← A10B BCS← A10D BVC | ||||||
| LDA #0 ; A=0: clear service state | |||||||
| A11D | STA svc_state ; Store cleared service state | ||||||
| A11F | LDA cmd_table_fs_hi,x ; Load command handler address high | ||||||
| A122 | PHA ; Push high byte | ||||||
| A123 | LDA cmd_table_fs_lo,x ; Load command handler address low | ||||||
| A126 | PHA ; Push low byte | ||||||
| A127 | RTS ; RTS dispatches to command handler | ||||||
Match command name against FS command tableCase-insensitive compare of the command line against table entries with bit-7-terminated names. Returns with the matched entry address on success. |
|
| A128 | .match_fs_cmd←5← 8C4A JSR← 8C78 JSR← A108 JSR← B304 JSR← B331 JSR |
| TYA ; Save Y (command line offset) | |
| A129 | PHA ; Push on stack |
| A12A | .restart_table_scan←1← A150 BNE |
| PLA ; Restore saved Y | |
| A12B | PHA ; Push back (keep on stack) |
| A12C | TAY ; Transfer to Y |
| A12D | LDA cmd_table_fs,x ; Load table entry byte |
| A130 | BMI check_char_type ; Bit 7 set: end of table names |
| A132 | .loop_match_char←1← A13F BNE |
| LDA cmd_table_fs,x ; Load table byte | |
| A135 | BMI check_separator ; Bit 7 set: end of this name |
| A137 | EOR (fs_crc_lo),y ; Compare with command line char |
| A139 | AND #&df ; Case-insensitive compare |
| A13B | BNE skip_entry_chars ; Mismatch: skip to next entry |
| A13D | INY ; Match: advance command line |
| A13E | INX ; Advance table pointer |
| A13F | BNE loop_match_char ; Loop for next character |
| A141 | .skip_entry_chars←2← A13B BNE← A145 BPL |
| INX ; Advance past remaining table chars | |
| A142 | LDA cmd_table_fs,x ; Load next table byte |
| A145 | BPL skip_entry_chars ; Bit 7 clear: more chars to skip |
| A147 | LDA (fs_crc_lo),y ; Check command line terminator |
| A149 | CMP #&2e ; Is it '.' (abbreviation)? |
| A14B | BEQ skip_dot_and_spaces ; Yes: skip spaces after dot |
| A14D | .loop_skip_to_next←1← A162 BNE |
| INX ; X += 3: skip flags and address bytes | |
| A14E | INX ; (continued) |
| A14F | INX ; (continued) |
| A150 | BNE restart_table_scan ; Try next table entry |
| A152 | .check_separator←1← A135 BMI |
| TYA ; Save Y (end of matched name) | |
| A153 | PHA ; Push position |
| A154 | LDA (fs_crc_lo),y ; Load char after matched portion |
| A156 | LDY #9 ; Y=9: check 10 separator chars |
| A158 | .loop_check_sep_table←1← A15E BPL |
| CMP sep_table_data,y ; Compare with separator table | |
| A15B | BEQ separator_matched ; Match: valid command separator |
| A15D | DEY ; Try next separator |
| A15E | BPL loop_check_sep_table ; Loop through separator list |
| A160 | PLA ; No separator match: restore Y |
| A161 | TAY ; Transfer back to Y |
| A162 | BNE loop_skip_to_next ; Try next table entry |
| ; Command separator table (9 bytes) | |
| ; Characters that terminate a command name in the | |
| ; star command parser. loop_check_sep_table scans | |
| ; Y down from 8 to 0, comparing each input char | |
| ; against this table. | |
| A164 | .sep_table_data←1← A158 CMP |
| EQUB &20 ; Space | |
| A165 | EQUB &22 ; '"' double quote |
| A166 | EQUB &23 ; '#' hash |
| A167 | EQUB &24 ; '$' dollar |
| A168 | EQUB &26 ; '&' ampersand |
| A169 | EQUB &2A ; '*' asterisk |
| A16A | EQUB &3A ; ':' colon |
| A16B | EQUB &40 ; '@' at-sign |
| A16C | EQUB &0D ; CR (carriage return) |
| A16D | .separator_matched←1← A15B BEQ |
| PLA ; Restore saved Y | |
| A16E | TAY ; Transfer to Y |
| A16F | .loop_skip_trail_spaces←1← A176 JMP |
| LDA (fs_crc_lo),y ; Load next char | |
| A171 | CMP #&20 ; Is it space? |
| A173 | BNE check_cmd_flags ; No: done skipping |
| A175 | .skip_dot_and_spaces←1← A14B BEQ |
| INY ; Advance past space | |
| A176 | JMP loop_skip_trail_spaces ; Loop for more spaces |
| A179 | .check_cmd_flags←1← A173 BNE |
| LDA cmd_table_fs,x ; Load command flags byte | |
| A17C | ASL ; Shift: check 'no-arg' bit |
| A17D | BPL clear_v_flag ; Bit clear: allow arguments |
| A17F | LDA (fs_crc_lo),y ; Check if line ends here |
| A181 | CMP #&0d ; Is it CR? |
| A183 | BNE clear_v_flag ; No: argument present, V clear |
| A185 | BIT bit_test_ff ; CR found: set V (no argument) |
| A188 | BVS clear_c_flag ; V set: command is valid |
| A18A | .clear_v_flag←2← A17D BPL← A183 BNE |
| CLV ; Clear V (argument present) | |
| A18B | .clear_c_flag←1← A188 BVS |
| CLC ; C=0: command not found | |
| A18C | .return_with_result←1← A1A7 BCS |
| PLA ; Pop saved Y from stack | |
| A18D | LDA (fs_crc_lo),y ; Load command line char at Y |
| A18F | RTS ; Return (C and V set per result) |
| A190 | .loop_scan_past_word←1← A19D BNE |
| INY ; Advance past character | |
| A191 | .check_char_type←1← A130 BMI |
| LDA (fs_crc_lo),y ; Load current char | |
| A193 | CMP #&0d ; Is it CR (end of line)? |
| A195 | BEQ set_c_and_return ; Yes: end of input |
| A197 | CMP #&2e ; Is it '.' (abbreviation dot)? |
| A199 | BEQ skip_sep_spaces ; Yes: skip to next word |
| A19B | CMP #&20 ; Is it space? |
| A19D | BNE loop_scan_past_word ; No: keep scanning |
| A19F | .skip_sep_spaces←2← A199 BEQ← A1A4 BEQ |
| INY ; Skip past separator | |
| A1A0 | LDA (fs_crc_lo),y ; Load next char |
| A1A2 | CMP #&20 ; Is it space? |
| A1A4 | BEQ skip_sep_spaces ; Yes: skip consecutive spaces |
| A1A6 | .set_c_and_return←1← A195 BEQ |
| SEC ; C=1: have more text to match | |
| A1A7 | BCS return_with_result ; ALWAYS branch |
| A1A9 | .fscv_2_star_run |
| JSR save_ptr_to_os_text ; Save text pointer | |
| A1AC | JSR mask_owner_access ; Set owner-only access mask |
| A1AF | JSR parse_cmd_arg_y0 ; Parse command argument (Y=0) |
| A1B2 | .open_file_for_run←1← A229 BNE |
| LDX #1 ; X=1: buffer index | |
| A1B4 | JSR copy_arg_to_buf ; Copy argument to buffer |
| A1B7 | LDA #2 ; A=2: open for update |
| A1B9 | STA fs_cmd_data ; Store open mode |
| A1BC | LDY #&12 ; Y=&12: open file command |
| A1BE | JSR save_net_tx_cb ; Send open request to server |
| A1C1 | LDA fs_cmd_data ; Load reply status |
| A1C4 | CMP #1 ; Status 1 (success)? |
| A1C6 | BNE try_library_path ; No: file not found, try library |
| A1C8 | LDX #3 ; X=3: check 4 handle bytes |
| A1CA | .loop_check_handles←1← A1D3 BPL |
| INC fs_handle_check,x ; Increment handle byte | |
| A1CD | BEQ alloc_run_fcb ; Was &FF (overflow to 0): try next |
| A1CF | JMP check_exec_addr ; Non-zero: handle valid, execute |
| A1D2 | .alloc_run_fcb←1← A1CD BEQ |
| DEX ; Try next handle byte | |
| A1D3 | BPL loop_check_handles ; Loop until all checked |
| A1D5 | JSR alloc_fcb_or_error ; Allocate new FCB or raise error |
| A1D8 | LDX #1 ; X=1: open mode index |
| A1DA | STX fs_cmd_data ; Store in l0f05 |
| A1DD | STX fs_func_code ; Store in l0f06 |
| A1E0 | INX ; X=2 X=&02 |
| A1E1 | JSR copy_arg_to_buf ; Copy argument to buffer |
| A1E4 | LDY #6 ; Y=6: re-open command |
| A1E6 | JSR save_net_tx_cb ; Send re-open request |
| A1E9 | BCS done_run_dispatch ; C set: error on re-open |
| A1EB | JMP alloc_run_channel ; C clear: finalise file opening |
| A1EE | .done_run_dispatch←1← A1E9 BCS |
| JMP finalise_and_return ; Jump to finalise and return | |
| A1F1 | .try_library_path←1← A1C6 BNE |
| LDA fs_filename_buf ; Load first char of filename | |
| A1F4 | CMP #&24 ; Is it '$' (root dir)? |
| A1F6 | BEQ error_bad_command ; Yes: no library search, error |
| A1F8 | LDA fs_lib_flags ; Load library flag byte |
| A1FB | BMI library_tried ; Bit 7 set: library already tried |
| A1FD | ROL ; Rotate bits to check library state |
| A1FE | ROL ; Rotate again |
| A1FF | BMI restore_filename ; Bit 7 set: restore from backup |
| A201 | BCS error_bad_command ; Carry set: bad command |
| A203 | LDX #&ff ; X=&FF: pre-increment for loop |
| A205 | .loop_find_name_end←1← A20B BNE |
| INX ; Find end of filename | |
| A206 | LDA fs_filename_buf,x ; Load filename byte |
| A209 | CMP #&0d ; Is it CR (end)? |
| A20B | BNE loop_find_name_end ; No: continue scanning |
| A20D | .loop_shift_name_right←1← A214 BPL |
| LDA fs_filename_buf,x ; Shift filename right by 8 bytes | |
| A210 | STA fs_filename_backup,x ; Store shifted byte |
| A213 | DEX ; Previous byte |
| A214 | BPL loop_shift_name_right ; Loop until all shifted |
| A216 | LDX #7 ; X=7: 'Library.' is 8 bytes |
| A218 | .loop_copy_lib_prefix←1← A21F BPL |
| LDA library_dir_prefix,x ; Copy 'Library.' prefix | |
| A21B | STA fs_filename_buf,x ; Store prefix byte |
| A21E | DEX ; Previous byte |
| A21F | BPL loop_copy_lib_prefix ; Loop until prefix copied |
| A221 | LDA fs_lib_flags ; Load library flag |
| A224 | ORA #&60 ; Set bits 5-6: library path active |
| A226 | STA fs_lib_flags ; Store updated flag |
| A229 | .retry_with_library←1← A240 BNE |
| BNE open_file_for_run ; Retry file open with library path | |
| A22B | .restore_filename←1← A1FF BMI |
| LDX #&ff ; X=&FF: pre-increment for loop | |
| A22D | .loop_restore_name←1← A236 BNE |
| INX ; Restore original filename | |
| A22E | LDA fs_filename_backup,x ; Load backup byte |
| A231 | STA fs_filename_buf,x ; Store to filename buffer |
| A234 | EOR #&0d ; Is it CR (end)? |
| A236 | BNE loop_restore_name ; No: continue restoring |
| A238 | JSR mask_owner_access ; Set owner-only access mask |
| A23B | ORA #&80 ; Set bit 7: library tried |
| A23D | STA fs_lib_flags ; Store updated flag |
| A240 | BNE retry_with_library ; ALWAYS branch |
| A242 | .library_tried←1← A1FB BMI |
| JSR mask_owner_access ; Set owner-only access mask | |
| A245 | .error_bad_command←3← A1F6 BEQ← A201 BCS← B316 JMP |
| LDA #&fe ; Error code &FE | |
| A247 | JSR error_bad_inline ; Generate 'Bad command' error |
| A24A | EQUS "command." |
| A252 | .check_exec_addr←1← A1CF JMP |
| LDX #3 ; X=3: check 4 execution bytes | |
| A254 | .loop_check_exec_bytes←1← A25A BNE |
| INC fs_func_code,x ; Increment execution address byte | |
| A257 | BNE setup_oscli_arg ; Non-zero: valid, go to OSCLI |
| A259 | DEX ; Try next byte |
| A25A | BNE loop_check_exec_bytes ; Loop until all checked |
| A25C | LDA #&93 ; Error code &93 |
| A25E | JSR error_inline_log ; Generate 'No!' error |
| A261 | EQUS "No!." |
| A265 | .alloc_run_channel←1← A1EB JMP |
| LDA fs_cmd_data ; Load open mode result | |
| A268 | JSR alloc_fcb_slot ; Allocate FCB slot |
| A26B | TAY ; Transfer to Y |
| A26C | LDA #0 ; A=0: clear channel status |
| A26E | STA chan_status,x ; Clear status in channel table |
| A271 | STY cur_dir_handle ; Store handle in l1070 |
| A274 | LDY #3 ; Y=3: OSCLI execution |
| A276 | JMP boot_cmd_oscli ; Execute via boot/OSCLI path |
| A279 | .library_dir_prefix←1← A218 LDA |
| EQUS "Library." | |
| A281 | .setup_oscli_arg←1← A257 BNE |
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer (X=0) | |
| A284 | LDY #0 ; Y=0 |
| A286 | CLC ; C=0 for GSINIT |
| A287 | JSR gsinit ; Initialise GS string read |
| A28A | .loop_read_gs_string←1← A28D BCC |
| JSR gsread ; Read next GS character | |
| A28D | BCC loop_read_gs_string ; C clear: more chars |
| A28F | DEY ; Back up one position |
| A290 | .loop_skip_trailing←1← A295 BEQ |
| INY ; Skip trailing spaces | |
| A291 | LDA (os_text_ptr),y ; Load next char |
| A293 | CMP #&20 ; Is it space? |
| A295 | BEQ loop_skip_trailing ; Yes: skip it |
| A297 | EOR #&0d ; Check for CR (end of line) |
| A299 | CLC ; C=0 for addition |
| A29A | TYA ; Transfer Y offset to A |
| A29B | ADC os_text_ptr ; Add to text pointer low |
| A29D | STA fs_cmd_context ; Store as command tail pointer low |
| A2A0 | LDA os_text_ptr_hi ; Load text pointer high |
| A2A2 | ADC #0 ; Add carry |
| A2A4 | STA fs_context_hi ; Store as command tail pointer high |
| A2A7 | JSR save_text_ptr ; Save text pointer for later |
| A2AA | LDX #&0e ; X=&0E: OSWORD parameter offset |
| A2AC | STX fs_block_offset ; Store as block offset high |
| A2AE | LDA #&10 ; A=&10: OSWORD parameter size |
| A2B0 | STA fs_options ; Store as options pointer |
| A2B2 | STA fs_work_16 ; Store to l0e16 |
| A2B5 | LDX #&4a ; X=&4A: FS command table offset |
| A2B7 | LDY #5 ; Y=5 |
| A2B9 | JSR do_fs_cmd_iteration ; Execute FS command iteration |
| A2BC | LDA tube_present ; Load tube flag |
| A2BF | BEQ dispatch_via_vector ; Zero: no tube transfer needed |
| A2C1 | AND fs_load_upper ; AND with l0f0b |
| A2C4 | AND fs_addr_check ; AND with l0f0c |
| A2C7 | CMP #&ff ; All &FF? |
| A2C9 | BEQ dispatch_via_vector ; Yes: no tube transfer needed |
| A2CB | JSR tube_claim_c3 ; Claim tube for data transfer |
| A2CE | LDX #9 ; X=9: parameter count |
| A2D0 | LDY #&0f ; Y=&0F: parameter offset |
| A2D2 | LDA #4 ; A=4: tube transfer type |
| A2D4 | JMP tube_addr_data_dispatch ; Dispatch tube address/data |
| A2D7 | .dispatch_via_vector←2← A2BF BEQ← A2C9 BEQ |
| LDA #1 ; A=1 | |
| A2D9 | JMP (fs_load_vector) ; Dispatch via indirect vector |
| A2DC | .fsreply_3_set_csd←1← 944B JMP |
| JSR find_station_bit3 ; Find station with bit 3 set | |
| A2DF | JMP return_with_last_flag ; Return with last flag state |
| A2E2 | .fsreply_5_set_lib |
| JSR flip_set_station_boot ; Flip/set station boot config | |
| A2E5 | JMP return_with_last_flag ; Return with last flag state |
Find printer server station in table (bit 2)Scans the 16-entry station table for a slot matching the current station/network address with bit 2 set (printer server active). Sets V if found, clears V if not. Falls through to allocate or update the matching slot with the new station address and status flags. |
|
| A2E8 | .find_station_bit2←1← A387 JSR |
| LDX #&10 ; X=&10: scan 16 slots (15 to 0) | |
| A2EA | CLV ; Clear V |
| A2EB | .loop_search_stn_bit2←2← A2F1 BNE← A2F8 BEQ |
| DEX ; Try next slot | |
| A2EC | BMI done_search_bit2 ; All slots checked: not found |
| A2EE | JSR match_station_net ; Compare station/network |
| A2F1 | BNE loop_search_stn_bit2 ; No match: try next |
| A2F3 | LDA chan_status,x ; Load slot status byte |
| A2F6 | AND #4 ; Test bit 2 (PS active flag)? |
| A2F8 | BEQ loop_search_stn_bit2 ; Not set: try next |
| A2FA | TYA ; Transfer Y to A |
| A2FB | STA fcb_net_or_port,x ; Store Y in slot data |
| A2FE | BIT bit_test_ff ; Set V (found match) |
| A301 | .done_search_bit2←1← A2EC BMI |
| STY fs_urd_handle ; Store Y to l0e02 | |
| A304 | BVS set_flags_bit2 ; V set: found, skip allocation |
| A306 | TYA ; Transfer Y to A |
| A307 | JSR alloc_fcb_slot ; Allocate FCB slot |
| A30A | STA handle_1_fcb ; Store allocation result |
| A30D | BEQ jmp_restore_fs_ctx ; Zero: failed, restore context |
| A30F | .set_flags_bit2←1← A304 BVS |
| LDA #&26 ; A=&26: station flags value | |
| A311 | BNE store_stn_flags_restore ; ALWAYS branch |
Find file server station in table (bit 3)Scans the 16-entry station table for a slot matching the current station/network address with bit 3 set (file server active). Sets V if found, clears V if not. Falls through to allocate or update the matching slot with the new station address and status flags. |
|
| A313 | .find_station_bit3←3← A2DC JSR← A345 JSR← A38D JSR |
| LDX #&10 ; X=&10: scan 16 slots (15 to 0) | |
| A315 | CLV ; Clear V |
| A316 | .loop_search_stn_bit3←2← A31C BNE← A323 BEQ |
| DEX ; Try next slot | |
| A317 | BMI done_search_bit3 ; All slots checked: not found |
| A319 | JSR match_station_net ; Compare station/network |
| A31C | BNE loop_search_stn_bit3 ; No match: try next |
| A31E | LDA chan_status,x ; Load slot status byte |
| A321 | AND #8 ; Test bit 3 (FS active flag)? |
| A323 | BEQ loop_search_stn_bit3 ; Not set: try next |
| A325 | TYA ; Transfer Y to A |
| A326 | STA fcb_net_or_port,x ; Store Y in slot data |
| A329 | BIT bit_test_ff ; Set V (found match) |
| A32C | .done_search_bit3←1← A317 BMI |
| STY fs_csd_handle ; Store Y to l0e03 | |
| A32F | BVS set_flags_bit3 ; V set: found, skip allocation |
| A331 | TYA ; Transfer Y to A |
| A332 | JSR alloc_fcb_slot ; Allocate FCB slot |
| A335 | STA handle_2_fcb ; Store allocation result |
| A338 | BEQ jmp_restore_fs_ctx ; Zero: failed, restore context |
| A33A | .set_flags_bit3←1← A32F BVS |
| LDA #&2a ; A=&2A: station flags value | |
| A33C | BNE store_stn_flags_restore ; ALWAYS branch |
*Flip command handlerExchanges the CSD and CSL (library) handles. Saves the current CSD handle (&0E03), loads the library handle (&0E04) into Y, and calls find_station_bit3 to install it as the new CSD. Restores the original CSD handle and falls through to flip_set_station_boot to install it as the new library. Useful when files to be LOADed are in the library and *DIR/*LIB would be inconvenient.
|
||||
| A33E | .cmd_flip | |||
| LDA fs_csd_handle ; Load current CSD handle | ||||
| A341 | PHA ; Save CSD handle | |||
| A342 | LDY fs_lib_handle ; Load library handle into Y | |||
| A345 | JSR find_station_bit3 ; Install library as new CSD | |||
| A348 | PLA ; Restore original CSD handle | |||
| A349 | TAY ; Y = original CSD (becomes library) | |||
| fall through ↓ | ||||
Set boot option for a station in the tableScans up to 16 station table entries for one matching the current address with bit 4 set (boot-eligible). Stores the requested boot type in the matching entry and calls restore_fs_context to re-establish the filing system state. |
|
| A34A | .flip_set_station_boot←2← A2E2 JSR← A393 JSR |
| LDX #&10 ; X=&10: max 16 station entries | |
| A34C | CLV ; Clear V (no match found yet) |
| A34D | .loop_search_stn_boot←2← A353 BNE← A35A BEQ |
| DEX ; Decrement station index | |
| A34E | BMI done_search_boot ; All searched: exit loop |
| A350 | JSR match_station_net ; Check if station[X] matches |
| A353 | BNE loop_search_stn_boot ; No match: try next station |
| A355 | LDA chan_status,x ; Load station flags byte |
| A358 | AND #&10 ; Test bit 4 (active flag) |
| A35A | BEQ loop_search_stn_boot ; Not active: try next station |
| A35C | TYA ; Transfer boot type to A |
| A35D | STA fcb_net_or_port,x ; Store boot setting for station |
| A360 | BIT bit_test_ff ; Set V flag (station match found) |
| A363 | .done_search_boot←1← A34E BMI |
| STY fs_lib_handle ; Store boot type | |
| A366 | BVS set_flags_boot ; V set (matched): skip allocation |
| A368 | TYA ; Boot type to A |
| A369 | JSR alloc_fcb_slot ; Allocate FCB slot for new entry |
| A36C | STA handle_3_fcb ; Store allocation result |
| A36F | BEQ jmp_restore_fs_ctx ; Zero: allocation failed, exit |
| A371 | .set_flags_boot←1← A366 BVS |
| LDA #&32 ; A=&32: station flags (active+boot) | |
| A373 | .store_stn_flags_restore←2← A311 BNE← A33C BNE |
| STA chan_status,x ; Store station flags | |
| A376 | .jmp_restore_fs_ctx←3← A30D BEQ← A338 BEQ← A36F BEQ |
| JMP restore_fs_context ; Restore FS context and return | |
| A379 | .fsreply_1_copy_handles_boot |
| JSR close_all_net_chans ; Close all network channels | |
| A37C | SEC ; Set carry flag |
| A37D | LDA fs_reply_cmd ; Load reply boot type |
| A380 | STA fs_boot_option ; Store as current boot type |
| A383 | .fsreply_2_copy_handles |
| PHP ; Save processor status | |
| A384 | LDY fs_cmd_data ; Load station number from reply |
| A387 | JSR find_station_bit2 ; Find station entry with bit 2 |
| A38A | LDY fs_func_code ; Load network number from reply |
| A38D | JSR find_station_bit3 ; Find station entry with bit 3 |
| A390 | LDY fs_data_count ; Load boot type from reply |
| A393 | JSR flip_set_station_boot ; Set boot config for station |
| A396 | PLP ; Restore processor status |
| A397 | BCS check_auto_boot_flag ; Carry set: proceed with boot |
| A399 | JMP return_with_last_flag ; Return with last flag |
| A39C | .check_auto_boot_flag←1← A397 BCS |
| LDA fs_lib_flags ; Load config flags | |
| A39F | TAX ; Save copy in X |
| A3A0 | AND #4 ; Test bit 2 (auto-boot flag) |
| A3A2 | PHP ; Save bit 2 test result |
| A3A3 | TXA ; Restore full flags |
| A3A4 | AND #&fb ; Clear bit 2 (consume flag) |
| A3A6 | STA fs_lib_flags ; Store cleared flags |
| A3A9 | PLP ; Restore bit 2 test result |
| A3AA | BNE load_boot_type ; Bit 2 was set: skip to boot cmd |
| A3AC | LDA #osbyte_scan_keyboard ; OSBYTE &79: scan keyboard |
| A3AE | LDX #(255 - inkey_key_ctrl) EOR 128 ; X=internal key number EOR 128 |
| A3B0 | JSR osbyte ; Test for 'CTRL' key pressed (X=129) |
| A3B3 | TXA ; X has top bit set if 'CTRL' pressed |
| A3B4 | BPL load_boot_type ; CTRL not pressed: proceed to boot |
| A3B6 | .boot_load_cmd←1← A3CE BEQ |
| RTS ; CTRL pressed: cancel boot, return | |
| A3B7 | EQUS "L.!BOOT" |
| A3BE | EQUB &0D |
| A3BF | .boot_exec_cmd |
| EQUS "E.!BOOT" | |
| A3C6 | EQUB &0D |
| ; Boot option OSCLI address table | |
| ; Low bytes of boot command string addresses, | |
| ; all in page &A3. Indexed by boot option 0-3 | |
| ; (option 0 is never reached due to BEQ). | |
| ; Entry 2 reuses the tail of 'L.!BOOT' to | |
| ; get '!BOOT' (*RUN equivalent). | |
| A3C7 | .boot_oscli_lo_table←1← A3D0 LDX |
| EQUB &C6 ; Opt 0: &A3C6 (don't-care, unused) | |
| A3C8 | EQUB &B7 ; Opt 1: &A3B7 'L.!BOOT' (*LOAD) |
| A3C9 | EQUB &B9 ; Opt 2: &A3B9 '!BOOT' (*RUN) |
| A3CA | EQUB &BF ; Opt 3: &A3BF 'E.!BOOT' (*EXEC) |
| A3CB | .load_boot_type←2← A3AA BNE← A3B4 BPL |
| LDY fs_boot_option ; Load boot type | |
| A3CE | BEQ boot_load_cmd ; Type 0: no command, just return |
| A3D0 | .boot_cmd_oscli←1← A276 JMP |
| LDX boot_oscli_lo_table,y ; Look up boot command address low | |
| A3D3 | LDY #&a3 ; Boot command address high (&A3xx) |
| A3D5 | JMP oscli ; Execute boot command via OSCLI |
| ; Star command table (4 interleaved sub-tables). | |
| ; Each entry: ASCII name + flag byte (&80+) + | |
| ; dispatch address word (PHA/PHA/RTS, addr-1). | |
| ; Sub-tables separated by &80 sentinel bytes. | |
| ; Flag byte: bit 7 = end of name marker, | |
| ; bit 6 = set V on return if no argument, | |
| ; bits 0-4 = *HELP syntax string index. | |
| ; 1: Utility cmds 2: NFS commands | |
| ; 3: Help topics 4: Copro/attributes | |
| A3D8 | .cmd_table_fs←12← 8BA3 LDA← 8BB2 LDA← 8BBA LDA← 8BC7 LDA← 8BF5 LDA← 8BFC LDA← 9316 LDA← 931E LDA← A12D LDA← A132 LDA← A142 LDA← A179 LDA |
| EQUS "C" ; *Close (first char) | |
| A3D9 | .cmd_table_fs_lo←3← 8C88 LDA← A123 LDA← B30A ORA |
| EQUS "l" ; *Close cont (dispatch lo base) | |
| A3DA | .cmd_table_fs_hi←3← 8C84 LDA← A11F LDA← B337 AND |
| EQUS "ose" ; *Close cont (dispatch hi base) | |
| A3DD | EQUB &80 ; No syntax |
| A3DE | EQUW cmd_close-1 ; Dispatch addr-1 |
| A3E0 | EQUS "Dump" ; *Dump |
| A3E4 | EQUB &C4 ; V no arg; syn 4: <filename> ... |
| A3E5 | EQUW cmd_dump-1 ; Dispatch addr-1 |
| A3E7 | EQUS "Net" ; *Net (select NFS) |
| A3EA | EQUB &80 ; No syntax |
| A3EB | EQUW cmd_net_fs-1 ; Dispatch addr-1 |
| A3ED | EQUS "Pollps" ; *Pollps |
| A3F3 | EQUB &88 ; Syn 8: (<stn. id.>|<ps type>) |
| A3F4 | EQUW cmd_pollps-1 ; Dispatch addr-1 |
| A3F6 | EQUS "Print" ; *Print |
| A3FB | EQUB &CC ; V no arg; syn 12: <filename> |
| A3FC | EQUW cmd_print-1 ; Dispatch addr-1 |
| A3FE | EQUS "Prot" ; *Prot |
| A402 | EQUB &8E ; Syn 14: (attribute keywords) |
| A403 | EQUW cmd_prot-1 ; Dispatch addr-1 |
| A405 | EQUS "PS" ; *PS; syn 8: (<stn. id.>|<ps type>) |
| A407 | EQUB &88 |
| A408 | EQUW cmd_ps-1 ; Dispatch addr-1 |
| A40A | EQUS "Roff" ; *Roff |
| A40E | EQUB &80 ; No syntax |
| A40F | EQUW cmd_roff-1 ; Dispatch addr-1 |
| A411 | EQUS "Type" ; *Type |
| A415 | EQUB &CC ; V no arg; syn 12: <filename> |
| A416 | EQUW cmd_type-1 ; Dispatch addr-1 |
| A418 | EQUS "Unprot" ; *Unprot |
| A41E | EQUB &8E ; Syn 14: (attribute keywords) |
| A41F | EQUW cmd_unprot-1 ; Dispatch addr-1 |
| A421 | EQUB &80 ; End of utility sub-table |
| A422 | .cmd_table_nfs |
| EQUS "Access" ; *Access | |
| A428 | EQUB &C9 ; V no arg; syn 9: <obj> (L)(W)(R)... |
| A429 | EQUW cmd_fs_operation-1 ; Dispatch addr-1 |
| A42B | EQUS "Bye" ; *Bye |
| A42E | EQUB &80 ; No syntax |
| A42F | EQUW cmd_bye-1 ; Dispatch addr-1 |
| A431 | EQUS "Cdir" ; *Cdir |
| A435 | EQUB &C6 ; V no arg; syn 6: <dir> (<number>) |
| A436 | EQUW cmd_cdir-1 ; Dispatch addr-1 |
| A438 | EQUS "Delete" ; *Delete |
| A43E | EQUB &C3 ; V no arg; syn 3: <object> |
| A43F | EQUW cmd_fs_operation-1 ; Dispatch addr-1 |
| A441 | EQUS "Dir" ; *Dir |
| A444 | EQUB &81 ; Syn 1: (<dir>) |
| A445 | EQUW cmd_dir-1 ; Dispatch addr-1 |
| A447 | EQUS "Ex" ; *Ex; syn 1: (<dir>) |
| A449 | EQUB &81 |
| A44A | EQUW cmd_ex-1 ; Dispatch addr-1 |
| A44C | EQUS "Flip" ; *Flip |
| A450 | EQUB &80 ; No syntax |
| A451 | EQUW cmd_flip-1 ; Dispatch addr-1 |
| A453 | EQUS "FS" ; *FS; syn 11: (<stn. id.>) |
| A455 | EQUB &8B |
| A456 | EQUW cmd_fs-1 ; Dispatch addr-1 |
| A458 | EQUS "Info" ; *Info |
| A45C | EQUB &C3 ; V no arg; syn 3: <object> |
| A45D | EQUW cmd_fs_operation-1 ; Dispatch addr-1 |
| A45F | .cmd_table_nfs_iam←1← 8DA4 LDA |
| EQUS "I am" ; *I am | |
| A463 | EQUB &C2 ; V no arg; syn 2: (<stn>) <user>... |
| A464 | EQUW cmd_iam-1 ; Dispatch addr-1 |
| A466 | EQUS "Lcat" ; *Lcat |
| A46A | EQUB &81 ; Syn 1: (<dir>) |
| A46B | EQUW cmd_lcat-1 ; Dispatch addr-1 |
| A46D | EQUS "Lex" ; *Lex |
| A470 | EQUB &81 ; Syn 1: (<dir>) |
| A471 | EQUW cmd_lex-1 ; Dispatch addr-1 |
| A473 | EQUS "Lib" ; *Lib |
| A476 | EQUB &C5 ; V no arg; syn 5: <dir> |
| A477 | EQUW cmd_fs_operation-1 ; Dispatch addr-1 |
| A479 | EQUS "Pass" ; *Pass |
| A47D | EQUB &C7 ; V no arg; syn 7: <pass> ... |
| A47E | EQUW cmd_pass-1 ; Dispatch addr-1 |
| A480 | EQUS "Remove" ; *Remove |
| A486 | EQUB &C3 ; V no arg; syn 3: <object> |
| A487 | EQUW cmd_remove-1 ; Dispatch addr-1 |
| A489 | EQUS "Rename" ; *Rename |
| A48F | EQUB &CA ; V no arg; syn 10: <file> <new file> |
| A490 | EQUW cmd_rename-1 ; Dispatch addr-1 |
| A492 | EQUS "Wipe" ; *Wipe |
| A496 | EQUB &81 ; Syn 1: (<dir>) |
| A497 | EQUW cmd_wipe-1 ; Dispatch addr-1 |
| A499 | EQUB &80 ; End of NFS sub-table |
| A49A | .cmd_table_help |
| EQUB &09, &8E ; &09/&8E: before help-only entries | |
| A49C | EQUS "Net" ; *Net (local) |
| A49F | EQUB &80 ; No syntax |
| A4A0 | EQUW help_net-1 ; Dispatch addr-1 |
| A4A2 | EQUS "Utils" ; *Utils |
| A4A7 | EQUB &80 ; No syntax |
| A4A8 | EQUW help_utils-1 ; Dispatch addr-1 |
| A4AA | EQUB &80 ; End of help topic sub-table |
| ; Protection attribute keyword table. Each entry: | |
| ; ASCII name + flag byte (&80+) + OR mask + AND mask. | |
| ; Used by *Prot (ORA lo byte) and *Unprot (AND hi | |
| ; byte) to set/clear individual protection bits. | |
| ; Also listed by *HELP Prot/*HELP Unprot via the | |
| ; shared commands handler (syntax index 14). | |
| ; Bits: 0=Peek 1=Poke 2=JSR 3=Proc 4=Utils 5=Halt | |
| A4AB | .cmd_table_copro |
| EQUS "Halt" ; Halt | |
| A4AF | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4B0 | EQUB &20 ; *Prot OR mask: bit 5 |
| A4B1 | EQUB &DF ; *Unprot AND mask: ~bit 5 |
| A4B2 | EQUS "JSR" ; JSR |
| A4B5 | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4B6 | EQUB &04 ; *Prot OR mask: bit 2 |
| A4B7 | EQUB &FB ; *Unprot AND mask: ~bit 2 |
| A4B8 | EQUS "Peek" ; Peek |
| A4BC | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4BD | EQUB &01 ; *Prot OR mask: bit 0 |
| A4BE | EQUB &FE ; *Unprot AND mask: ~bit 0 |
| A4BF | EQUS "Poke" ; Poke |
| A4C3 | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4C4 | EQUB &02 ; *Prot OR mask: bit 1 |
| A4C5 | EQUB &FD ; *Unprot AND mask: ~bit 1 |
| A4C6 | EQUS "Proc" ; Proc |
| A4CA | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4CB | EQUB &08 ; *Prot OR mask: bit 3 |
| A4CC | EQUB &F7 ; *Unprot AND mask: ~bit 3 |
| A4CD | EQUS "Utils" ; Utils |
| A4D2 | EQUB &A9 ; Flag &A9: syn 9 (unused) |
| A4D3 | EQUB &10 ; *Prot OR mask: bit 4 |
| A4D4 | EQUB &EF ; *Unprot AND mask: ~bit 4 |
| A4D5 | EQUB &80 ; End of attribute keyword table |
Filing system OSWORD entryHandles MOS service call 8 (unrecognised OSWORD). Filters OSWORD codes &0E-&14 by subtracting &0E (via CLC/SBC &0D) and rejecting values outside 0-6. For valid codes, calls osword_setup_handler to push the dispatch address, then copies 3 bytes from the RX buffer to osword_flag workspace. |
|
| A4D6 | .svc_8_osword |
| CLC ; CLC so SBC subtracts value+1 | |
| A4D7 | LDA osbyte_a_copy ; A = OSWORD number |
| A4D9 | SBC #&0d ; A = OSWORD - &0E (CLC+SBC = -&0E) |
| A4DB | BMI return_from_osword_setup ; Below &0E: not ours, return |
| A4DD | CMP #7 ; Index >= 7? (OSWORD > &14) |
| A4DF | BCS return_from_osword_setup ; Above &14: not ours, return |
| A4E1 | JSR osword_setup_handler ; Set up dispatch and save state |
| A4E4 | LDY #2 ; Copy 3 bytes (Y=2,1,0) |
| A4E6 | .loop_copy_osword_data←1← A4EC BPL |
| LDA (net_rx_ptr),y ; Load from RX buffer | |
| A4E8 | STA osword_flag,y ; Store to osword_flag workspace |
| A4EB | DEY ; Next byte down |
| A4EC | BPL loop_copy_osword_data ; Loop for all 3 bytes |
| A4EE | RTS ; Return from svc_8_osword |
Push OSWORD handler address for RTS dispatchIndexes the OSWORD dispatch table by X to push a handler address (hi then lo) onto the stack. Copies 3 bytes from the osword_flag workspace into the RX buffer, loads PB byte 0 (the OSWORD sub-code), and clears svc_state. The subsequent RTS dispatches to the pushed handler address.
|
||||
| A4EF | .osword_setup_handler←1← A4E1 JSR | |||
| TAX ; X = OSWORD index (0-6) | ||||
| A4F0 | LDA osword_dispatch_hi_table,x ; Load handler address high byte | |||
| A4F3 | PHA ; Push high byte for RTS dispatch | |||
| A4F4 | LDA osword_dispatch_lo_table,x ; Load handler address low byte | |||
| A4F7 | PHA ; Push low byte for RTS dispatch | |||
| A4F8 | LDY #2 ; Copy 3 bytes (Y=2,1,0) | |||
| A4FA | .loop_copy_osword_flag←1← A500 BPL | |||
| LDA osword_flag,y ; Load from osword_flag workspace | ||||
| A4FD | STA (net_rx_ptr),y ; Store to RX buffer | |||
| A4FF | DEY ; Next byte down | |||
| A500 | BPL loop_copy_osword_flag ; Loop for all 3 bytes | |||
| A502 | INY ; Y=0 (INY from -1) | |||
| A503 | LDA (osword_pb_ptr),y ; Load PB byte 0 (OSWORD sub-code) | |||
| A505 | STY svc_state ; Clear service state | |||
| A507 | .return_from_osword_setup←2← A4DB BMI← A4DF BCS | |||
| RTS ; RTS dispatches to pushed handler | ||||
| ; OSWORD dispatch table (7 entries, split lo/hi). | ||||
| ; PHA/PHA/RTS dispatch used by svc_8_osword. | ||||
| ; Maps OSWORD codes &0E-&14 to handler routines. | ||||
| A508 | .osword_dispatch_lo_table←1← A4F4 LDA | |||
| EQUB <(osword_0e_handler-1) ; lo-&0E: Read clock | ||||
| A509 | EQUB <(return_from_osword_setup-1) ; lo-&0F: (unimplemented) | |||
| A50A | EQUB <(osword_10_handler-1) ; lo-&10: Transmit | |||
| A50B | EQUB <(osword_11_handler-1) ; lo-&11: Receive | |||
| A50C | EQUB <(osword_12_handler-1) ; lo-&12: Read station info | |||
| A50D | EQUB <(osword_13_dispatch-1) ; lo-&13: Misc operations | |||
| A50E | EQUB <(osword_14_handler-1) ; lo-&14: Bridge/net config | |||
| A50F | .osword_dispatch_hi_table←1← A4F0 LDA | |||
| EQUB >(osword_0e_handler-1) ; hi-&0E: Read clock | ||||
| A510 | EQUB >(return_from_osword_setup-1) ; hi-&0F: (unimplemented) | |||
| A511 | EQUB >(osword_10_handler-1) ; hi-&10: Transmit | |||
| A512 | EQUB >(osword_11_handler-1) ; hi-&11: Receive | |||
| A513 | EQUB >(osword_12_handler-1) ; hi-&12: Read station info | |||
| A514 | EQUB >(osword_13_dispatch-1) ; hi-&13: Misc operations | |||
| A515 | EQUB >(osword_14_handler-1) ; hi-&14: Bridge/net config | |||
| A516 | .osword_0e_handler | |||
| PHA ; Save A for later test | ||||
| A517 | BIT fs_flags ; Test station active flag | |||
| A51A | BPL return_from_osword_0e ; Not active: just return | |||
| A51C | PLA ; Restore A (OSWORD sub-code) | |||
| A51D | CMP #4 ; Sub-code = 4? (read clock) | |||
| A51F | BEQ save_txcb_and_convert ; Yes: handle clock read | |||
| A521 | LDA #8 ; Other sub-codes: set state = 8 | |||
| A523 | STA svc_state ; Store service state | |||
| A525 | .return_from_osword_0e←1← A51A BPL | |||
| RTS ; Return | ||||
| A526 | .save_txcb_and_convert←1← A51F BEQ | |||
| LDX #0 ; X=0: start of TX control block | ||||
| A528 | LDY #&10 ; Y=&10: length of TXCB to save | |||
| A52A | JSR save_net_tx_cb ; Save current TX control block | |||
| A52D | LDA fs_load_vector ; Load seconds from clock workspace | |||
| A530 | JSR bin_to_bcd ; Convert binary to BCD | |||
| A533 | STA fs_load_upper ; Store BCD seconds | |||
| A536 | LDA fs_reply_cmd ; Load minutes from clock workspace | |||
| A539 | JSR bin_to_bcd ; Convert binary to BCD | |||
| A53C | STA fs_handle_check ; Store BCD minutes | |||
| A53F | LDA fs_data_count ; Load hours from clock workspace | |||
| A542 | JSR bin_to_bcd ; Convert binary to BCD | |||
| A545 | STA fs_load_vector ; Store BCD hours | |||
| A548 | LDA #0 ; Clear hours high position | |||
| A54A | STA fs_reply_cmd ; Store zero | |||
| A54D | LDA fs_func_code ; Load day+month byte | |||
| A550 | PHA ; Save for later high nibble extract | |||
| A551 | LDA fs_cmd_data ; Load day value | |||
| A554 | JSR bin_to_bcd ; Convert day to BCD | |||
| A557 | STA fs_data_count ; Store BCD day | |||
| A55A | PLA ; Restore day+month byte | |||
| A55B | PHA ; Save again for month extract | |||
| A55C | AND #&0f ; Mask low nibble (month low bits) | |||
| A55E | JSR bin_to_bcd ; Convert to BCD | |||
| A561 | STA fs_func_code ; Store BCD month | |||
| A564 | PLA ; Restore day+month byte | |||
| A565 | LSR ; Shift high nibble down | |||
| A566 | LSR ; Continue shifting | |||
| A567 | LSR ; Continue shifting | |||
| A568 | LSR ; 4th shift: isolate high nibble | |||
| A569 | ADC #&51 ; Add &51 for year offset + carry | |||
| A56B | JSR bin_to_bcd ; Convert year to BCD | |||
| A56E | STA fs_cmd_data ; Store BCD year | |||
| A571 | LDY #6 ; Copy 7 bytes (Y=6 down to 0) | |||
| A573 | .loop_copy_bcd_to_pb←1← A579 BPL | |||
| LDA fs_cmd_data,y ; Load BCD byte from workspace | ||||
| A576 | STA (osword_pb_ptr),y ; Store to parameter block | |||
| A578 | DEY ; Next byte down | |||
| A579 | BPL loop_copy_bcd_to_pb ; Loop for all 7 bytes | |||
| A57B | RTS ; Return | |||
Convert binary byte to BCDUses decimal mode (SED) with a count-up loop: starts at BCD 0 and adds 1 in decimal mode for each decrement of the binary input. Saves and restores the processor flags to avoid leaving decimal mode active. Called 6 times by save_txcb_and_convert for clock date/time conversion.
|
|||||||
| A57C | .bin_to_bcd←6← A530 JSR← A539 JSR← A542 JSR← A554 JSR← A55E JSR← A56B JSR | ||||||
| PHP ; Save processor flags (decimal mode) | |||||||
| A57D | TAX ; X = binary count | ||||||
| A57E | BEQ done_bcd_convert ; Zero: result is 0, skip loop | ||||||
| A580 | SED ; Set decimal mode for BCD add | ||||||
| A581 | LDA #0 ; Start BCD result at 0 | ||||||
| A583 | .loop_bcd_add←1← A587 BNE | ||||||
| CLC ; Clear carry for BCD add | |||||||
| A584 | ADC #1 ; Add 1 in decimal mode | ||||||
| A586 | DEX ; Count down binary value | ||||||
| A587 | BNE loop_bcd_add ; Loop until zero | ||||||
| A589 | .done_bcd_convert←1← A57E BEQ | ||||||
| PLP ; Restore flags (clears decimal mode) | |||||||
| A58A | RTS ; Return with BCD result in A | ||||||
| A58B | .osword_10_handler | ||||||
| ASL ws_0d60 ; Shift ws_0d60 left (status flag) | |||||||
| A58E | TYA ; A = Y (saved index) | ||||||
| A58F | BCS setup_ws_rx_ptrs ; C=1: transmit active path | ||||||
| A591 | STA (osword_pb_ptr),y ; C=0: store Y to parameter block | ||||||
| A593 | RTS ; Return (transmit not active) | ||||||
| A594 | .setup_ws_rx_ptrs←1← A58F BCS | ||||||
| LDA net_rx_ptr_hi ; Set workspace high byte | |||||||
| A596 | STA ws_ptr_hi ; Copy to ws_ptr_hi | ||||||
| A598 | STA nmi_tx_block_hi ; Also set as NMI TX block high | ||||||
| A59A | LDA #&6f ; Low byte = &6F | ||||||
| A59C | STA ws_ptr_lo ; Set ws_ptr_lo | ||||||
| A59E | STA nmi_tx_block ; Set NMI TX block low | ||||||
| A5A0 | LDX #&0f ; X=&0F: byte count for copy | ||||||
| A5A2 | JSR copy_pb_byte_to_ws ; Copy data and begin transmission | ||||||
| A5A5 | JMP tx_begin ; Jump to begin Econet transmission | ||||||
| A5A8 | .osword_11_handler | ||||||
| LDX nfs_workspace_hi ; Load NFS workspace page high byte | |||||||
| A5AA | STX ws_ptr_hi ; Set workspace pointer high | ||||||
| A5AC | STY ws_ptr_lo ; Set workspace pointer low from Y | ||||||
| A5AE | ROR econet_flags ; Rotate Econet flags (save interrupt state) | ||||||
| A5B1 | LDA (osword_pb_ptr),y ; Load PB byte 0 (OSWORD flag) | ||||||
| A5B3 | STA osword_flag ; Store OSWORD flag | ||||||
| A5B5 | BNE use_specified_slot ; Non-zero: use specified slot | ||||||
| A5B7 | LDA #3 ; A=3: start searching from slot 3 | ||||||
| A5B9 | .loop_find_rx_slot←1← A5CB BNE | ||||||
| JSR byte_to_2bit_index ; Convert slot to 2-bit workspace index | |||||||
| A5BC | BCS store_rx_result ; C set: slot invalid, store result | ||||||
| A5BE | LSR ; Shift index right (divide by 4) | ||||||
| A5BF | LSR ; Continue shift | ||||||
| A5C0 | TAX ; Transfer to X as workspace offset | ||||||
| A5C1 | LDA (ws_ptr_lo),y ; Load workspace byte at offset | ||||||
| A5C3 | BEQ store_rx_result ; Zero: slot empty, store result | ||||||
| A5C5 | CMP #&3f ; Compare with &3F ('?' marker) | ||||||
| A5C7 | BEQ store_rx_slot_found ; Match: slot found for receive | ||||||
| A5C9 | INX ; Try next slot index | ||||||
| A5CA | TXA ; Transfer back to A | ||||||
| A5CB | BNE loop_find_rx_slot ; Loop back (A != 0) | ||||||
| A5CD | .store_rx_slot_found←1← A5C7 BEQ | ||||||
| TXA ; Transfer found slot to A | |||||||
| A5CE | LDX #0 ; X=0: index for indirect store | ||||||
| A5D0 | STA (osword_pb_ptr,x) ; Store slot number to PB byte 0 | ||||||
| A5D2 | .use_specified_slot←1← A5B5 BNE | ||||||
| JSR byte_to_2bit_index ; Convert specified slot to workspace index | |||||||
| A5D5 | BCS store_rx_result ; C set: slot invalid, store result | ||||||
| A5D7 | DEY ; Y=Y-1: adjust workspace offset | ||||||
| A5D8 | STY ws_ptr_lo ; Update workspace pointer low | ||||||
| A5DA | LDA #&c0 ; A=&C0: slot active marker | ||||||
| A5DC | LDY #1 ; Y=1: workspace byte offset | ||||||
| A5DE | LDX #&0b ; X=&0B: byte count for PB copy | ||||||
| A5E0 | CPY osword_flag ; Compare Y with OSWORD flag | ||||||
| A5E2 | ADC (ws_ptr_lo),y ; Add workspace byte (check slot state) | ||||||
| A5E4 | BEQ copy_pb_and_mark ; Zero: slot ready, copy PB and mark | ||||||
| A5E6 | BMI increment_and_retry ; Negative: slot busy, increment and retry | ||||||
| A5E8 | .loop_copy_slot_data←1← A5F8 BNE | ||||||
| CLC ; Clear carry for PB copy | |||||||
| A5E9 | .copy_pb_and_mark←1← A5E4 BEQ | ||||||
| JSR copy_pb_byte_to_ws ; Copy PB byte to workspace slot | |||||||
| A5EC | BCS osword_11_done ; C set: copy done, finish | ||||||
| A5EE | LDA #&3f ; A=&3F: mark slot as pending ('?') | ||||||
| A5F0 | LDY #1 ; Y=1: workspace flag offset | ||||||
| A5F2 | STA (ws_ptr_lo),y ; Store pending marker to workspace | ||||||
| A5F4 | BNE osword_11_done ; ALWAYS branch | ||||||
| A5F6 | .increment_and_retry←1← A5E6 BMI | ||||||
| ADC #1 ; Increment retry counter | |||||||
| A5F8 | BNE loop_copy_slot_data ; Non-zero: retry copy loop | ||||||
| A5FA | DEY ; Decrement Y (adjust offset) | ||||||
| A5FB | .store_rx_result←3← A5BC BCS← A5C3 BEQ← A5D5 BCS | ||||||
| STA (osword_pb_ptr),y ; Store result A to PB via Y | |||||||
| A5FD | .osword_11_done←2← A5EC BCS← A5F4 BNE | ||||||
| ROL econet_flags ; Rotate Econet flags back (restore state) | |||||||
| A600 | RTS ; Return from OSWORD 11 handler | ||||||
Store OSWORD parameter block pointer+1 to workspaceComputes PB pointer + 1 and stores the resulting 16-bit address at workspace offset &1C via store_ptr_at_ws_y. Then reads PB byte 1 (the transfer length) and adds the PB low byte to compute the buffer end pointer, stored at workspace offset &20. |
|
| A601 | .store_osword_pb_ptr←1← A8F0 JSR |
| LDY #&1c ; Y=&1C: workspace offset | |
| A603 | LDA osword_pb_ptr ; Load PB pointer low byte |
| A605 | ADC #1 ; Add 1 (C from earlier operation) |
| A607 | JSR store_ptr_at_ws_y ; Store ptr at workspace+Y |
| A60A | LDY #1 ; Y=1: read PB byte 1 |
| A60C | LDA (osword_pb_ptr),y ; Load transfer length from PB |
| A60E | LDY #&20 ; Y=&20: second workspace offset |
| A610 | ADC osword_pb_ptr ; Add PB low byte to get end ptr |
| fall through ↓ | |
Store 16-bit pointer at workspace offset YWrites a 16-bit address to (nfs_workspace)+Y. The low byte comes from A; the high byte is computed from osword_pb_ptr_hi plus carry, supporting pointer arithmetic across page boundaries.
|
||||||||
| A612 | .store_ptr_at_ws_y←1← A607 JSR | |||||||
| STA (nfs_workspace),y ; Store low byte to workspace+Y | ||||||||
| A614 | INY ; Next byte | |||||||
| A615 | LDA osword_pb_ptr_hi ; Load PB pointer high byte | |||||||
| A617 | ADC #0 ; Add carry | |||||||
| A619 | STA (nfs_workspace),y ; Store high byte to workspace+Y+1 | |||||||
| A61B | RTS ; Return | |||||||
| A61C | .osword_12_handler | |||||||
| LDA net_rx_ptr_hi ; Set workspace from RX ptr high | ||||||||
| A61E | STA ws_ptr_hi ; Store to ws_ptr_hi | |||||||
| A620 | LDY #&7f ; Y=&7F: last byte of RX buffer | |||||||
| A622 | LDA (net_rx_ptr),y ; Load port/count from RX buffer | |||||||
| A624 | INY ; Y=&80: set workspace pointer Y=&80 | |||||||
| A625 | STY ws_ptr_lo ; Store as ws_ptr_lo | |||||||
| A627 | TAX ; X = port/count value | |||||||
| A628 | DEX ; X-1: adjust count | |||||||
| A629 | LDY #0 ; Y=0 for copy | |||||||
| A62B | JSR copy_pb_byte_to_ws ; Copy workspace data | |||||||
| A62E | JMP commit_state_byte ; Update state and return | |||||||
| A631 | .osword_13_dispatch | |||||||
| TAX ; X = sub-code | ||||||||
| A632 | CMP #&13 ; Sub-code < &13? | |||||||
| A634 | BCS return_from_osword_13 ; Out of range: return | |||||||
| A636 | LDA osword_13_hi_table,x ; Load handler address high byte | |||||||
| A639 | PHA ; Push high byte | |||||||
| A63A | LDA osword_13_lo_table,x ; Load handler address low byte | |||||||
| A63D | PHA ; Push low byte | |||||||
| A63E | .return_from_osword_13←1← A634 BCS | |||||||
| RTS ; RTS dispatches to handler | ||||||||
| A63F | .osword_13_lo_table←1← A63A LDA | |||||||
| EQUB <(osword_13_read_station-1) ; lo-sub 0: read FS station | ||||||||
| A640 | EQUB <(osword_13_set_station-1) ; lo-sub 1: set FS station | |||||||
| A641 | EQUB <(osword_13_read_ws_pair-1) ; lo-sub 2: read workspace pair | |||||||
| A642 | EQUB <(osword_13_write_ws_pair-1) ; lo-sub 3: write workspace pair | |||||||
| A643 | EQUB <(osword_13_read_prot-1) ; lo-sub 4: read protection mask | |||||||
| A644 | EQUB <(osword_13_write_prot-1) ; lo-sub 5: write protection mask | |||||||
| A645 | EQUB <(osword_13_read_handles-1) ; lo-sub 6: read FCB handles | |||||||
| A646 | EQUB <(osword_13_set_handles-1) ; lo-sub 7: set FCB handles | |||||||
| A647 | EQUB <(osword_13_read_rx_flag-1) ; lo-sub 8: read RX flag | |||||||
| A648 | EQUB <(osword_13_read_rx_port-1) ; lo-sub 9: read RX port | |||||||
| A649 | EQUB <(osword_13_read_error-1) ; lo-sub 10: read error flag | |||||||
| A64A | EQUB <(osword_13_read_context-1) ; lo-sub 11: read context byte | |||||||
| A64B | EQUB <(osword_13_read_csd-1) ; lo-sub 12: read CSD path | |||||||
| A64C | EQUB <(osword_13_write_csd-1) ; lo-sub 13: write CSD path | |||||||
| A64D | EQUB <(osword_13_read_free_bufs-1) ; lo-sub 14: read free buffers | |||||||
| A64E | EQUB <(osword_13_read_ctx_3-1) ; lo-sub 15: read 3 context bytes | |||||||
| A64F | EQUB <(osword_13_write_ctx_3-1) ; lo-sub 16: write 3 context bytes | |||||||
| A650 | EQUB <(osword_13_bridge_query-1) ; lo-sub 17: query bridge status | |||||||
| A651 | .osword_13_hi_table←1← A636 LDA | |||||||
| EQUB >(osword_13_read_station-1) ; hi-sub 0: read FS station | ||||||||
| A652 | EQUB >(osword_13_set_station-1) ; hi-sub 1: set FS station | |||||||
| A653 | EQUB >(osword_13_read_ws_pair-1) ; hi-sub 2: read workspace pair | |||||||
| A654 | EQUB >(osword_13_write_ws_pair-1) ; hi-sub 3: write workspace pair | |||||||
| A655 | EQUB >(osword_13_read_prot-1) ; hi-sub 4: read protection mask | |||||||
| A656 | EQUB >(osword_13_write_prot-1) ; hi-sub 5: write protection mask | |||||||
| A657 | EQUB >(osword_13_read_handles-1) ; hi-sub 6: read FCB handles | |||||||
| A658 | EQUB >(osword_13_set_handles-1) ; hi-sub 7: set FCB handles | |||||||
| A659 | EQUB >(osword_13_read_rx_flag-1) ; hi-sub 8: read RX flag | |||||||
| A65A | EQUB >(osword_13_read_rx_port-1) ; hi-sub 9: read RX port | |||||||
| A65B | EQUB >(osword_13_read_error-1) ; hi-sub 10: read error flag | |||||||
| A65C | EQUB >(osword_13_read_context-1) ; hi-sub 11: read context byte | |||||||
| A65D | EQUB >(osword_13_read_csd-1) ; hi-sub 12: read CSD path | |||||||
| A65E | EQUB >(osword_13_write_csd-1) ; hi-sub 13: write CSD path | |||||||
| A65F | EQUB >(osword_13_read_free_bufs-1) ; hi-sub 14: read free buffers | |||||||
| A660 | EQUB >(osword_13_read_ctx_3-1) ; hi-sub 15: read 3 context bytes | |||||||
| A661 | EQUB >(osword_13_write_ctx_3-1) ; hi-sub 16: write 3 context bytes | |||||||
| A662 | EQUB >(osword_13_bridge_query-1) ; hi-sub 17: query bridge status | |||||||
OSWORD &13 sub 0: read file server stationReturns the current file server station and network numbers in PB[1..2]. If the NFS is not active (l0d6c bit 7 clear), returns zero in PB[0] instead. |
|
| A663 | .osword_13_read_station |
| BIT fs_flags ; NFS active? | |
| A666 | BMI read_station_bytes ; Yes: read station data |
| A668 | .nfs_inactive_exit←1← A679 BPL |
| JMP return_zero_in_pb ; No: return zero | |
| A66B | .read_station_bytes←1← A666 BMI |
| LDY #2 ; Y=2: copy 2 bytes | |
| A66D | .loop_copy_station←1← A673 BNE |
| LDA fs_server_base,y ; Load station byte | |
| A670 | STA (osword_pb_ptr),y ; Store to PB[Y] |
| A672 | DEY ; Previous byte |
| A673 | BNE loop_copy_station ; Loop for bytes 2..1 |
| A675 | RTS ; Return |
OSWORD &13 sub 1: set file server stationSets the file server station and network numbers from PB[1..2]. Processes all FCBs, then scans the 16-entry FCB table to reassign handles matching the new station. If the NFS is not active, returns zero. |
|
| A676 | .osword_13_set_station |
| BIT fs_flags ; NFS active? | |
| A679 | BPL nfs_inactive_exit ; No: return zero |
| A67B | LDY #0 ; Y=0 for process_all_fcbs |
| A67D | JSR process_all_fcbs ; Close all open FCBs |
| A680 | LDY #2 ; Y=2: copy 2 bytes |
| A682 | .loop_store_station←1← A688 BNE |
| LDA (osword_pb_ptr),y ; Load new station byte from PB | |
| A684 | STA fs_server_base,y ; Store to l0dff |
| A687 | DEY ; Previous byte |
| A688 | BNE loop_store_station ; Loop for bytes 2..1 |
| A68A | JSR clear_if_station_match ; Clear handles if station matches |
| A68D | LDX #&0f ; X=&0F: scan 16 FCB entries |
| A68F | .scan_fcb_entry←1← A6E8 BPL |
| LDA chan_status,x ; Load FCB flags | |
| A692 | TAY ; Save flags in Y |
| A693 | AND #2 ; Test bit 1 (FCB allocated?) |
| A695 | BEQ next_fcb_entry ; No: skip to next entry |
| A697 | TYA ; Restore flags |
| A698 | AND #&df ; Clear bit 5 (pending update) |
| A69A | STA chan_status,x ; Store updated flags |
| A69D | TAY ; Save in Y |
| A69E | JSR match_station_net ; Does FCB match new station? |
| A6A1 | BNE next_fcb_entry ; No match: skip to next |
| A6A3 | CLC ; Clear carry for ADC |
| A6A4 | TYA ; Restore flags |
| A6A5 | AND #4 ; Test bit 2 (handle 1 active?) |
| A6A7 | BEQ check_handle_2 ; No: check handle 2 |
| A6A9 | TYA ; Restore flags |
| A6AA | ORA #&20 ; Set bit 5 (handle reassigned) |
| A6AC | TAY ; Save updated flags |
| A6AD | LDA fcb_net_or_port,x ; Get FCB high byte |
| A6B0 | STA fs_urd_handle ; Store as handle 1 station |
| A6B3 | TXA ; FCB index |
| A6B4 | ADC #&20 ; Add &20 for FCB table offset |
| A6B6 | STA handle_1_fcb ; Store as handle 1 FCB index |
| A6B9 | .check_handle_2←1← A6A7 BEQ |
| TYA ; Restore flags | |
| A6BA | AND #8 ; Test bit 3 (handle 2 active?) |
| A6BC | BEQ check_handle_3 ; No: check handle 3 |
| A6BE | TYA ; Restore flags |
| A6BF | ORA #&20 ; Set bit 5 |
| A6C1 | TAY ; Save updated flags |
| A6C2 | LDA fcb_net_or_port,x ; Get FCB high byte |
| A6C5 | STA fs_csd_handle ; Store as handle 2 station |
| A6C8 | TXA ; FCB index |
| A6C9 | ADC #&20 ; Add &20 for FCB table offset |
| A6CB | STA handle_2_fcb ; Store as handle 2 FCB index |
| A6CE | .check_handle_3←1← A6BC BEQ |
| TYA ; Restore flags | |
| A6CF | AND #&10 ; Test bit 4 (handle 3 active?) |
| A6D1 | BEQ store_updated_status ; No: store final flags |
| A6D3 | TYA ; Restore flags |
| A6D4 | ORA #&20 ; Set bit 5 |
| A6D6 | TAY ; Save updated flags |
| A6D7 | LDA fcb_net_or_port,x ; Get FCB high byte |
| A6DA | STA fs_lib_handle ; Store as handle 3 station |
| A6DD | TXA ; FCB index |
| A6DE | ADC #&20 ; Add &20 for FCB table offset |
| A6E0 | STA handle_3_fcb ; Store as handle 3 FCB index |
| A6E3 | .store_updated_status←1← A6D1 BEQ |
| TYA ; Store final flags for this FCB | |
| A6E4 | STA chan_status,x ; Update l1060[X] |
| A6E7 | .next_fcb_entry←2← A695 BEQ← A6A1 BNE |
| DEX ; Next FCB entry | |
| A6E8 | BPL scan_fcb_entry ; Loop for all 16 entries |
| A6EA | RTS ; Return |
OSWORD &13 sub 12: read CSD pathReads 5 current selected directory path bytes from the RX workspace at offset &1B into PB[1..5]. Sets carry clear to select the workspace-to-PB copy direction. |
|
| A6EB | .osword_13_read_csd |
| CLC ; C=0: workspace-to-PB direction | |
| A6EC | BCC setup_csd_copy ; Skip SEC ALWAYS branch |
OSWORD &13 sub 13: write CSD pathWrites 5 current selected directory path bytes from PB[1..5] into the RX workspace at offset &1B. Sets carry to select the PB-to-workspace copy direction. |
|
| A6EE | .osword_13_write_csd |
| SEC ; C=1: PB-to-workspace direction | |
| A6EF | .setup_csd_copy←1← A6EC BCC |
| LDA #&1b ; Workspace offset &1B | |
| A6F1 | STA ws_ptr_lo ; Set ws_ptr_lo |
| A6F3 | LDA net_rx_ptr_hi ; Page from RX pointer high byte |
| A6F5 | STA ws_ptr_hi ; Set ws_ptr_hi |
| A6F7 | LDY #1 ; Y=1: first PB data byte |
| A6F9 | LDX #5 ; X=5: copy 5 bytes |
| fall through ↓ | |
Conditionally copy parameter block byte to workspaceIf carry is set, loads a byte from the OSWORD parameter block at offset Y; if clear, uses the value already in A. Stores the result to workspace at the current offset. Decrements X and loops until the requested byte count is transferred.
|
||||||||
| A6FB | .copy_pb_byte_to_ws←5← A5A2 JSR← A5E9 JSR← A62B JSR← A707 BPL← A714 BCC | |||||||
| BCC copy_ws_byte_to_pb ; C=0: skip PB-to-WS copy | ||||||||
| A6FD | LDA (osword_pb_ptr),y ; C=1: load from parameter block | |||||||
| A6FF | STA (ws_ptr_lo),y ; Store to workspace | |||||||
| A701 | .copy_ws_byte_to_pb←1← A6FB BCC | |||||||
| LDA (ws_ptr_lo),y ; Load from workspace | ||||||||
| A703 | STA (osword_pb_ptr),y ; Store to parameter block | |||||||
| A705 | INY ; Next byte | |||||||
| A706 | DEX ; Count down | |||||||
| A707 | BPL copy_pb_byte_to_ws ; Loop for all bytes | |||||||
| A709 | RTS ; Return | |||||||
OSWORD &13 sub 2: read workspace byte pairReads 2 bytes from the NFS workspace page starting at offset 1 into PB[1..2]. Uses nfs_workspace_hi as the page and copy_pb_byte_to_ws with carry clear for the workspace-to-PB direction. |
|
| A70A | .osword_13_read_ws_pair |
| LDA nfs_workspace_hi ; Load workspace page high byte | |
| A70C | STA ws_ptr_hi ; Set ws_ptr_hi |
| A70E | INY ; Y=1 |
| A70F | TYA ; A=1 |
| A710 | STA ws_ptr_lo ; Set ws_ptr_lo = 1 |
| A712 | TAX ; X=1: copy 2 bytes |
| A713 | CLC ; C=0: workspace-to-PB direction |
| A714 | BCC copy_pb_byte_to_ws ; Copy via copy_pb_byte_to_ws ALWAYS branch |
OSWORD &13 sub 3: write workspace byte pairWrites 2 bytes from PB[1..2] into the NFS workspace at offsets 2 and 3. Then calls init_bridge_poll and conditionally clears the workspace byte if the bridge status changed. |
|
| A716 | .osword_13_write_ws_pair |
| INY ; Y=1: first PB data byte | |
| A717 | LDA (osword_pb_ptr),y ; Load PB[1] |
| A719 | INY ; Y=2 |
| A71A | STA (nfs_workspace),y ; Store to (nfs_workspace)+2 |
| A71C | LDA (osword_pb_ptr),y ; Load PB[2] |
| A71E | INY ; Y=3 |
| A71F | STA (nfs_workspace),y ; Store to (nfs_workspace)+3 |
| A721 | JSR init_bridge_poll ; Reinitialise bridge routing |
| A724 | EOR (nfs_workspace),y ; Compare result with workspace |
| A726 | BNE return_from_write_ws_pair ; Different: leave unchanged |
| A728 | STA (nfs_workspace),y ; Same: clear workspace byte |
| A72A | .return_from_write_ws_pair←1← A726 BNE |
| RTS ; Return | |
OSWORD &13 sub 4: read protection maskReturns the current protection mask (ws_0d68) in PB[1]. |
|
| A72B | .osword_13_read_prot |
| LDA ws_0d68 ; Load protection mask | |
| A72E | JMP store_a_to_pb_1 ; Store to PB[1] and return |
OSWORD &13 sub 5: write protection maskSets the protection mask from PB[1] via store_prot_mask. |
|
| A731 | .osword_13_write_prot |
| INY ; Y=1: PB data offset | |
| A732 | LDA (osword_pb_ptr),y ; Load new mask from PB[1] |
| A734 | JMP store_prot_mask ; Store via store_prot_mask |
OSWORD &13 sub 6: read FCB handle infoReturns the 3-byte FCB handle/port data from l1071[1..3] in PB[1..3]. If the NFS is not active, returns zero in PB[0]. |
|
| A737 | .osword_13_read_handles |
| BIT fs_flags ; NFS active? | |
| A73A | BPL return_zero_in_pb ; No: return zero |
| A73C | LDY #3 ; Y=3: copy 3 bytes |
| A73E | .loop_copy_handles←1← A744 BNE |
| LDA fs_lib_flags,y ; Load handle byte | |
| A741 | STA (osword_pb_ptr),y ; Store to PB[Y] |
| A743 | DEY ; Previous byte |
| A744 | BNE loop_copy_handles ; Loop for bytes 3..1 |
| A746 | RTS ; Return |
OSWORD &13 sub 7: set FCB handlesValidates and assigns up to 3 FCB handles from PB[1..3]. Each handle value (&20-&2F) indexes the l1010/l1040 tables. For valid handles with bit 2 set in l1040, stores the station to l0e01+Y and FCB index to l1071+Y, then updates flag bits across all FCB entries via update_fcb_flag_bits. |
|
| A747 | .osword_13_set_handles |
| BIT fs_flags ; NFS active? | |
| A74A | BMI start_set_handles ; Yes: process handles |
| A74C | .return_zero_in_pb←2← A668 JMP← A73A BPL |
| LDA #0 ; A=0 | |
| A74E | TAY ; Y=&00 |
| A74F | STA (osword_pb_ptr),y ; Store 0 to PB[0] |
| A751 | RTS ; Return |
| A752 | .start_set_handles←1← A74A BMI |
| LDY #1 ; Y=1: first handle in PB | |
| A754 | .validate_handle←1← A794 BNE |
| LDA (osword_pb_ptr),y ; Load handle value from PB[Y] | |
| A756 | CMP #&20 ; Must be >= &20 |
| A758 | BCC handle_invalid ; Below range: invalid |
| A75A | CMP #&30 ; Must be < &30 |
| A75C | BCS handle_invalid ; Above range: invalid |
| A75E | TAX ; X = handle value |
| A75F | LDA fcb_attr_or_count_mid,x ; Load l1010[handle] |
| A762 | BNE check_handle_alloc ; Non-zero: FCB exists |
| A764 | .handle_invalid←3← A758 BCC← A75C BCS← A770 BEQ |
| LDA #0 ; Invalid: store 0 to PB[0] | |
| A766 | TAX ; X=&00 |
| A767 | STA (osword_pb_ptr,x) ; Clear PB[0] status |
| A769 | BEQ next_handle_slot ; Skip to next handle ALWAYS branch |
| A76B | .check_handle_alloc←1← A762 BNE |
| LDA fcb_flags,x ; Load l1040[handle] flags | |
| A76E | AND #2 ; Test bit 1 (allocated?) |
| A770 | BEQ handle_invalid ; Not allocated: invalid |
| A772 | TXA ; X = handle value |
| A773 | STA fs_lib_flags,y ; Store handle to l1071+Y |
| A776 | LDA fcb_attr_or_count_mid,x ; Load station from l1010 |
| A779 | STA fs_server_net,y ; Store station to l0e01+Y |
| A77C | CPY #1 ; Is this handle 1 (Y=1)? |
| A77E | BNE assign_handle_2 ; No: check handle 2 |
| A780 | TYA ; Save Y |
| A781 | PHA ; Push Y |
| A782 | LDY #4 ; Bit mask &04 for handle 1 |
| A784 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| A787 | PLA ; Restore Y |
| A788 | TAY ; Back to Y |
| A789 | LDA fcb_flags,x ; Reload l1040 flags |
| A78C | ORA #&24 ; Set bits 2+5 (active+updated) |
| A78E | STA fcb_flags,x ; Store updated flags |
| A791 | .next_handle_slot←3← A769 BEQ← A7AD BNE← A7C0 BNE |
| INY ; Next handle slot | |
| A792 | CPY #4 ; Done all 3 handles? |
| A794 | BNE validate_handle ; No: process next handle |
| A796 | DEY ; Y=3 for return |
| A797 | RTS ; Return |
| A798 | .assign_handle_2←1← A77E BNE |
| CPY #2 ; Is this handle 2 (Y=2)? | |
| A79A | BNE assign_handle_3 ; No: must be handle 3 |
| A79C | TYA ; Save Y |
| A79D | PHA ; Push Y |
| A79E | LDY #8 ; Bit mask &08 for handle 2 |
| A7A0 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| A7A3 | PLA ; Restore Y |
| A7A4 | TAY ; Back to Y |
| A7A5 | LDA fcb_flags,x ; Reload l1040 flags |
| A7A8 | ORA #&28 ; Set bits 3+5 (active+updated) |
| A7AA | STA fcb_flags,x ; Store updated flags |
| A7AD | BNE next_handle_slot ; Next handle slot ALWAYS branch |
| A7AF | .assign_handle_3←1← A79A BNE |
| TYA ; Handle 3: save Y | |
| A7B0 | PHA ; Push Y |
| A7B1 | LDY #&10 ; Bit mask &10 for handle 3 |
| A7B3 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| A7B6 | PLA ; Restore Y |
| A7B7 | TAY ; Back to Y |
| A7B8 | LDA fcb_flags,x ; Reload l1040 flags |
| A7BB | ORA #&30 ; Set bits 4+5 (active+updated) |
| A7BD | STA fcb_flags,x ; Store updated flags |
| A7C0 | BNE next_handle_slot ; Next handle slot ALWAYS branch |
Update FCB flag bits across all entriesScans all 16 FCB entries in l1060. For each entry with bit 6 set, tests the Y-specified bit mask: if matching, ORs bit 5 into the flags; if not, leaves bit 5 clear. In both cases, inverts and clears the tested bits. Preserves X.
|
||||||
| A7C2 | .update_fcb_flag_bits←3← A784 JSR← A7A0 JSR← A7B3 JSR | |||||
| TXA ; Save X (current FCB index) | ||||||
| A7C3 | PHA ; Push X | |||||
| A7C4 | LDX #&0f ; X=&0F: scan 16 FCB entries | |||||
| A7C6 | .loop_scan_fcb_flags←1← A7E2 BPL | |||||
| LDA chan_status,x ; Load FCB flags | ||||||
| A7C9 | ROL ; Shift bits 6-7 into bits 7-0 | |||||
| A7CA | ROL ; Bit 6 now in bit 7 (N flag) | |||||
| A7CB | BPL next_flag_entry ; Bit 6 clear: skip entry | |||||
| A7CD | TYA ; Restore Y (bit mask) | |||||
| A7CE | AND chan_status,x ; Test mask bits against flags | |||||
| A7D1 | BEQ no_flag_match ; Zero: no matching bits | |||||
| A7D3 | TYA ; Matching: restore Y | |||||
| A7D4 | ORA #&20 ; Set bit 5 (updated) | |||||
| A7D6 | BNE clear_flag_bits ; Skip clear path ALWAYS branch | |||||
| A7D8 | .no_flag_match←1← A7D1 BEQ | |||||
| TYA ; No match: restore Y | ||||||
| A7D9 | .clear_flag_bits←1← A7D6 BNE | |||||
| EOR #&ff ; Invert all bits | ||||||
| A7DB | AND chan_status,x ; Clear tested bits in flags | |||||
| A7DE | STA chan_status,x ; Store updated flags | |||||
| A7E1 | .next_flag_entry←1← A7CB BPL | |||||
| DEX ; Next FCB entry | ||||||
| A7E2 | BPL loop_scan_fcb_flags ; Loop for all 16 entries | |||||
| A7E4 | PLA ; Restore original X | |||||
| A7E5 | TAX ; Back to X | |||||
| A7E6 | RTS ; Return | |||||
OSWORD &13 sub 8: read RX control block flagReturns byte 5 of the current RX control block in PB[1]. |
|
| A7E7 | .osword_13_read_rx_flag |
| LDY #5 ; Y=5: RX control block offset | |
| A7E9 | LDA (net_rx_ptr),y ; Load (net_rx_ptr)+5 |
| A7EB | LDY #0 ; Y=0 |
| A7ED | JMP store_a_to_pb_1 ; Store to PB[1] and return |
OSWORD &13 sub 9: read RX port byteReturns byte &7F of the current RX control block in PB[1], and stores &80 in PB[2]. |
|
| A7F0 | .osword_13_read_rx_port |
| LDY #&7f ; Y=&7F: port byte offset | |
| A7F2 | LDA (net_rx_ptr),y ; Load (net_rx_ptr)+&7F |
| A7F4 | LDY #1 ; Y=1 |
| A7F6 | STA (osword_pb_ptr),y ; Store to PB[1] |
| A7F8 | INY ; Y=&02 |
| A7F9 | LDA #&80 ; A=&80 |
| A7FB | STA (osword_pb_ptr),y ; Store &80 to PB[2] |
| A7FD | RTS ; Return |
OSWORD &13 sub 10: read error flagReturns the error flag (l0e09) in PB[1]. |
|
| A7FE | .osword_13_read_error |
| LDA fs_last_error ; Load error flag | |
| A801 | JMP store_a_to_pb_1 ; Store to PB[1] and return |
OSWORD &13 sub 11: read context byteReturns the context byte (l0d6d) in PB[1]. |
|
| A804 | .osword_13_read_context |
| LDA net_context ; Load context byte | |
| A807 | JMP store_a_to_pb_1 ; Store to PB[1] and return |
OSWORD &13 sub 14: read printer buffer free spaceReturns the number of free bytes remaining in the printer spool buffer (&6F minus spool_buf_idx) in PB[1]. The buffer starts at offset &25 and can hold up to &4A bytes of spool data. |
|
| A80A | .osword_13_read_free_bufs |
| LDA #&6f ; Total buffers = &6F | |
| A80C | SEC ; Subtract used count |
| A80D | SBC spool_buf_idx ; Free = &6F - l0d6b |
| A810 | .store_a_to_pb_1←4← A72E JMP← A7ED JMP← A801 JMP← A807 JMP |
| INY ; Y=1 | |
| A811 | STA (osword_pb_ptr),y ; Store A to PB[1] |
| A813 | RTS ; Return |
OSWORD &13 sub 15: read retry countsReturns the three retry count values in PB[1..3]: PB[1] = transmit retry count (default &FF = 255), PB[2] = receive poll count (default &28 = 40), PB[3] = machine peek retry count (default &0A = 10). Setting transmit retries to 0 means retry forever. |
|
| A814 | .osword_13_read_ctx_3←1← A81C BNE |
| INY ; Next byte offset | |
| A815 | LDA net_context,y ; Load l0d6d[Y] |
| A818 | STA (osword_pb_ptr),y ; Store to PB[Y] |
| A81A | CPY #3 ; Done 3 bytes? |
| A81C | BNE osword_13_read_ctx_3 ; No: loop |
| A81E | RTS ; Return |
OSWORD &13 sub 16: write retry countsSets the three retry count values from PB[1..3]: PB[1] = transmit retry count, PB[2] = receive poll count, PB[3] = machine peek retry count. |
|
| A81F | .osword_13_write_ctx_3←1← A827 BNE |
| INY ; Next byte offset | |
| A820 | LDA (osword_pb_ptr),y ; Load PB[Y] |
| A822 | STA net_context,y ; Store to l0d6d[Y] |
| A825 | CPY #3 ; Done 3 bytes? |
| A827 | BNE osword_13_write_ctx_3 ; No: loop |
| A829 | RTS ; Return |
OSWORD &13 sub 17: query bridge statusCalls init_bridge_poll, then returns the bridge status. If l0d72 is &FF (no bridge), stores 0 in PB[0]. Otherwise stores l0d72 in PB[1] and conditionally updates PB[3] based on station comparison. |
|
| A82A | .osword_13_bridge_query |
| JSR init_bridge_poll ; Poll for bridge | |
| A82D | LDY #0 ; Y=0 |
| A82F | LDA bridge_status ; Load bridge status |
| A832 | CMP #&ff ; Is it &FF (no bridge)? |
| A834 | BNE bridge_found ; No: bridge found |
| A836 | TYA ; A=&00 |
| A837 | STA (osword_pb_ptr),y ; PB[0] = 0 (no bridge) |
| A839 | RTS ; Return |
| A83A | .bridge_found←1← A834 BNE |
| INY ; Y=1 | |
| A83B | STA (osword_pb_ptr),y ; PB[1] = bridge status |
| A83D | INY ; Y=2 |
| A83E | INY ; Y=3 |
| A83F | LDA (osword_pb_ptr),y ; Load PB[3] (caller value) |
| A841 | BEQ use_default_station ; Zero: use default station |
| A843 | .compare_bridge_status |
| EOR bridge_status ; Compare with bridge status | |
| A846 | BNE return_from_bridge_query ; Different: return unchanged |
| A848 | BEQ store_bridge_station ; Same: confirm station ALWAYS branch |
| A84A | .use_default_station←1← A841 BEQ |
| LDA fs_server_net ; Load default from l0e01 | |
| A84D | .store_bridge_station←1← A848 BEQ |
| STA (osword_pb_ptr),y ; Store to PB[3] | |
| A84F | .return_from_bridge_query←1← A846 BNE |
| RTS ; Return | |
| ; Bridge discovery init data (24 bytes) | |
| ; Two 12-byte templates copied simultaneously by | |
| ; loop_copy_bridge_init. X counts down &0B to 0, | |
| ; copying the TXCB template into &C0. Y counts up | |
| ; &18 to &23, copying the RXCB data into workspace | |
| ; via bridge_ws_init_data (compare_bridge_status+1) | |
| ; + Y to reach the RXCB data area. | |
| ; The TX broadcasts "BRIDGE" as immediate data on | |
| ; port &9C to all stations (FF.FF). The RX listens | |
| ; on the same port for a reply into the bridge | |
| ; status bytes at &0D72. | |
| A850 | .bridge_txcb_init_table←1← A87D LDA |
| EQUB &82 ; TX 0: ctrl = &82 (immediate mode) | |
| A851 | EQUB &9C ; TX 1: port = &9C (bridge discovery) |
| A852 | EQUB &FF ; TX 2: dest station = &FF (broadcast) |
| A853 | EQUB &FF ; TX 3: dest network = &FF (all nets) |
| A854 | EQUS "BRIDGE" ; TX 4-9: immediate data payload |
| A85A | EQUB &9C ; TX 10: &9C (port echo) |
| A85B | EQUB &00 ; TX 11: &00 (terminator) |
| A85C | .bridge_rxcb_init_data |
| EQUB &7F ; RX 0: ctrl = &7F (receive) | |
| A85D | EQUB &9C ; RX 1: port = &9C (bridge discovery) |
| A85E | EQUB &00 ; RX 2: station = &00 (any) |
| A85F | EQUB &00 ; RX 3: network = &00 (any) |
| A860 | EQUB &72 ; RX 4: buf start lo (&72) |
| A861 | EQUB &0D ; RX 5: buf start hi (&0D) -> &0D72 |
| A862 | EQUB &FF ; RX 6: extended addr fill (&FF) |
| A863 | EQUB &FF ; RX 7: extended addr fill (&FF) |
| A864 | EQUB &74 ; RX 8: buf end lo (&74) |
| A865 | EQUB &0D ; RX 9: buf end hi (&0D) -> &0D74 |
| A866 | EQUB &FF ; RX 10: extended addr fill (&FF) |
| A867 | EQUB &FF ; RX 11: extended addr fill (&FF) |
Initialise Econet bridge routing tableChecks the bridge status byte: if &FF (uninitialised), broadcasts a bridge query packet and polls for replies. Each reply adds a network routing entry to the bridge table. Skips the broadcast if the table has already been populated from a previous call. |
|
| A868 | .init_bridge_poll←4← 8DFE JSR← A09C JSR← A721 JSR← A82A JSR |
| LDA bridge_status ; Check bridge status | |
| A86B | CMP #&ff ; Is it &FF (uninitialised)? |
| A86D | BNE return_from_bridge_poll ; No: bridge already active, return |
| A86F | TYA ; Save Y |
| A870 | PHA ; Preserve Y on stack |
| A871 | LDY #&18 ; Y=&18: workspace offset for init |
| A873 | LDX #&0b ; X=&0B: 12 bytes to copy |
| A875 | ROR econet_flags ; Rotate l0d61 right (save flag) |
| A878 | .loop_copy_bridge_init←1← A884 BPL |
| LDA bridge_ws_init_data,y ; Load init data byte | |
| A87B | STA (nfs_workspace),y ; Store to workspace |
| A87D | LDA bridge_txcb_init_table,x ; Load TXCB template byte |
| A880 | STA txcb_ctrl,x ; Store to TX control block |
| A882 | INY ; Next workspace byte |
| A883 | DEX ; Next template byte |
| A884 | BPL loop_copy_bridge_init ; Loop for all 12 bytes |
| A886 | STX bridge_status ; Store X (-1) as bridge counter |
| A889 | ROL econet_flags ; Restore l0d61 flag |
| A88C | .loop_wait_ws_status←2← A88F BCC← A8BE BPL |
| ASL ws_0d60 ; Shift ws_0d60 left (check status) | |
| A88F | BCC loop_wait_ws_status ; C=0: status clear, retry |
| A891 | LDA #&82 ; Control byte &82 for TX |
| A893 | STA txcb_ctrl ; Set in TX control block |
| A895 | LDA #&c0 ; Data block at &00C0 |
| A897 | STA nmi_tx_block ; Set NMI TX block low |
| A899 | LDA #0 ; High byte = 0 (page 0) |
| A89B | STA nmi_tx_block_hi ; Set NMI TX block high |
| A89D | JSR tx_begin ; Begin Econet transmission |
| A8A0 | .loop_wait_tx_done←1← A8A2 BMI |
| BIT txcb_ctrl ; Test TX control block bit 7 | |
| A8A2 | BMI loop_wait_tx_done ; Negative: TX still in progress |
| A8A4 | TAX ; X = result status |
| A8A5 | PHA ; Save TX status |
| A8A6 | LDA osword_pb_ptr_hi ; Save PB pointer high |
| A8A8 | PHA ; Push for later restore |
| A8A9 | LDX osword_pb_ptr ; X = PB pointer low |
| A8AB | LDA #osbyte_vsync ; OSBYTE &13: wait for VSYNC |
| A8AD | JSR osbyte ; Wait for vertical sync |
| A8B0 | PLA ; Restore PB pointer high |
| A8B1 | STA osword_pb_ptr_hi ; Restore to osword_pb_ptr_hi |
| A8B3 | PLA ; Restore TX status |
| A8B4 | TAX ; Back to X |
| A8B5 | LDY #&18 ; Y=&18: check workspace response |
| A8B7 | LDA (nfs_workspace),y ; Load bridge response |
| A8B9 | BMI bridge_responded ; Negative: bridge responded |
| A8BB | JSR advance_x_by_8 ; Advance retry counter by 8 |
| A8BE | BPL loop_wait_ws_status ; Positive: retry poll loop |
| A8C0 | .bridge_responded←1← A8B9 BMI |
| LDA #&3f ; Set response to &3F (OK) | |
| A8C2 | STA (nfs_workspace),y ; Store to workspace |
| A8C4 | PLA ; Restore saved Y |
| A8C5 | TAY ; Back to Y |
| A8C6 | LDA bridge_status ; Load bridge status |
| A8C9 | TAX ; X = bridge status |
| A8CA | EOR #&ff ; Complement status |
| A8CC | BEQ return_from_bridge_poll ; Status was &FF: return (no bridge) |
| A8CE | TXA ; Return bridge station in A |
| A8CF | .return_from_bridge_poll←4← A86D BNE← A8CC BEQ← A8D7 BPL← A943 BEQ |
| RTS ; Return | |
| A8D0 | .osword_14_handler |
| CMP #1 ; Compare sub-code with 1 | |
| A8D2 | BCS handle_tx_request ; Sub-code >= 1: handle TX request |
| A8D4 | BIT fs_flags ; Test station active flag |
| A8D7 | BPL return_from_bridge_poll ; Not active: return |
| A8D9 | LDY #&23 ; Y=&23: workspace offset for params |
| A8DB | JSR mask_owner_access ; Set owner access mask |
| A8DE | .loop_copy_txcb_init←1← A8EB BNE |
| LDA init_txcb,y ; Load TXCB init byte | |
| A8E1 | BNE store_txcb_init_byte ; Non-zero: use template value |
| A8E3 | LDA txcb_default_base,y ; Zero: use workspace default value |
| A8E6 | .store_txcb_init_byte←1← A8E1 BNE |
| STA (nfs_workspace),y ; Store to workspace | |
| A8E8 | DEY ; Next byte down |
| A8E9 | CPY #&17 ; Until Y reaches &17 |
| A8EB | BNE loop_copy_txcb_init ; Loop for all bytes |
| A8ED | INY ; Y=&18 (INY from &17) |
| A8EE | STY net_tx_ptr ; Set net_tx_ptr low byte |
| A8F0 | JSR store_osword_pb_ptr ; Store PB pointer to workspace |
| A8F3 | LDY #2 ; Y=2: parameter offset |
| A8F5 | LDA #&90 ; Control byte &90 |
| A8F7 | STA escapable ; Set escapable flag |
| A8F9 | STA (osword_pb_ptr),y ; Store control byte to PB |
| A8FB | INY ; Y=&03 |
| A8FC | INY ; Y=&04 |
| A8FD | .loop_copy_ws_to_pb←1← A905 BNE |
| LDA osword_ws_base,y ; Load workspace data | |
| A900 | STA (osword_pb_ptr),y ; Store to parameter block |
| A902 | INY ; Next byte |
| A903 | CPY #7 ; Until Y reaches 7 |
| A905 | BNE loop_copy_ws_to_pb ; Loop for 3 bytes (Y=4,5,6) |
| A907 | LDA nfs_workspace_hi ; Set TX pointer high byte |
| A909 | STA net_tx_ptr_hi ; Store to net_tx_ptr_hi |
| A90B | CLI ; Enable interrupts |
| A90C | JSR send_net_packet ; Send the network packet |
| A90F | LDY #&20 ; Y=&20: workspace offset |
| A911 | LDA #&ff ; Set to &FF (pending) |
| A913 | STA (nfs_workspace),y ; Mark send pending in workspace |
| A915 | INY ; Y=&21 |
| A916 | STA (nfs_workspace),y ; Also mark offset &21 |
| A918 | LDY #&19 ; Y=&19: control offset |
| A91A | LDA #&90 ; Control byte &90 |
| A91C | STA (nfs_workspace),y ; Store to workspace |
| A91E | DEY ; Y=&18: RX control offset Y=&18 |
| A91F | LDA #&7f ; Control byte &7F |
| A921 | STA (nfs_workspace),y ; Store RX control |
| A923 | JMP wait_net_tx_ack ; Wait for TX acknowledgement |
| A926 | .handle_tx_request←1← A8D2 BCS |
| PHP ; Save processor flags | |
| A927 | LDY #1 ; Y=1: PB offset for station |
| A929 | LDA (osword_pb_ptr),y ; Load station number from PB |
| A92B | TAX ; X = station number |
| A92C | INY ; Y=&02 |
| A92D | LDA (osword_pb_ptr),y ; Load network number from PB |
| A92F | INY ; Y=3: workspace start offset Y=&03 |
| A930 | STY ws_ptr_lo ; Store Y as ws_ptr_lo |
| A932 | LDY #&72 ; Y=&72: workspace offset for dest |
| A934 | STA (net_rx_ptr),y ; Store network to workspace |
| A936 | DEY ; Y=&71 |
| A937 | TXA ; A = station (from X) |
| A938 | STA (net_rx_ptr),y ; Store station to workspace |
| A93A | PLP ; Restore flags from PHP |
| A93B | BNE handle_burst_xfer ; Non-zero sub-code: handle burst |
| A93D | .loop_send_pb_chars←1← A956 BNE |
| LDY ws_ptr_lo ; Load current offset | |
| A93F | INC ws_ptr_lo ; Advance offset for next byte |
| A941 | LDA (osword_pb_ptr),y ; Load next char from PB |
| A943 | BEQ return_from_bridge_poll ; Zero: end of data, return |
| A945 | LDY #&7d ; Y=&7D: workspace char offset |
| A947 | STA (net_rx_ptr),y ; Store char to RX buffer |
| A949 | PHA ; Save char for later test |
| A94A | JSR init_ws_copy_wide ; Init workspace copy for wide xfer |
| A94D | JSR enable_irq_and_poll ; Enable IRQ and send packet |
| A950 | .loop_bridge_tx_delay←1← A951 BNE |
| DEX ; Delay countdown | |
| A951 | BNE loop_bridge_tx_delay ; Loop for short delay |
| A953 | PLA ; Restore char |
| A954 | EOR #&0d ; Test if char was CR (&0D) |
| A956 | BNE loop_send_pb_chars ; Not CR: send next char |
| A958 | RTS ; CR sent: return |
| A959 | .handle_burst_xfer←1← A93B BNE |
| JSR init_ws_copy_wide ; Init workspace for wide copy | |
| A95C | LDY #&7b ; Y=&7B: workspace offset |
| A95E | LDA (net_rx_ptr),y ; Load buffer size |
| A960 | ADC #3 ; Add 3 for header |
| A962 | STA (net_rx_ptr),y ; Store adjusted size |
| fall through ↓ | |
Enable interrupts and send Econet packetExecutes CLI to re-enable interrupts, then falls through to send_net_packet. Used after a sequence that ran with interrupts disabled to ensure the packet is sent with normal interrupt handling active. |
|
| A964 | .enable_irq_and_poll←1← A94D JSR |
| CLI ; Enable interrupts | |
| A965 | JMP send_net_packet ; Send packet and return |
NETV handler: OSWORD dispatchInstalled as the NETV handler via write_vector_entry. Saves all registers, reads the OSWORD number from the stack, and dispatches OSWORDs 0-8 via push_osword_handler_addr. OSWORDs >= 9 are ignored (registers restored, RTS returns to MOS). Address stored at netv_handler_addr (&8E74) in the extended vector data area. |
|
| A968 | .netv_handler |
| PHP ; Save processor flags | |
| A969 | PHA ; Save A |
| A96A | TXA ; Save X |
| A96B | PHA ; Push X |
| A96C | TYA ; Save Y |
| A96D | PHA ; Push Y |
| A96E | TSX ; Get stack pointer |
| A96F | LDA stack_page_3,x ; Read OSWORD number from stack |
| A972 | CMP #9 ; OSWORD >= 9? |
| A974 | BCS restore_regs_return ; Yes: out of range, restore + return |
| A976 | TAX ; X = OSWORD number |
| A977 | JSR push_osword_handler_addr ; Push handler address for dispatch |
| A97A | .restore_regs_return←1← A974 BCS |
| PLA ; Restore Y | |
| A97B | TAY ; Back to Y |
| A97C | PLA ; Restore X |
| A97D | TAX ; Back to X |
| A97E | PLA ; Restore A |
| A97F | PLP ; Restore processor flags |
| A980 | RTS ; RTS dispatches via pushed address |
Push OSWORD handler address for RTS dispatchIndexes the OSWORD handler dispatch table using the current OSWORD number to push the handler's address (hi/lo) onto the stack. Reloads the OSWORD number from osbyte_a_copy so the dispatched handler can identify the specific call. |
|
| A981 | .push_osword_handler_addr←1← A977 JSR |
| LDA osword_handler_hi_table,x ; Load handler high byte from table | |
| A984 | PHA ; Push for RTS dispatch |
| A985 | LDA osword_handler_lo_table,x ; Load handler low byte from table |
| A988 | PHA ; Push for RTS dispatch |
| A989 | LDA osbyte_a_copy ; Reload OSWORD number for handler |
| A98B | RTS ; RTS will dispatch to handler |
| ; OSWORD handler dispatch table | |
| ; 9-entry PHA/PHA/RTS table for OSWORD numbers | |
| ; 0-8. push_osword_handler_addr indexes by the | |
| ; OSWORD number, pushes the handler address-1, | |
| ; then RTS dispatches to the handler with the | |
| ; OSWORD number reloaded in A. | |
| A98C | .osword_handler_lo_table←1← A985 LDA |
| EQUB <(dispatch_rts-1) ; lo OSWORD 0: no-op (RTS) | |
| A98D | EQUB <(netv_print_data-1) ; lo OSWORD 1: printer spool data |
| A98E | EQUB <(netv_print_data-1) ; lo OSWORD 2: printer spool data |
| A98F | EQUB <(netv_print_data-1) ; lo OSWORD 3: printer spool data |
| A990 | EQUB <(osword_4_handler-1) ; lo OSWORD 4: clear carry + abort |
| A991 | EQUB <(netv_spool_check-1) ; lo OSWORD 5: spool buffer check |
| A992 | EQUB <(dispatch_rts-1) ; lo OSWORD 6: no-op (RTS) |
| A993 | EQUB <(netv_claim_release-1) ; lo OSWORD 7: claim/release handler |
| A994 | EQUB <(osword_8_handler-1) ; lo OSWORD 8: copy PB + abort |
| A995 | .osword_handler_hi_table←1← A981 LDA |
| EQUB >(dispatch_rts-1) ; hi OSWORD 0: no-op (RTS) | |
| A996 | EQUB >(netv_print_data-1) ; hi OSWORD 1: printer spool data |
| A997 | EQUB >(netv_print_data-1) ; hi OSWORD 2: printer spool data |
| A998 | EQUB >(netv_print_data-1) ; hi OSWORD 3: printer spool data |
| A999 | EQUB >(osword_4_handler-1) ; hi OSWORD 4: clear carry + abort |
| A99A | EQUB >(netv_spool_check-1) ; hi OSWORD 5: spool buffer check |
| A99B | EQUB >(dispatch_rts-1) ; hi OSWORD 6: no-op (RTS) |
| A99C | EQUB >(netv_claim_release-1) ; hi OSWORD 7: claim/release handler |
| A99D | EQUB >(osword_8_handler-1) ; hi OSWORD 8: copy PB + abort |
OSWORD 4 handler: clear carry and send abortClears the carry flag in the stacked processor status, stores the original Y to workspace at offset &DA, and falls through to tx_econet_abort with A=0. Called via OSWORD handler dispatch table for OSWORD 4 (write interval timer). |
|
| A99E | .osword_4_handler |
| TSX ; Get stack pointer | |
| A99F | ROR stack_page_6,x ; Clear bit 0 of stacked P (carry) |
| A9A2 | ASL stack_page_6,x ; Shift back (clears carry flag) |
| A9A5 | TYA ; A = original Y |
| A9A6 | LDY #&da ; Y=&DA: workspace offset |
| A9A8 | STA (nfs_workspace),y ; Store Y to workspace |
| A9AA | LDA #0 ; Abort code = 0 |
| fall through ↓ | |
Send Econet abort/disconnect packetStores the abort code in workspace, configures the TX control block with control byte &80 (immediate operation flag), and transmits the abort packet. Used to cleanly disconnect from a remote station during error recovery. |
|
| A9AC | .tx_econet_abort←3← 8ADF JSR← A9FF JSR← AA65 JSR |
| LDY #&d9 ; Y=&D9: workspace abort offset | |
| A9AE | STA (nfs_workspace),y ; Store abort code to workspace |
| A9B0 | LDA #&80 ; Control byte &80 (abort) |
| A9B2 | LDY #&0c ; Y=&0C: control offset |
| A9B4 | STA (nfs_workspace),y ; Store control byte |
| A9B6 | LDA net_tx_ptr ; Save current TX ptr low |
| A9B8 | PHA ; Push on stack |
| A9B9 | LDA net_tx_ptr_hi ; Save current TX ptr high |
| A9BB | PHA ; Push on stack |
| A9BC | STY net_tx_ptr ; Set TX ptr to workspace offset |
| A9BE | LDX nfs_workspace_hi ; Load workspace high byte |
| A9C0 | STX net_tx_ptr_hi ; Set TX ptr high |
| A9C2 | JSR send_net_packet ; Send the abort packet |
| A9C5 | LDA #&3f ; Set status to &3F (complete) |
| A9C7 | STA (net_tx_ptr,x) ; Store at TX ptr offset 0 |
| A9C9 | PLA ; Restore TX ptr high |
| A9CA | STA net_tx_ptr_hi ; Back to net_tx_ptr_hi |
| A9CC | PLA ; Restore TX ptr low |
| A9CD | STA net_tx_ptr ; Back to net_tx_ptr |
| A9CF | RTS ; Return |
OSWORD 7 handler: claim/release network resourcesHandles OSWORD 7 (SOUND) intercepted via NETV. Searches the claim code table in two passes: first 11 entries (state 2), then all 18 (state 3). On match, saves 3 tube state bytes to workspace and sends an abort with the state code. For state 3 matches, also polls workspace for a response and restores the caller's stack frame from the saved bytes. |
|
| A9D0 | .netv_claim_release |
| LDY osword_pb_ptr_hi ; Load PB pointer high | |
| A9D2 | CMP #&81 ; Compare with &81 (special case) |
| A9D4 | BEQ process_match_result ; Match: skip to processing |
| A9D6 | LDY #1 ; Y=1: first claim code position |
| A9D8 | LDX #&0a ; X=&0A: 11 codes to check |
| A9DA | JSR match_rx_code ; Search claim code table |
| A9DD | BEQ process_match_result ; Found: skip to processing |
| A9DF | DEY ; Try second table range |
| A9E0 | DEY ; Y=-1: flag second range |
| A9E1 | LDX #&11 ; X=&11: 18 codes to check |
| A9E3 | JSR match_rx_code ; Search claim code table |
| A9E6 | BEQ process_match_result ; Found: skip to processing |
| A9E8 | INY ; Not found: increment Y |
| A9E9 | .process_match_result←3← A9D4 BEQ← A9DD BEQ← A9E6 BEQ |
| LDX #2 ; X=2: default state | |
| A9EB | TYA ; A = Y (search result) |
| A9EC | BEQ return_from_claim_release ; Zero: not found, return |
| A9EE | PHP ; Save result flags |
| A9EF | BPL save_tube_state ; Positive: use state X=2 |
| A9F1 | INX ; X=&03 |
| A9F2 | .save_tube_state←1← A9EF BPL |
| LDY #&dc ; Y=&DC: workspace offset for save | |
| A9F4 | .loop_save_tube_bytes←1← A9FC BPL |
| LDA tube_claimed_id,y ; Load tube claim ID byte | |
| A9F7 | STA (nfs_workspace),y ; Store to workspace |
| A9F9 | DEY ; Next byte down |
| A9FA | CPY #&da ; Until Y reaches &DA |
| A9FC | BPL loop_save_tube_bytes ; Loop for 3 bytes |
| A9FE | TXA ; A = state (2 or 3) |
| A9FF | JSR tx_econet_abort ; Send abort with state code |
| AA02 | PLP ; Restore flags |
| AA03 | BPL return_from_claim_release ; Positive: return without poll |
| AA05 | LDA #&7f ; Set control to &7F |
| AA07 | LDY #&0c ; Y=&0C: control offset |
| AA09 | STA (nfs_workspace),y ; Store control byte |
| AA0B | .loop_poll_ws_status←1← AA0D BPL |
| LDA (nfs_workspace),y ; Load status from workspace | |
| AA0D | BPL loop_poll_ws_status ; Positive: keep waiting |
| AA0F | TSX ; Get stack pointer |
| AA10 | LDY #&dd ; Y=&DD: workspace result offset |
| AA12 | LDA (nfs_workspace),y ; Load result byte |
| AA14 | ORA #&44 ; Set bit 6 and bit 2 |
| AA16 | BNE store_stack_byte ; Always branch (NZ from ORA) ALWAYS branch |
| AA18 | .loop_restore_stack←1← AA21 BNE |
| DEY ; Previous workspace byte | |
| AA19 | DEX ; Previous stack position |
| AA1A | LDA (nfs_workspace),y ; Load workspace byte |
| AA1C | .store_stack_byte←1← AA16 BNE |
| STA stack_page_6,x ; Store to caller's stack frame | |
| AA1F | CPY #&da ; Reached start of save area? |
| AA21 | BNE loop_restore_stack ; No: copy next byte |
| AA23 | .return_from_claim_release←2← A9EC BEQ← AA03 BPL |
| RTS ; Return | |
Search receive code table for matchScans a table of receive operation codes starting at index X, comparing each against A. Returns with Z set if a match is found, Z clear if the end-of-table marker is reached.
|
|||||||||
| AA24 | .match_rx_code←3← A9DA JSR← A9E3 JSR← AA2A BPL | ||||||||
| CMP osword_claim_codes,x ; Compare A with code at index X | |||||||||
| AA27 | BEQ return_from_match_rx_code ; Match: return with Z set | ||||||||
| AA29 | DEX ; Try next code | ||||||||
| AA2A | BPL match_rx_code ; More codes: continue search | ||||||||
| AA2C | .return_from_match_rx_code←2← AA27 BEQ← AA47 BNE | ||||||||
| RTS ; Return (Z clear = not found) | |||||||||
| ; OSWORD claim code table | |||||||||
| ; Table of OSWORD numbers that trigger NMI | |||||||||
| ; claim processing. Searched in two passes by | |||||||||
| ; the OSWORD 7 handler: first the 11-entry | |||||||||
| ; range (indices 0-&0A), then the full 18-entry | |||||||||
| ; range (indices 0-&11). A match in the first | |||||||||
| ; range sets state 2 (standard claim); a match | |||||||||
| ; only in the extended range sets state 3. | |||||||||
| AA2D | .osword_claim_codes←1← AA24 CMP | ||||||||
| EQUB &04 ; Range 1+2: OSWORD &04 | |||||||||
| AA2E | EQUB &09 ; Range 1+2: OSWORD &09 | ||||||||
| AA2F | EQUB &0A ; Range 1+2: OSWORD &0A | ||||||||
| AA30 | EQUB &14 ; Range 1+2: OSWORD &14 | ||||||||
| AA31 | EQUB &15 ; Range 1+2: OSWORD &15 | ||||||||
| AA32 | EQUB &9A ; Range 1+2: OSWORD &9A | ||||||||
| AA33 | EQUB &9B ; Range 1+2: OSWORD &9B | ||||||||
| AA34 | EQUB &E1 ; Range 1+2: OSWORD &E1 | ||||||||
| AA35 | EQUB &E2 ; Range 1+2: OSWORD &E2 | ||||||||
| AA36 | EQUB &E3 ; Range 1+2: OSWORD &E3 | ||||||||
| AA37 | EQUB &E4 ; Range 1+2: OSWORD &E4 | ||||||||
| AA38 | EQUB &0B ; Range 2 only: OSWORD &0B | ||||||||
| AA39 | EQUB &0C ; Range 2 only: OSWORD &0C | ||||||||
| AA3A | EQUB &0F ; Range 2 only: OSWORD &0F | ||||||||
| AA3B | EQUB &79 ; Range 2 only: OSWORD &79 | ||||||||
| AA3C | EQUB &7A ; Range 2 only: OSWORD &7A | ||||||||
| AA3D | EQUB &86 ; Range 2 only: OSWORD &86 | ||||||||
| AA3E | EQUB &87 ; Range 2 only: OSWORD &87 | ||||||||
OSWORD 7/8 handler: copy PB to workspace and abortHandles OSWORD 7 or 8 by copying 15 bytes from the parameter block to workspace at offset &DB, storing the OSWORD number at offset &DA, setting control value &E9, and sending an abort packet. Returns via tx_econet_abort. Rejects other OSWORD numbers by returning immediately. |
|
| AA3F | .osword_8_handler |
| LDY #&0e ; Y=&0E: copy 15 bytes (0-14) | |
| AA41 | CMP #7 ; OSWORD 7? |
| AA43 | BEQ copy_pb_to_ws ; Yes: handle |
| AA45 | CMP #8 ; OSWORD 8? |
| AA47 | BNE return_from_match_rx_code ; No: return |
| AA49 | .copy_pb_to_ws←1← AA43 BEQ |
| LDX #&db ; Workspace low = &DB | |
| AA4B | STX nfs_workspace ; Set nfs_workspace low byte |
| AA4D | .loop_copy_pb_to_ws←1← AA52 BPL |
| LDA (osword_pb_ptr),y ; Load PB[Y] | |
| AA4F | STA (nfs_workspace),y ; Store to workspace[Y] |
| AA51 | DEY ; Next byte down |
| AA52 | BPL loop_copy_pb_to_ws ; Loop for 15 bytes |
| AA54 | INY ; Y=0 |
| AA55 | DEC nfs_workspace ; Workspace low = &DA |
| AA57 | LDA osbyte_a_copy ; Load OSWORD number |
| AA59 | STA (nfs_workspace),y ; Store at workspace+0 (= &DA) |
| AA5B | STY nfs_workspace ; Workspace low = 0 (restore) |
| AA5D | LDY #&14 ; Y=&14: control offset |
| AA5F | LDA #&e9 ; Control value &E9 |
| AA61 | STA (nfs_workspace),y ; Store to workspace+&14 |
| AA63 | LDA #1 ; Abort code = 1 |
| AA65 | JSR tx_econet_abort ; Send abort packet |
| AA68 | STX nfs_workspace ; Restore nfs_workspace low |
| fall through ↓ | |
Initialise workspace copy in wide mode (14 bytes)Copies 14 bytes to workspace offset &7C. Falls through to the template-driven copy loop which handles &FD (skip), &FE (end), and &FC (page pointer) markers. |
|
| AA6A | .init_ws_copy_wide←2← A94A JSR← A959 JSR |
| LDX #&0d ; X=&0D: 14 bytes to copy | |
| AA6C | LDY #&7c ; Y=&7C: workspace destination offset |
| AA6E | BIT bit_test_ff ; Test bit 6 via BIT (V flag check) |
| AA71 | BVS loop_copy_ws_template ; V=1: skip to wide mode copy |
| fall through ↓ | |
Initialise workspace copy in narrow mode (27 bytes)Sets up a 27-byte copy to workspace offset &17, then falls through to ws_copy_vclr_entry for the template-driven copy loop. Used for the compact workspace initialisation variant. |
|
| AA73 | .init_ws_copy_narrow←1← 958C JSR |
| LDY #&17 ; Y=&17: narrow mode dest offset | |
| AA75 | LDX #&1a ; X=&1A: 27 bytes to copy |
| fall through ↓ | |
Template-driven workspace copy with V clearProcesses a template byte array to initialise workspace. Special marker bytes: &FE terminates the copy, &FD skips the current offset, and &FC substitutes the workspace page pointer. All other values are stored directly to the workspace at the current offset. |
|
| AA77 | .ws_copy_vclr_entry←1← AB38 JSR |
| CLV ; Clear V flag for narrow mode | |
| AA78 | .loop_copy_ws_template←2← AA71 BVS← AA99 BPL |
| LDA ws_txcb_template_data,x ; Load template byte | |
| AA7B | CMP #&fe ; Is it &FE? (end marker) |
| AA7D | BEQ done_ws_template_copy ; Yes: finished, set TX ptr |
| AA7F | CMP #&fd ; Is it &FD? (skip marker) |
| AA81 | BEQ advance_template_idx ; Yes: skip store, just advance |
| AA83 | CMP #&fc ; Is it &FC? (page ptr marker) |
| AA85 | BNE select_store_target ; No: use literal value |
| AA87 | LDA net_rx_ptr_hi ; &FC: load RX buffer page |
| AA89 | BVS store_tx_ptr_hi ; V=1: use net_rx_ptr_hi |
| AA8B | LDA nfs_workspace_hi ; V=0: use nfs_workspace_hi |
| AA8D | .store_tx_ptr_hi←1← AA89 BVS |
| STA net_tx_ptr_hi ; Store as TX ptr high | |
| AA8F | .select_store_target←1← AA85 BNE |
| BVS store_via_rx_ptr ; V=1: store to net_rx_ptr target | |
| AA91 | STA (nfs_workspace),y ; V=0: store to nfs_workspace |
| AA93 | BVC advance_template_idx ; Continue to next byte ALWAYS branch |
| AA95 | .store_via_rx_ptr←1← AA8F BVS |
| STA (net_rx_ptr),y ; V=1: store to net_rx_ptr | |
| AA97 | .advance_template_idx←2← AA81 BEQ← AA93 BVC |
| DEY ; Advance workspace offset down | |
| AA98 | DEX ; Advance template index |
| AA99 | BPL loop_copy_ws_template ; More bytes: continue copy |
| AA9B | .done_ws_template_copy←1← AA7D BEQ |
| INY ; Adjust Y for start of TX data | |
| AA9C | STY net_tx_ptr ; Set net_tx_ptr from Y |
| AA9E | RTS ; Return |
| ; Workspace TXCB init template | |
| ; 39-byte template with three overlapping | |
| ; regions, each a TXCB/RXCB structure: | |
| ; Wide [0..13]: ws+&6F..&7C via net_rx_ptr | |
| ; Narrow [14..26]: ws+&0C..&17 via workspace | |
| ; Spool [27..38]: ws+&01..&0B via workspace | |
| ; Markers: &FE=end, &FD=skip, &FC=page ptr. | |
| AA9F | .ws_txcb_template_data←1← AA78 LDA |
| EQUB &85 ; Wide &6F: ctrl=&85 | |
| AAA0 | EQUB &00 ; Wide &70: port=&00 |
| AAA1 | EQUB &FD ; Wide &71: skip (dest station) |
| AAA2 | EQUB &FD ; Wide &72: skip (dest network) |
| AAA3 | EQUB &7D ; Wide &73: buf start lo=&7D |
| AAA4 | EQUB &FC ; Wide &74: buf start hi=page ptr |
| AAA5 | EQUB &FF ; Wide &75: buf start ext lo |
| AAA6 | EQUB &FF ; Wide &76: buf start ext hi |
| AAA7 | EQUB &7E ; Wide &77: buf end lo=&7E |
| AAA8 | EQUB &FC ; Wide &78: buf end hi=page ptr |
| AAA9 | EQUB &FF ; Wide &79: buf end ext lo |
| AAAA | EQUB &FF ; Wide &7A: buf end ext hi |
| AAAB | EQUB &00 ; Wide &7B: zero |
| AAAC | EQUB &00 ; Wide &7C: zero |
| AAAD | EQUB &FE ; Narrow stop (&FE terminator) |
| AAAE | EQUB &80 ; Narrow &0C: ctrl=&80 (standard) |
| AAAF | EQUB &93 ; Narrow &0D: port=&93 |
| AAB0 | EQUB &FD ; Narrow &0E: skip (dest station) |
| AAB1 | EQUB &FD ; Narrow &0F: skip (dest network) |
| AAB2 | EQUB &D9 ; Narrow &10: buf start lo=&D9 |
| AAB3 | EQUB &FC ; Narrow &11: buf start hi=page ptr |
| AAB4 | EQUB &FF ; Narrow &12: buf start ext lo |
| AAB5 | EQUB &FF ; Narrow &13: buf start ext hi |
| AAB6 | EQUB &DE ; Narrow &14: buf end lo=&DE |
| AAB7 | EQUB &FC ; Narrow &15: buf end hi=page ptr |
| AAB8 | EQUB &FF ; Narrow &16: buf end ext lo |
| AAB9 | EQUB &FF ; Narrow &17: buf end ext hi |
| AABA | EQUB &FE ; Spool stop (&FE terminator) |
| AABB | EQUB &D1 ; Spool &01: port=&D1 |
| AABC | EQUB &FD ; Spool &02: skip (dest station) |
| AABD | EQUB &FD ; Spool &03: skip (dest network) |
| AABE | EQUB &25 ; Spool &04: buf start lo=&25 |
| AABF | EQUB &FD ; Spool &05: skip (buf start hi) |
| AAC0 | EQUB &FF ; Spool &06: buf start ext lo |
| AAC1 | EQUB &FF ; Spool &07: buf start ext hi |
| AAC2 | EQUB &FD ; Spool &08: skip (buf end lo) |
| AAC3 | EQUB &FD ; Spool &09: skip (buf end hi) |
| AAC4 | EQUB &FF ; Spool &0A: buf end ext lo |
| AAC5 | EQUB &FF ; Spool &0B: buf end ext hi |
OSWORD 5 handler: check spool PB and reset bufferHandles OSWORD 5 intercepted via NETV. Checks if X-1 matches osword_pb_ptr and bit 0 of &00D0 is clear. If both conditions are met, falls through to reset_spool_buf_state to reinitialise the spool buffer for new data. |
|
| AAC6 | .netv_spool_check |
| DEX ; X = X - 1 | |
| AAC7 | CPX osword_pb_ptr ; Match osword_pb_ptr? |
| AAC9 | BNE return_from_spool_reset ; No: return (not our PB) |
| AACB | LDA vdu_status ; Load spool state byte |
| AACD | ROR ; Rotate bit 0 into carry |
| AACE | BCS return_from_spool_reset ; C=1: already active, return |
| fall through ↓ | |
Reset spool buffer to initial stateSets the spool buffer pointer to &25 (first available data position) and the control state byte to &41 (ready for new data). Called after processing a complete spool data block. |
|
| AAD0 | .reset_spool_buf_state←2← 8F0E JSR← AB21 JMP |
| LDA #&25 ; Buffer start at &25 | |
| AAD2 | STA spool_buf_idx ; Store as buffer pointer |
| AAD5 | LDA #&41 ; Control state &41 |
| AAD7 | STA ws_0d6a ; Store as spool control state |
| AADA | .return_from_spool_reset←4← AAC9 BNE← AACE BCS← AADD BNE← AAF1 BCS |
| RTS ; Return | |
OSWORD 1-3 handler: drain printer bufferHandles OSWORDs 1-3 intercepted via NETV. When X=1, drains the printer buffer (OSBYTE &91, buffer 3) into the receive buffer, sending packets via process_spool_data when the buffer exceeds &6E bytes. When X>1, routes to handle_spool_ctrl_byte for spool state control. |
|
| AADB | .netv_print_data |
| CPY #4 ; Check Y == 4 | |
| AADD | BNE return_from_spool_reset ; No: return |
| AADF | TXA ; A = X (control byte) |
| AAE0 | DEX ; Decrement X |
| AAE1 | BNE handle_spool_ctrl_byte ; Non-zero: handle spool ctrl byte |
| AAE3 | TSX ; Get stack pointer |
| AAE4 | ORA stack_page_6,x ; OR with stack value |
| AAE7 | STA stack_page_6,x ; Store back to stack |
| AAEA | .loop_drain_printer_buf←2← AAF9 BCC← AAFE BCC |
| LDA #osbyte_read_buffer ; OSBYTE &91: read buffer | |
| AAEC | LDX #buffer_printer ; X=3: printer buffer |
| AAEE | JSR osbyte ; Read character from buffer Get character from input buffer (C is set if the buffer is empty, otherwise Y=extracted character) |
| AAF1 | BCS return_from_spool_reset ; C=1: buffer empty, return |
| AAF3 | TYA ; A = extracted character Y is the character extracted from the buffer |
| AAF4 | JSR append_byte_to_rxbuf ; Add byte to RX buffer |
| AAF7 | CPY #&6e ; Buffer past &6E limit? |
| AAF9 | BCC loop_drain_printer_buf ; No: read more from buffer |
| AAFB | JSR process_spool_data ; Buffer full: send packet |
| AAFE | BCC loop_drain_printer_buf ; More room: continue reading |
| fall through ↓ | |
Append byte to receive bufferStores A in the receive buffer at the current buffer index (ws_ptr_lo), then increments the index. Used to accumulate incoming spool data bytes before processing.
|
||||
| AB00 | .append_byte_to_rxbuf←3← AAF4 JSR← AB1B JSR← ABD2 JSR | |||
| LDY spool_buf_idx ; Load current buffer index | ||||
| AB03 | STA (net_rx_ptr),y ; Store byte at buffer position | |||
| AB05 | INC spool_buf_idx ; Advance buffer index | |||
| AB08 | RTS ; Return | |||
Handle spool control byte and flush bufferRotates bit 0 of the control byte into carry for mode selection (print vs spool), appends the byte to the buffer, calls process_spool_data to transmit the accumulated data, and resets the buffer state ready for the next block. |
|
| AB09 | .handle_spool_ctrl_byte←2← 8F3D JSR← AAE1 BNE |
| ROR ; Rotate bit 0 into carry | |
| AB0A | BCC check_spool_state ; Bit 0 clear: not active path |
| AB0C | LDA ws_0d6a ; Load spool control state |
| AB0F | PHA ; Save for bit test |
| AB10 | ROR ; Rotate bit 0 into carry |
| AB11 | PLA ; Restore state |
| AB12 | BCS done_spool_ctrl ; C=1: already started, reset |
| AB14 | ORA #3 ; Set bits 0-1 (active + pending) |
| AB16 | STA ws_0d6a ; Store updated state |
| AB19 | LDA #3 ; Control byte 3 for header |
| AB1B | JSR append_byte_to_rxbuf ; Add to RX buffer |
| AB1E | JSR process_spool_data ; Send current buffer |
| AB21 | .done_spool_ctrl←1← AB12 BCS |
| JMP reset_spool_buf_state ; Reset spool buffer state | |
Transmit accumulated spool buffer dataCopies the workspace state to the TX control block, sends a disconnect reply if the previous transfer requires acknowledgment, then handles the spool output sequence by setting up and sending the pass-through TX buffer. |
|
| AB24 | .process_spool_data←4← AAFB JSR← AB1E JSR← AB67 BCC← ABD5 JSR |
| LDY #8 ; Y=8: workspace offset for length | |
| AB26 | LDA spool_buf_idx ; Load buffer index (=length) |
| AB29 | STA (nfs_workspace),y ; Store length to workspace |
| AB2B | LDA net_rx_ptr_hi ; Set data page high byte |
| AB2D | INY ; Y=&09 |
| AB2E | STA (nfs_workspace),y ; Store to workspace+9 |
| AB30 | LDY #5 ; Y=5: workspace offset |
| AB32 | STA (nfs_workspace),y ; Store page to workspace+5 |
| AB34 | LDY #&0b ; Y=&0B: template start offset |
| AB36 | LDX #&26 ; X=&26: template index |
| AB38 | JSR ws_copy_vclr_entry ; Copy template to workspace |
| AB3B | DEY ; Adjust Y down |
| AB3C | LDA ws_0d6a ; Load spool control state |
| AB3F | PHA ; Save state |
| AB40 | ROL ; Rotate to get carry (bit 7) |
| AB41 | PLA ; Restore state |
| AB42 | EOR #&80 ; Toggle bit 7 |
| AB44 | STA ws_0d6a ; Store updated state |
| AB47 | ROL ; Shift to get both flag bits |
| AB48 | STA (nfs_workspace),y ; Store flags to workspace |
| AB4A | LDA vdu_status ; Save l00d0 (exec flag) |
| AB4C | PHA ; Push for later restore |
| AB4D | AND #&fe ; Clear bit 0 of exec flag |
| AB4F | STA vdu_status ; Store modified exec flag |
| AB51 | LDY #&25 ; Reset buffer start to &25 |
| AB53 | STY spool_buf_idx ; Store reset buffer index |
| AB56 | LDA #0 ; A=0 for disconnect reply |
| AB58 | TAX ; X=0 X=&00 |
| AB59 | LDY nfs_workspace_hi ; Y = workspace page |
| AB5B | CLI ; Enable interrupts |
| AB5C | JSR send_disconnect_reply ; Send disconnect reply packet |
| AB5F | PLA ; Restore exec flag |
| AB60 | STA vdu_status ; Store original exec flag |
| AB62 | RTS ; Return |
| AB63 | .check_spool_state←1← AB0A BCC |
| LDA ws_0d6a ; Load spool control state | |
| AB66 | ROR ; Rotate bit 0 to carry |
| AB67 | BCC process_spool_data ; C=0: send current buffer |
| AB69 | LDA vdu_status ; Save exec flag |
| AB6B | PHA ; Push for restore |
| AB6C | AND #&fe ; Clear bit 0 |
| AB6E | STA vdu_status ; Store modified flag |
| AB70 | LDA #&14 ; Control byte &14 (repeat count) |
| AB72 | .start_spool_retry←1← ABE6 BNE |
| PHA ; Save retry count | |
| AB73 | LDX #&0b ; X=&0B: 12 bytes of template |
| AB75 | LDY #&30 ; Y=&30: workspace offset for TXCB |
| AB77 | .loop_copy_spool_tx←1← AB7E BPL |
| LDA tx_econet_txcb_template,x ; Load template byte | |
| AB7A | STA (net_rx_ptr),y ; Store to workspace |
| AB7C | DEY ; Next byte down |
| AB7D | DEX ; Next template byte |
| AB7E | BPL loop_copy_spool_tx ; Loop for 12 bytes |
| AB80 | STX escapable ; X=-1: disable escape checking |
| AB82 | LDY #2 ; Y=2: workspace offset for station |
| AB84 | LDA (nfs_workspace),y ; Load station number |
| AB86 | PHA ; Save station |
| AB87 | INY ; Y=&03 |
| AB88 | LDA (nfs_workspace),y ; Load network number |
| AB8A | LDY #&28 ; Y=&28: TXCB dest network offset |
| AB8C | STA (net_rx_ptr),y ; Store network to TXCB |
| AB8E | DEY ; Y=&27 |
| AB8F | PLA ; Restore station |
| AB90 | STA (net_rx_ptr),y ; Store station to TXCB |
| AB92 | LDX #&0b ; X=&0B: 12 bytes of RX template |
| AB94 | LDY #&0b ; Y=&0B: workspace RX offset |
| AB96 | .loop_copy_spool_rx←1← ABA7 BPL |
| LDA rx_palette_txcb_template,x ; Load RX template byte | |
| AB99 | CMP #&fd ; Is it &FD? (skip marker) |
| AB9B | BEQ advance_spool_rx_idx ; Yes: skip store |
| AB9D | CMP #&fc ; Is it &FC? (page ptr marker) |
| AB9F | BNE store_spool_rx_byte ; No: use literal value |
| ABA1 | LDA net_rx_ptr_hi ; &FC: substitute RX buffer page |
| ABA3 | .store_spool_rx_byte←1← AB9F BNE |
| STA (nfs_workspace),y ; Store to workspace | |
| ABA5 | .advance_spool_rx_idx←1← AB9B BEQ |
| DEY ; Next byte down | |
| ABA6 | DEX ; Next template byte |
| ABA7 | BPL loop_copy_spool_rx ; Loop for 12 bytes |
| ABA9 | LDA #&25 ; TX data start at &25 |
| ABAB | STA net_tx_ptr ; Set net_tx_ptr low |
| ABAD | LDA net_rx_ptr_hi ; Set data page high byte |
| ABAF | STA net_tx_ptr_hi ; Set net_tx_ptr high |
| ABB1 | JSR setup_pass_txbuf ; Set up password in TX buffer |
| ABB4 | JSR send_net_packet ; Send the packet |
| ABB7 | LDA #0 ; Clear net_tx_ptr low (page base) |
| ABB9 | STA net_tx_ptr ; Store zero |
| ABBB | LDA nfs_workspace_hi ; Set TX high to workspace page |
| ABBD | STA net_tx_ptr_hi ; Store workspace high byte |
| ABBF | JSR wait_net_tx_ack ; Wait for TX acknowledgement |
| ABC2 | LDY #&31 ; Y=&31: check reply status |
| ABC4 | LDA (net_rx_ptr),y ; Load reply byte |
| ABC6 | BEQ spool_tx_succeeded ; Zero: success |
| ABC8 | CMP #3 ; Status = 3? (busy, can retry) |
| ABCA | BNE spool_tx_retry ; Other error: handle failure |
| ABCC | .spool_tx_succeeded←1← ABC6 BEQ |
| PLA ; Discard retry count | |
| ABCD | PLA ; Discard saved exec flag |
| ABCE | STA vdu_status ; Restore l00d0 |
| ABD0 | LDA #0 ; A=0: null terminator |
| ABD2 | JSR append_byte_to_rxbuf ; Add zero to RX buffer (end marker) |
| ABD5 | JSR process_spool_data ; Send final buffer |
| ABD8 | LDA ws_0d6a ; Load spool state |
| ABDB | AND #&f0 ; Clear low nibble |
| ABDD | STA ws_0d6a ; Store cleaned state |
| ABE0 | RTS ; Return |
| ABE1 | .spool_tx_retry←1← ABCA BNE |
| TAX ; X = error code | |
| ABE2 | PLA ; Restore retry count |
| ABE3 | SEC ; Set carry for subtract |
| ABE4 | SBC #1 ; Decrement retry count |
| ABE6 | BNE start_spool_retry ; Non-zero: retry send |
| ABE8 | CPX #1 ; Error code = 1? (busy) |
| ABEA | BNE error_printer_jammed ; No: printer jammed error |
| ABEC | .err_printer_busy←1← AFD5 JMP |
| LDA #&a6 ; A=&A6: printer busy error number | |
| ABEE | JSR error_inline_log ; Generate 'Printer busy' error |
| ABF1 | EQUS "Printer busy." |
| ABFE | .error_printer_jammed←1← ABEA BNE |
| LDA #&a7 ; A=&A7: printer jammed error number | |
| AC00 | JSR error_inline_log ; Generate 'Printer jammed' error |
| AC03 | EQUS "Printer jammed." |
| fall through ↓ | |
Send Econet disconnect reply packetSets up the TX pointer, copies station addresses, matches the station in the table, and sends the response. Waits for acknowledgment before returning. |
|
| AC12 | .send_disconnect_reply←3← 94F5 JSR← AB5C JSR← B946 JSR |
| STX net_tx_ptr ; Set TX ptr low byte | |
| AC14 | STY net_tx_ptr_hi ; Set TX ptr high byte |
| AC16 | PHA ; Save disconnect code |
| AC17 | ORA #0 ; Test if zero |
| AC19 | BEQ send_disconnect_status ; Zero: skip station search |
| AC1B | LDX #&ff ; X=&FF: start search from -1 |
| AC1D | TAY ; Y = disconnect code |
| AC1E | .loop_scan_disconnect←2← AC27 BNE← AC31 BNE |
| TYA ; A = disconnect code | |
| AC1F | INX ; Next station index |
| AC20 | CMP fcb_net_or_port,x ; Compare with station table entry |
| AC23 | BEQ verify_stn_match ; Match: verify station/network |
| AC25 | CPX #&0f ; Past last station? |
| AC27 | BNE loop_scan_disconnect ; No: try next |
| AC29 | LDA #0 ; Not found: A=0 |
| AC2B | BEQ send_disconnect_status ; Skip to status update ALWAYS branch |
| AC2D | .verify_stn_match←1← AC23 BEQ |
| TAY ; Y = disconnect code for compare | |
| AC2E | JSR match_station_net ; Check station and network match |
| AC31 | BNE loop_scan_disconnect ; No match: try next station |
| AC33 | LDA chan_status,x ; Load station status flags |
| AC36 | AND #1 ; Isolate bit 0 (active flag) |
| AC38 | .send_disconnect_status←2← AC19 BEQ← AC2B BEQ |
| LDY #0 ; Y=0: TX buffer status offset | |
| AC3A | ORA (net_tx_ptr),y ; OR with existing status byte |
| AC3C | PHA ; Save combined status |
| AC3D | STA (net_tx_ptr),y ; Store to TX buffer |
| AC3F | JSR send_net_packet ; Send the packet |
| AC42 | LDA #&ff ; Set end markers to &FF |
| AC44 | LDY #8 ; Y=8: first end marker offset |
| AC46 | STA (net_tx_ptr),y ; Store &FF |
| AC48 | INY ; Y=&09 |
| AC49 | STA (net_tx_ptr),y ; Store &FF at offset 9 too |
| AC4B | PLA ; Restore disconnect code |
| AC4C | TAX ; X = status for control byte |
| AC4D | LDY #&d1 ; Y=&D1: default control |
| AC4F | PLA ; Check original disconnect code |
| AC50 | PHA ; Peek but keep on stack |
| AC51 | BEQ store_tx_ctrl_byte ; Zero: use &D1 control |
| AC53 | LDY #&90 ; Non-zero: use &90 control |
| AC55 | .store_tx_ctrl_byte←1← AC51 BEQ |
| TYA ; A = control byte (Y) | |
| AC56 | LDY #1 ; Y=1: control byte offset |
| AC58 | STA (net_tx_ptr),y ; Store control byte |
| AC5A | TXA ; A = X (status) |
| AC5B | DEY ; Y=0 Y=&00 |
| AC5C | PHA ; Save status on stack |
| AC5D | .loop_wait_disc_tx_ack←1← AC69 BCS |
| LDA #&7f ; Set status to &7F (waiting) | |
| AC5F | STA (net_tx_ptr),y ; Store at TX buffer offset 0 |
| AC61 | JSR wait_net_tx_ack ; Wait for TX acknowledgement |
| AC64 | PLA ; Restore status |
| AC65 | PHA ; Keep on stack for next check |
| AC66 | EOR (net_tx_ptr),y ; Compare with current TX buffer |
| AC68 | ROR ; Rotate result bit 0 to carry |
| AC69 | BCS loop_wait_disc_tx_ack ; C=1: status changed, retry |
| AC6B | PLA ; Done: discard status |
| AC6C | PLA ; Discard disconnect code |
| AC6D | RTS ; Return |
| ; Spool TX control block template | |
| ; 12-byte TXCB template copied directly (no | |
| ; marker processing) to workspace at offset | |
| ; &25..&30. Destination station and network | |
| ; (&27/&28) are filled in from (nfs_workspace) | |
| ; after the copy. | |
| AC6E | .tx_econet_txcb_template←1← AB77 LDA |
| EQUB &80 ; ctrl=&80 (standard TX) | |
| AC6F | EQUB &9F ; port=&9F |
| AC70 | EQUB &00 ; dest station=&00 (filled later) |
| AC71 | EQUB &00 ; dest network=&00 (filled later) |
| AC72 | EQUB &43 ; buf start lo=&43 |
| AC73 | EQUB &8E ; buf start hi=&8E |
| AC74 | EQUB &FF ; buf start ext lo=&FF |
| AC75 | EQUB &FF ; buf start ext hi=&FF |
| AC76 | EQUB &4B ; buf end lo=&4B |
| AC77 | EQUB &8E ; buf end hi=&8E |
| AC78 | EQUB &FF ; buf end ext lo=&FF |
| AC79 | EQUB &FF ; buf end ext hi=&FF |
| ; Spool RX control block template | |
| ; 12-byte RXCB template with marker processing: | |
| ; &FD skips the offset (preserves existing value) | |
| ; and &FC substitutes net_rx_ptr_hi. Copied to | |
| ; workspace at offset &00..&0B. Sets up a 3-byte | |
| ; receive buffer at &xx31..&xx34. | |
| AC7A | .rx_palette_txcb_template←1← AB96 LDA |
| EQUB &7F ; ctrl=&7F (RX listen) | |
| AC7B | EQUB &9E ; port=&9E |
| AC7C | EQUB &FD ; skip: preserve dest station |
| AC7D | EQUB &FD ; skip: preserve dest network |
| AC7E | EQUB &31 ; buf start lo=&31 |
| AC7F | EQUB &FC ; buf start hi=page ptr (&FC) |
| AC80 | EQUB &FF ; buf start ext lo=&FF |
| AC81 | EQUB &FF ; buf start ext hi=&FF |
| AC82 | EQUB &34 ; buf end lo=&34 |
| AC83 | EQUB &FC ; buf end hi=page ptr (&FC) |
| AC84 | EQUB &FF ; buf end ext lo=&FF |
| AC85 | EQUB &FF ; buf end ext hi=&FF |
| AC86 | .lang_2_save_palette_vdu |
| LDA table_idx ; Save l00ad counter | |
| AC88 | PHA ; Push for later restore |
| AC89 | LDA #&e9 ; Set workspace low to &E9 |
| AC8B | STA nfs_workspace ; Store to nfs_workspace low |
| AC8D | LDY #0 ; Y=0: initial palette index |
| AC8F | STY table_idx ; Clear palette counter |
| AC91 | LDA vdu_screen_mode ; Load current screen mode |
| AC94 | STA (nfs_workspace),y ; Store mode to workspace |
| AC96 | INC nfs_workspace ; Advance workspace ptr |
| AC98 | LDA vdu_display_start_hi ; Load video ULA copy |
| AC9B | PHA ; Save for later restore |
| AC9C | TYA ; A=0 for first palette entry A=&00 |
| AC9D | .loop_read_palette←1← ACBC BNE |
| STA (nfs_workspace),y ; Store logical colour to workspace | |
| AC9F | LDX nfs_workspace ; X = workspace ptr low |
| ACA1 | LDY nfs_workspace_hi ; Y = workspace ptr high |
| ACA3 | LDA #osword_read_palette ; OSWORD &0B: read palette |
| ACA5 | JSR osword ; Read palette entry Read palette |
| ACA8 | PLA ; Restore previous ULA value |
| ACA9 | LDY #0 ; Y=0: reset index |
| ACAB | STA (nfs_workspace),y ; Store ULA value to workspace |
| ACAD | INY ; Y=1: physical colour offset Y=&01 |
| ACAE | LDA (nfs_workspace),y ; Load physical colour |
| ACB0 | PHA ; Save for next iteration |
| ACB1 | LDX nfs_workspace ; X = workspace ptr |
| ACB3 | INC nfs_workspace ; Advance workspace ptr |
| ACB5 | INC table_idx ; Advance palette counter |
| ACB7 | DEY ; Y=0 Y=&00 |
| ACB8 | LDA table_idx ; Load counter |
| ACBA | CPX #&f9 ; Reached &F9 workspace limit? |
| ACBC | BNE loop_read_palette ; No: read next palette entry |
| ACBE | PLA ; Discard last ULA value |
| ACBF | STY table_idx ; Clear counter |
| ACC1 | INC nfs_workspace ; Advance workspace ptr |
| ACC3 | JSR serialise_palette_entry ; Store extra palette info |
| ACC6 | INC nfs_workspace ; Advance workspace ptr again |
| ACC8 | PLA ; Restore original l00ad |
| ACC9 | STA table_idx ; Store restored counter |
| fall through ↓ | |
Copy current state byte to committed stateReads the working state byte from workspace and stores it to the committed state location. Used to finalise a state transition after all related workspace fields have been updated. |
|
| ACCB | .commit_state_byte←4← 9570 JMP← 9598 JSR← 95BF JSR← A62E JMP |
| LDA ws_0d69 ; Load current state | |
| ACCE | STA ws_0d68 ; Store as committed state |
| ACD1 | RTS ; Return |
Serialise palette register to workspaceReads the current logical colour for a palette register via OSBYTE &0B and stores both the palette value and the display mode information in the workspace block. Used during remote screen state capture. |
|
| ACD2 | .serialise_palette_entry←1← ACC3 JSR |
| LDA vdu_mode ; Load palette register value | |
| ACD5 | STA (nfs_workspace),y ; Store to workspace |
| ACD7 | LDX vdu_mode ; X = palette register |
| ACDA | JSR read_osbyte_to_ws ; Read OSBYTE for this mode |
| ACDD | INC nfs_workspace ; Advance workspace ptr |
| ACDF | TYA ; A=0 |
| ACE0 | STA (nfs_workspace,x) ; Store zero to workspace |
| ACE2 | JSR read_osbyte_to_ws_x0 ; Read OSBYTE with X=0 |
| fall through ↓ | |
Read OSBYTE with X=0 and store to workspaceSets X=0 then falls through to read_osbyte_to_ws to issue the OSBYTE call and store the result. Used when the OSBYTE parameter X must be zero. |
|
| ACE5 | .read_osbyte_to_ws_x0←1← ACE2 JSR |
| LDX #0 ; X=0: read mode info | |
| fall through ↓ | |
Issue OSBYTE from table and store resultLoads the OSBYTE function code from the next entry in the OSBYTE table, issues the call, and stores the Y result in workspace at the current offset. Advances the table pointer for the next call. |
|
| ACE7 | .read_osbyte_to_ws←1← ACDA JSR |
| LDY table_idx ; Load OSBYTE code index | |
| ACE9 | INC table_idx ; Advance index counter |
| ACEB | INC nfs_workspace ; Advance workspace ptr |
| ACED | LDA osbyte_mode_read_codes,y ; Load OSBYTE number from table |
| ACF0 | LDY #&ff ; Y=&FF: read current value |
| ACF2 | JSR osbyte ; Call OSBYTE to read value |
| ACF5 | TXA ; A = result (from X) |
| ACF6 | LDX #0 ; X=0 for indexed store |
| ACF8 | STA (nfs_workspace,x) ; Store result to workspace |
| ACFA | RTS ; Return |
| ; OSBYTE mode read codes | |
| ; Three OSBYTE numbers used by read_osbyte_to_ws | |
| ; to save display mode state to workspace before | |
| ; a language 2 file transfer. | |
| ACFB | .osbyte_mode_read_codes←1← ACED LDA |
| EQUB &85 ; OSBYTE &85: read display start addr | |
| ACFC | EQUB &C2 ; OSBYTE &C2: read video ULA ctrl |
| ACFD | EQUB &C3 ; OSBYTE &C3: read video ULA palette |
*CDir command handlerParses an optional allocation size argument: if absent, defaults to index 2 (standard 19-entry directory, &200 bytes); if present, parses the decimal value and searches a 26-entry threshold table to find the matching allocation size index. Parses the directory name via parse_filename_arg, copies it to the TX buffer, and sends FS command code &1B to create the directory.
|
||||
| ACFE | .cmd_cdir | |||
| TYA ; Save command line offset | ||||
| ACFF | PHA ; Push onto stack | |||
| AD00 | JSR mask_owner_access ; Set owner-only access mask | |||
| AD03 | JSR skip_to_next_arg ; Skip to optional size argument | |||
| AD06 | CMP #&0d ; End of line? | |||
| AD08 | BNE parse_cdir_size ; No: parse size argument | |||
| AD0A | LDX #2 ; Default allocation size index = 2 | |||
| AD0C | BNE done_cdir_size ; ALWAYS branch | |||
| AD0E | .parse_cdir_size←1← AD08 BNE | |||
| LDA #&ff ; A=&FF: mark as decimal parse | ||||
| AD10 | STA fs_work_4 ; Store decimal parse flag | |||
| AD12 | JSR parse_addr_arg ; Parse numeric size argument | |||
| AD15 | LDX #&1b ; X=&1B: top of 26-entry size table | |||
| AD17 | .loop_find_alloc_size←1← AD1B BCC | |||
| DEX ; Try next lower index | ||||
| AD18 | CMP cdir_alloc_size_table,x ; Compare size with threshold | |||
| AD1B | BCC loop_find_alloc_size ; A < threshold: keep searching | |||
| AD1D | .done_cdir_size←1← AD0C BNE | |||
| STX fs_cmd_data ; Store allocation size index | ||||
| AD20 | PLA ; Restore command line offset | |||
| AD21 | TAY ; Transfer to Y | |||
| AD22 | JSR save_ptr_to_os_text ; Save text pointer for filename parse | |||
| AD25 | JSR parse_filename_arg ; Parse directory name argument | |||
| AD28 | LDX #1 ; X=1: one argument to copy | |||
| AD2A | JSR copy_arg_to_buf ; Copy directory name to TX buffer | |||
| AD2D | LDY #&1b ; Y=&1B: *CDir FS command code | |||
| AD2F | .cdir_dispatch_col | |||
| JMP save_net_tx_cb ; Send command to file server | ||||
| ; *CDir allocation size threshold table | ||||
| ; 26 thresholds dividing 0-255 into size classes. | ||||
| ; Table base overlaps with the JMP high byte at | ||||
| ; cdir_dispatch_col+2 (entry 0 = &94, never reached). Searched | ||||
| ; from index 26 down to 0; the result index (1-26) | ||||
| ; is stored as the directory allocation size class. | ||||
| ; Default when no size argument given: index 2. | ||||
| AD32 | EQUB &00 ; Index 1: threshold 0 (catch-all) | |||
| AD33 | EQUB &0A ; Index 2: threshold 10 (default) | |||
| AD34 | EQUB &14 ; Index 3: threshold 20 | |||
| AD35 | EQUB &1D ; Index 4: threshold 29 | |||
| AD36 | EQUB &27 ; Index 5: threshold 39 | |||
| AD37 | EQUB &31 ; Index 6: threshold 49 | |||
| AD38 | EQUB &3B ; Index 7: threshold 59 | |||
| AD39 | EQUB &45 ; Index 8: threshold 69 | |||
| AD3A | EQUB &4F ; Index 9: threshold 79 | |||
| AD3B | EQUB &58 ; Index 10: threshold 88 | |||
| AD3C | EQUB &62 ; Index 11: threshold 98 | |||
| AD3D | EQUB &6C ; Index 12: threshold 108 | |||
| AD3E | EQUB &76 ; Index 13: threshold 118 | |||
| AD3F | EQUB &80 ; Index 14: threshold 128 | |||
| AD40 | EQUB &8A ; Index 15: threshold 138 | |||
| AD41 | EQUB &94 ; Index 16: threshold 148 | |||
| AD42 | EQUB &9D ; Index 17: threshold 157 | |||
| AD43 | EQUB &A7 ; Index 18: threshold 167 | |||
| AD44 | EQUB &B1 ; Index 19: threshold 177 | |||
| AD45 | EQUB &BB ; Index 20: threshold 187 | |||
| AD46 | EQUB &C5 ; Index 21: threshold 197 | |||
| AD47 | EQUB &CF ; Index 22: threshold 207 | |||
| AD48 | EQUB &D8 ; Index 23: threshold 216 | |||
| AD49 | EQUB &E2 ; Index 24: threshold 226 | |||
| AD4A | EQUB &EC ; Index 25: threshold 236 | |||
| AD4B | EQUB &F6 ; Index 26: threshold 246 | |||
| AD4C | EQUB &FF ; Unused (index 27, never accessed) | |||
*LCat command handlerSets the library flag by rotating SEC into bit 7 of l1071, then branches to cat_set_lib_flag inside cmd_ex to catalogue the library directory with three entries per column.
|
||||
| AD4D | .cmd_lcat | |||
| ROR fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| AD50 | SEC ; Set carry (= library directory) | |||
| AD51 | BCS cat_set_lib_flag ; ALWAYS branch | |||
*LEx command handlerSets the library flag by rotating SEC into bit 7 of l1071, then branches to ex_set_lib_flag inside cmd_ex to examine the library directory with one entry per line.
|
||||
| AD53 | .cmd_lex | |||
| ROR fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| AD56 | SEC ; Set carry (= library directory) | |||
| AD57 | BCS ex_set_lib_flag ; ALWAYS branch | |||
*Ex command handlerUnified handler for *Ex, *LCat, and *LEx. Sets the library flag from carry (CLC for current, SEC for library). Configures column format: 1 entry per line for Ex (command 3), 3 per column for Cat (command &0B). Sends the examine request (code &12), then prints the directory header: title, cycle number, Owner/Public label, option name, Dir. and Lib. paths. Paginates through entries, printing each via ex_print_col_sep until the server returns zero entries.
|
||||
| AD59 | .cmd_ex | |||
| ROR fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| AD5C | CLC ; Clear carry (= current directory) | |||
| AD5D | .ex_set_lib_flag←1← AD57 BCS | |||
| ROL fs_lib_flags ; Rotate carry back, clearing bit 7 | ||||
| AD60 | LDA #&ff ; A=&FF: initial column counter | |||
| AD62 | STA fs_spool_handle ; Store column counter | |||
| AD64 | LDA #1 ; One entry per line (Ex format) | |||
| AD66 | STA fs_work_7 ; Store entries per page | |||
| AD68 | LDA #3 ; FS command code 3: Examine | |||
| AD6A | STA fs_work_5 ; Store command code | |||
| AD6C | BNE setup_ex_request ; ALWAYS branch | |||
| AD6E | .fscv_5_cat | |||
| JSR set_xfer_params ; Set transfer parameters | ||||
| AD71 | LDY #0 ; Y=0: start from entry 0 | |||
| AD73 | ROR fs_lib_flags ; Rotate carry into lib flag | |||
| AD76 | CLC ; Clear carry (= current directory) | |||
| AD77 | .cat_set_lib_flag←1← AD51 BCS | |||
| ROL fs_lib_flags ; Rotate carry back, clearing bit 7 | ||||
| AD7A | LDA #3 ; Three entries per column (Cat) | |||
| AD7C | STA fs_spool_handle ; Store column counter | |||
| AD7E | STA fs_work_7 ; Store entries per page | |||
| AD80 | LDA #&0b ; FS command code &0B: Catalogue | |||
| AD82 | STA fs_work_5 ; Store command code | |||
| AD84 | .setup_ex_request←1← AD6C BNE | |||
| JSR save_ptr_to_os_text ; Save text pointer | ||||
| AD87 | LDA #&ff ; A=&FF: enable escape checking | |||
| AD89 | STA escapable ; Set escapable flag | |||
| AD8B | LDA #6 ; Command code 6 | |||
| AD8D | STA fs_cmd_data ; Store in TX buffer | |||
| AD90 | JSR parse_filename_arg ; Parse directory argument | |||
| AD93 | LDX #1 ; X=1: offset in buffer | |||
| AD95 | JSR copy_arg_to_buf ; Copy argument to TX buffer | |||
| AD98 | LDA fs_lib_flags ; Get library/FS flags | |||
| AD9B | LSR ; Shift bit 0 to carry | |||
| AD9C | BCC store_owner_flags ; Bit 0 clear: skip | |||
| AD9E | ORA #&40 ; Set bit 6 (owner access flag) | |||
| ADA0 | .store_owner_flags←1← AD9C BCC | |||
| ROL ; Rotate back | ||||
| ADA1 | STA fs_lib_flags ; Store modified flags | |||
| ADA4 | LDY #&12 ; Y=&12: FS command for examine | |||
| ADA6 | JSR save_net_tx_cb ; Send request to file server | |||
| ADA9 | LDX #3 ; X=3: offset to directory title | |||
| ADAB | JSR print_10_chars ; Print directory title (10 chars) | |||
| ADAE | JSR print_inline ; Print '(' | |||
| ADB1 | EQUS "(" | |||
| ADB2 | LDA fs_reply_stn ; Get cycle number | |||
| ADB5 | JSR print_decimal_3dig ; Print as 3-digit decimal | |||
| ADB8 | JSR print_inline ; Print ') ' | |||
| ADBB | EQUS ") " | |||
| ADC1 | LDY fs_access_level ; Get owner/public flag | |||
| ADC4 | BNE print_public_label ; Non-zero: public access | |||
| ADC6 | JSR print_inline ; Print 'Owner' + CR | |||
| ADC9 | EQUS "Owner." | |||
| ADCF | BNE send_dir_info_req ; Skip public; ALWAYS branch | |||
| ADD1 | .print_public_label←1← ADC4 BNE | |||
| JSR print_inline ; Print 'Public' + CR | ||||
| ADD4 | EQUS "Public." | |||
| ADDB | .send_dir_info_req←1← ADCF BNE | |||
| LDA fs_lib_flags ; Get flags | ||||
| ADDE | PHA ; Save flags | |||
| ADDF | JSR mask_owner_access ; Mask owner access bits | |||
| ADE2 | LDY #&15 ; Y=&15: FS command for dir info | |||
| ADE4 | JSR save_net_tx_cb ; Send request to file server | |||
| ADE7 | INX ; Advance X past header | |||
| ADE8 | LDY #&10 ; Y=&10: print 16 chars | |||
| ADEA | JSR print_chars_from_buf ; Print file entry | |||
| ADED | JSR print_inline ; Print ' Option ' | |||
| ADF0 | EQUS " Option " | |||
| ADFB | LDA fs_boot_option ; Get option byte | |||
| ADFE | TAX ; Transfer to X for table lookup | |||
| ADFF | JSR print_hex_byte ; Print option as hex | |||
| AE02 | JSR print_inline ; Print ' (' | |||
| AE05 | EQUS " (" | |||
| AE07 | LDY option_str_offset_data,x ; Index into option string table | |||
| AE0A | .loop_print_option_str←1← AE13 BNE | |||
| LDA roff_off_string,y ; Get option name character | ||||
| AE0D | BMI print_dir_header ; High bit set: end of string | |||
| AE0F | JSR osasci ; Write character | |||
| AE12 | INY ; Next character | |||
| AE13 | BNE loop_print_option_str ; Loop; ALWAYS branch | |||
| AE15 | .print_dir_header←1← AE0D BMI | |||
| JSR print_inline ; Print ')' + CR + 'Dir. ' | ||||
| AE18 | EQUS ").Dir. " | |||
| AE1F | LDX #&11 ; Offset &11: directory name | |||
| AE21 | JSR print_10_chars ; Print directory name (10 chars) | |||
| AE24 | JSR print_inline ; Print ' Lib. ' | |||
| AE27 | EQUS " Lib. " | |||
| AE31 | LDX #&1b ; Offset &1B: library name | |||
| AE33 | JSR print_10_chars ; Print library name (10 chars) | |||
| AE36 | JSR osnewl ; Write newline (characters 10 and 13) | |||
| AE39 | PLA ; Restore flags | |||
| AE3A | STA fs_lib_flags ; Store restored flags | |||
| AE3D | .setup_ex_pagination←1← AE6E BNE | |||
| STY fs_func_code ; Store entry count | ||||
| AE40 | STY fs_work_4 ; Also store in work_4 | |||
| AE42 | LDX fs_work_5 ; Get command code | |||
| AE44 | STX fs_data_count ; Store in buffer | |||
| AE47 | LDX fs_work_7 ; Get entries per page | |||
| AE49 | STX fs_cmd_data ; Store in buffer | |||
| AE4C | LDX #3 ; X=3: buffer offset | |||
| AE4E | JSR copy_arg_to_buf ; Copy argument to buffer | |||
| AE51 | LDY #3 ; Y=3: FS command for examine/cat | |||
| AE53 | JSR save_net_tx_cb ; Send request to file server | |||
| AE56 | INX ; Advance past header | |||
| AE57 | LDA fs_cmd_data ; Get number of entries returned | |||
| AE5A | BEQ jmp_osnewl ; Zero: no more entries | |||
| AE5C | PHA ; Save entry count | |||
| AE5D | .loop_scan_entry_data←1← AE61 BPL | |||
| INY ; Advance Y | ||||
| AE5E | LDA fs_cmd_data,y ; Get entry data byte | |||
| AE61 | BPL loop_scan_entry_data ; Bit 7 clear: more data | |||
| AE63 | STA fs_cmd_lib,y ; Store terminator byte | |||
| AE66 | JSR ex_print_col_sep ; Print entry with column separator | |||
| AE69 | PLA ; Restore entry count | |||
| AE6A | CLC ; Clear carry for addition | |||
| AE6B | ADC fs_work_4 ; Add entries processed | |||
| AE6D | TAY ; Transfer to Y | |||
| AE6E | BNE setup_ex_pagination ; More entries: loop | |||
| fall through ↓ | ||||
Print 10 characters from reply bufferSets Y=10 and falls through to print_chars_from_buf. Used by cmd_ex to print fixed-width directory title, directory name, and library name fields.
|
||||
| AE70 | .print_10_chars←3← ADAB JSR← AE21 JSR← AE33 JSR | |||
| LDY #&0a ; Y=10: characters to print | ||||
| fall through ↓ | ||||
Print Y characters from buffer via OSASCILoops Y times, loading each byte from l0f05+X and printing it via OSASCI. Advances X after each character, leaving X pointing past the last printed byte.
|
||||||
| AE72 | .print_chars_from_buf←2← ADEA JSR← AE7A BNE | |||||
| LDA fs_cmd_data,x ; Get character from buffer | ||||||
| AE75 | JSR osasci ; Write character | |||||
| AE78 | INX ; Next buffer position | |||||
| AE79 | DEY ; Decrement count | |||||
| AE7A | BNE print_chars_from_buf ; Loop until 10 printed | |||||
| AE7C | RTS ; Return | |||||
| AE7D | .jmp_osnewl←1← AE5A BEQ | |||||
| JMP osnewl ; Write newline (characters 10 and 13) | ||||||
Parse command argument from offset zeroSets Y=0 and falls through to parse_filename_arg for GSREAD-based filename parsing with prefix character handling. |
|
| AE80 | .parse_cmd_arg_y0←2← 9CF8 JSR← A1AF JSR |
| LDY #0 ; Y=0: start of command line | |
| fall through ↓ | |
Parse filename via GSREAD with prefix handlingCalls gsread_to_buf to read the command line string into the &0E30 buffer, then falls through to parse_access_prefix to process '&', ':', '.', and '#' prefix characters. |
|
| AE82 | .parse_filename_arg←4← AD25 JSR← AD90 JSR← AF57 JSR← B347 JSR |
| JSR gsread_to_buf ; Read string to buffer via GSREAD | |
| fall through ↓ | |
Parse access and FS selection prefix charactersExamines the first character(s) of the parsed buffer at &0E30 for prefix characters: '&' sets the FS selection flag (bit 6 of l1071) and strips the prefix, ':' with '.' also triggers FS selection, '#' is accepted as a channel prefix. Raises 'Bad file name' for invalid combinations like '&.' followed by CR. |
|
| AE85 | .parse_access_prefix←4← 92DA JSR← 9382 JSR← 93BB JSR← 992A JSR |
| LDA fs_filename_buf ; Get first parsed character | |
| AE88 | EOR #&26 ; Is it '&'? |
| AE8A | BNE check_colon_prefix ; No: check for ':' prefix |
| AE8C | LDA fs_lib_flags ; Get flags |
| AE8F | ORA #&40 ; Set FS selection flag (bit 6) |
| AE91 | STA fs_lib_flags ; Store updated flags |
| AE94 | JSR strip_token_prefix ; Remove '&' prefix character |
| AE97 | LDA fs_filename_buf ; Get next character |
| AE9A | EOR #&2e ; Is it '.'? |
| AE9C | BNE check_hash_prefix ; No: check for '#' |
| AE9E | LDA fs_filename_buf_1 ; Get char after '.' |
| AEA1 | EOR #&0d ; Is it CR (end of line)? |
| AEA3 | BEQ error_bad_prefix ; Yes: '&.' + CR only = bad filename |
| fall through ↓ | |
Strip first character from parsed token bufferShifts all bytes in the &0E30 buffer left by one position (removing the first character), then trims any trailing spaces by replacing them with CR terminators. Used after consuming a prefix character like '&' or ':'. |
|
| AEA5 | .strip_token_prefix←5← 9308 JSR← 93A2 JSR← 93A8 JSR← AE94 JSR← AEE7 BNE |
| TXA ; Save X | |
| AEA6 | PHA ; Push X |
| AEA7 | LDX #&ff ; X=&FF, will increment to 0 |
| AEA9 | .loop_shift_str_left←1← AEB2 BNE |
| INX ; Increment X | |
| AEAA | LDA fs_filename_buf_1,x ; Get character at offset+1 |
| AEAD | STA fs_filename_buf,x ; Store at offset (shift left) |
| AEB0 | EOR #&0d ; Is it CR (end of line)? |
| AEB2 | BNE loop_shift_str_left ; No: continue shifting |
| AEB4 | TXA ; Get shifted string length |
| AEB5 | BEQ done_strip_prefix ; Zero length: skip trailing trim |
| AEB7 | .loop_trim_trailing←1← AEC4 BNE |
| LDA fs_filename_buf_m1,x ; Get character at end of string | |
| AEBA | EOR #&20 ; Is it a space? |
| AEBC | BNE done_strip_prefix ; No: done trimming |
| AEBE | LDA #&0d ; Replace trailing space with CR |
| AEC0 | STA fs_filename_buf_m1,x ; Store CR |
| AEC3 | DEX ; Move back |
| AEC4 | BNE loop_trim_trailing ; Loop while more trailing spaces |
| AEC6 | .done_strip_prefix←2← AEB5 BEQ← AEBC BNE |
| PLA ; Restore X | |
| AEC7 | TAX ; Transfer back to X |
| AEC8 | .return_from_strip_prefix←3← AECB BEQ← AED2 BNE← AEDD BNE |
| RTS ; Return | |
| AEC9 | .check_hash_prefix←1← AE9C BNE |
| EOR #&23 ; Is it '#'? | |
| AECB | BEQ return_from_strip_prefix ; Yes: '#' prefix accepted |
| AECD | .error_bad_prefix←1← AEA3 BEQ |
| JMP error_bad_filename ; Bad filename error | |
| AED0 | .check_colon_prefix←1← AE8A BNE |
| EOR #&1c ; Check for ':' prefix | |
| AED2 | BNE return_from_strip_prefix ; Neither '&' nor ':': no prefix |
| AED4 | LDA fs_filename_buf_2 ; Get character after ':' |
| AED7 | EOR #&2e ; Is it '.'? |
| AED9 | BEQ set_fs_select_flag ; Yes: ':.' qualified prefix |
| AEDB | EOR #&23 ; Is it CR (end of line)? |
| AEDD | BNE return_from_strip_prefix ; No: no FS prefix, return |
| AEDF | .set_fs_select_flag←1← AED9 BEQ |
| LDA fs_lib_flags ; Get flags | |
| AEE2 | ORA #&40 ; Set FS selection flag (bit 6) |
| AEE4 | STA fs_lib_flags ; Store updated flags |
| AEE7 | BNE strip_token_prefix ; ALWAYS branch |
| AEE9 | .option_str_offset_data←1← AE07 LDY |
| BRK ; Data: option string offset table | |
| AEEA | EQUS "!.U" |
| AEED | .roff_off_string←1← AE0A LDA |
| EQUS "Off" | |
Copy argument to TX buffer from offset zeroSets X=0 and falls through to copy_arg_to_buf then copy_arg_validated. Provides the simplest entry point for copying a single parsed argument into the TX buffer at position zero. |
|
| AEF0 | .copy_arg_to_buf_x0←6← 8DB1 JSR← 8E0A JSR← 9938 JSR← 9B2A JSR← A281 JSR← AF5A JSR |
| LDX #0 ; X=0: start of buffer | |
| fall through ↓ | |
Copy argument to TX buffer with Y=0Sets Y=0 and falls through to copy_arg_validated with carry set, enabling '&' character validation. X must already contain the destination offset within the TX buffer. |
|
| AEF2 | .copy_arg_to_buf←10← 99EA JSR← 9B23 JSR← 9B46 JSR← 9CFD JSR← A1B4 JSR← A1E1 JSR← AD2A JSR← AD95 JSR← AE4E JSR← B35C JSR |
| LDY #0 ; Y=0: start of argument | |
| fall through ↓ | |
Copy command line characters to TX bufferCopies characters from (fs_crc_lo)+Y to l0f05+X until a CR terminator is reached. With carry set, validates each character against '&' — raising 'Bad file name' if found — to prevent FS selector characters from being embedded in filenames.
|
||||||||
| AEF4 | .copy_arg_validated←3← 8DAC JSR← 940A JSR← 9440 JSR | |||||||
| SEC ; Set carry: enable '&' validation | ||||||||
| AEF5 | .loop_copy_char←1← AF0B BNE | |||||||
| LDA (fs_crc_lo),y ; Get character from command line | ||||||||
| AEF7 | STA fs_cmd_data,x ; Store in TX buffer | |||||||
| AEFA | BCC advance_positions ; Carry clear: skip validation | |||||||
| AEFC | CMP #&21 ; Is it '!' or above? | |||||||
| AEFE | EOR #&26 ; Is it '&'? | |||||||
| AF00 | BNE restore_after_check ; No: continue copying | |||||||
| AF02 | JMP error_bad_filename ; '&' in filename: bad filename | |||||||
| AF05 | .restore_after_check←1← AF00 BNE | |||||||
| EOR #&26 ; Restore A (undo '&' EOR) | ||||||||
| AF07 | .advance_positions←1← AEFA BCC | |||||||
| INX ; Advance buffer position | ||||||||
| AF08 | INY ; Advance source position | |||||||
| AF09 | EOR #&0d ; Is it CR (end of line)? | |||||||
| AF0B | BNE loop_copy_char ; No: continue copying | |||||||
| AF0D | .return_from_copy_arg←1← AF23 BMI | |||||||
| RTS ; Return | ||||||||
| AF0E | EQUS "Load" | |||||||
Clear FS selection flags from options wordANDs the l1071 flags byte with &1F, clearing the FS selection flag (bit 6) and other high bits to retain only the 5-bit owner access mask. Called before parsing to reset the prefix state from a previous command. |
|
| AF12 | .mask_owner_access←13← 8E25 JSR← 937C JSR← 93B6 JSR← 9927 JSR← 9D49 JSR← 9E2A JSR← A1AC JSR← A238 JSR← A242 JSR← A8DB JSR← AD00 JSR← ADDF JSR← B33D JSR |
| LDA fs_lib_flags ; Get flags | |
| AF15 | AND #&1f ; Mask to low 5 bits only |
| AF17 | STA fs_lib_flags ; Store masked flags |
| AF1A | RTS ; Return |
| AF1B | EQUS "Run" |
| AF1E | .fsreply_0_print_dir |
| LDX #0 ; X=0: start from first entry | |
| AF20 | .loop_scan_entries←1← AF40 BNE |
| LDA fs_cmd_data,x ; Get entry byte from buffer | |
| AF23 | BMI return_from_copy_arg ; High bit set: end of entries |
| AF25 | BNE print_entry_char ; Non-zero: printable character |
| fall through ↓ | |
Print column separator or newline for *Ex/*CatIn *Cat mode, increments a column counter modulo 4 and prints a two-space separator between entries, with a newline at the end of each row. In *Ex mode (fs_spool_handle negative), prints a newline after every entry. Scans the entry data and loops back to print the next entry's characters. |
|
| AF27 | .ex_print_col_sep←1← AE66 JSR |
| LDY fs_spool_handle ; Get column counter | |
| AF29 | BMI print_col_newline ; Negative: newline mode (Ex) |
| AF2B | INY ; Increment column counter |
| AF2C | TYA ; Transfer to A |
| AF2D | AND #3 ; Modulo 4 (Cat: 3 per row) |
| AF2F | STA fs_spool_handle ; Store updated counter |
| AF31 | BEQ print_col_newline ; Zero: row full, print newline |
| AF33 | JSR print_inline ; Print ' ' column separator |
| AF36 | EQUS " " |
| AF38 | BNE next_col_entry ; Skip newline; ALWAYS branch |
| AF3A | .print_col_newline←2← AF29 BMI← AF31 BEQ |
| LDA #&0d ; CR character for newline | |
| AF3C | .print_entry_char←1← AF25 BNE |
| JSR osasci ; Write character 13 | |
| AF3F | .next_col_entry←1← AF38 BNE |
| INX ; Advance to next entry | |
| AF40 | BNE loop_scan_entries ; Loop for more entries |
| AF42 | EOR zp_0078 ; Embedded string data 'Exec' |
| AF44 | ADC zp_0063 ; Embedded string data (contd) |
| fall through ↓ | |
*Remove command handlerLike *Delete but suppresses the 'Not found' error, making it suitable for use in programs where a missing file should not cause an unexpected error. Validates that exactly one argument is present — raises 'Syntax' if extra arguments follow. Parses the filename via parse_filename_arg, copies it to the TX buffer, and sends FS command code &14 with the V flag set via BIT for save_net_tx_cb_vset dispatch.
|
||||
| AF46 | .cmd_remove | |||
| TYA ; Save command line offset | ||||
| AF47 | PHA ; Push onto stack | |||
| AF48 | JSR skip_to_next_arg ; Skip to check for extra arguments | |||
| AF4B | CMP #&0d ; End of line? | |||
| AF4D | BEQ done_extra_arg_check ; Yes: single arg, proceed | |||
| AF4F | JMP error_syntax ; No: extra args, syntax error | |||
| AF52 | .done_extra_arg_check←1← AF4D BEQ | |||
| PLA ; Restore command line offset | ||||
| AF53 | TAY ; Transfer to Y | |||
| AF54 | JSR save_ptr_to_os_text ; Save text pointer for parsing | |||
| AF57 | JSR parse_filename_arg ; Parse filename argument | |||
| AF5A | JSR copy_arg_to_buf_x0 ; Copy filename to TX buffer | |||
| AF5D | LDY #&14 ; Y=&14: *Delete FS command code | |||
| AF5F | BIT bit_test_ff ; Set V flag (via BIT #&FF) | |||
| AF62 | JMP save_net_tx_cb_vset ; Send to FS with V-flag dispatch | |||
Print decimal number with leading zero suppressionSets V via BIT bit_test_ff to enable leading zero suppression, then falls through to print_decimal_3dig. Used by print_station_id for compact station number display.
|
||||
| AF65 | .print_num_no_leading←1← 8FF3 JSR | |||
| BIT bit_test_ff ; Set V (suppress leading zeros) | ||||
| fall through ↓ | ||||
Print byte as 3-digit decimal via OSASCIExtracts hundreds, tens and units digits by successive calls to print_decimal_digit. The V flag controls leading zero suppression: if set, zero digits are skipped until a non-zero digit appears. V is always cleared before the units digit to ensure at least one digit is printed.
|
||||||
| AF68 | .print_decimal_3dig←3← ADB5 JSR← B179 JSR← B190 JMP | |||||
| TAY ; Transfer value to Y (remainder) | ||||||
| AF69 | LDA #&64 ; A=100: hundreds divisor | |||||
| AF6B | JSR print_decimal_digit ; Print hundreds digit | |||||
| AF6E | LDA #&0a ; A=10: tens divisor | |||||
| AF70 | JSR print_decimal_digit ; Print tens digit | |||||
| AF73 | CLV ; Clear V (always print units) | |||||
| AF74 | LDA #1 ; A=1: units divisor | |||||
| fall through ↓ | ||||||
Print one decimal digit by repeated subtractionInitialises X to '0'-1 and loops, incrementing X while subtracting the divisor from Y. On underflow, adds back the divisor to get the remainder in Y. If V is set, suppresses leading zeros by skipping the OSASCI call when the digit is '0'.
|
|||||||||
| AF76 | .print_decimal_digit←2← AF6B JSR← AF70 JSR | ||||||||
| STA fs_error_ptr ; Store divisor | |||||||||
| AF78 | TYA ; Get remaining value | ||||||||
| AF79 | LDX #&2f ; X='0'-1: digit counter | ||||||||
| AF7B | SEC ; Set carry for subtraction | ||||||||
| AF7C | PHP ; Save V flag for leading zero check | ||||||||
| AF7D | .loop_divide_digit←1← AF80 BCS | ||||||||
| INX ; Count quotient digit | |||||||||
| AF7E | SBC fs_error_ptr ; Subtract divisor | ||||||||
| AF80 | BCS loop_divide_digit ; No underflow: continue dividing | ||||||||
| AF82 | ADC fs_error_ptr ; Add back divisor (get remainder) | ||||||||
| AF84 | TAY ; Remainder to Y for next digit | ||||||||
| AF85 | TXA ; Digit character to A | ||||||||
| AF86 | PLP ; Restore V flag | ||||||||
| AF87 | BVC print_nonzero_digit ; V clear: always print digit | ||||||||
| AF89 | CMP #&30 ; V set: is digit '0'? | ||||||||
| AF8B | BEQ return_from_print_digit ; Yes: suppress leading zero | ||||||||
| AF8D | .print_nonzero_digit←1← AF87 BVC | ||||||||
| LDX fs_error_ptr ; Save divisor across OSASCI call | |||||||||
| AF8F | JSR osasci ; Write character | ||||||||
| AF92 | STX fs_error_ptr ; Restore divisor | ||||||||
| AF94 | .return_from_print_digit←1← AF8B BEQ | ||||||||
| RTS ; Return | |||||||||
| AF95 | .save_ptr_to_os_text←8← 9D4F JSR← A1A9 JSR← AD22 JSR← AD84 JSR← AF54 JSR← B018 JSR← B1F2 JSR← B344 JSR |
| PHA ; Save A | |
| AF96 | LDA fs_crc_lo ; Copy text pointer low byte |
| AF98 | STA os_text_ptr ; To OS text pointer low |
| AF9A | LDA fs_crc_hi ; Copy text pointer high byte |
| AF9C | STA os_text_ptr_hi ; To OS text pointer high |
| AF9E | PLA ; Restore A |
| AF9F | RTS ; Return |
| AFA0 | .loop_advance_char←1← AFAB BNE |
| INY ; Advance past current character | |
| fall through ↓ | |
Advance past spaces to the next command argumentScans (fs_crc_lo)+Y for space characters, advancing Y past each one. Returns with A holding the first non-space character, or CR if the end of line is reached. Used by *CDir and *Remove to detect extra arguments.
|
||||||
| AFA1 | .skip_to_next_arg←2← AD03 JSR← AF48 JSR | |||||
| LDA (fs_crc_lo),y ; Load char from command line | ||||||
| AFA3 | CMP #&20 ; Space? | |||||
| AFA5 | BEQ loop_skip_space_chars ; Yes: skip trailing spaces | |||||
| AFA7 | CMP #&0d ; CR (end of line)? | |||||
| AFA9 | BEQ return_from_skip_arg ; Yes: return (at end) | |||||
| AFAB | BNE loop_advance_char ; ALWAYS branch | |||||
| AFAD | .loop_skip_space_chars←2← AFA5 BEQ← AFB2 BEQ | |||||
| INY ; Advance past space | ||||||
| AFAE | LDA (fs_crc_lo),y ; Load next character | |||||
| AFB0 | CMP #&20 ; Still a space? | |||||
| AFB2 | BEQ loop_skip_space_chars ; Yes: skip multiple spaces | |||||
| AFB4 | .return_from_skip_arg←1← AFA9 BEQ | |||||
| RTS ; Return (at next argument) | ||||||
Copy text pointer to spool buffer pointerSaves fs_crc_lo/hi into fs_options/fs_block_offset for use as the spool buffer pointer. Preserves A on the stack. Called by *PS and *PollPS before parsing their arguments. |
|
| AFB5 | .save_ptr_to_spool_buf←2← AFDB JSR← B1AE JSR |
| PHA ; Save A | |
| AFB6 | LDA fs_crc_lo ; Copy text pointer low byte |
| AFB8 | STA fs_options ; To spool buffer pointer low |
| AFBA | LDA fs_crc_hi ; Copy text pointer high byte |
| AFBC | STA fs_block_offset ; To spool buffer pointer high |
| AFBE | PLA ; Restore A |
| AFBF | RTS ; Return |
Initialise spool drive page pointersCalls get_ws_page to read the workspace page number for the current ROM slot, stores it as the spool drive page high byte (l00af), and clears the low byte (l00ae) to zero. Preserves Y on the stack. |
|
| AFC0 | .init_spool_drive←2← AFD8 JSR← B1A1 JSR |
| TYA ; Save Y | |
| AFC1 | PHA ; Push it |
| AFC2 | JSR get_ws_page ; Get workspace page number |
| AFC5 | STA addr_work ; Store as spool drive page high |
| AFC7 | PLA ; Restore Y |
| AFC8 | TAY ; Transfer to Y |
| AFC9 | LDA #0 ; A=0 |
| AFCB | STA work_ae ; Clear spool drive page low |
| AFCD | RTS ; Return |
*PS command handlerChecks the printer server availability flag; raises 'Printer busy' if unavailable. Initialises the spool drive and buffer pointer, then dispatches on argument type: no argument branches to no_ps_name_given, a leading digit branches to save_ps_cmd_ptr as a station number, otherwise parses a named PS address via load_ps_server_addr and parse_fs_ps_args.
|
||||
| AFCE | .cmd_ps | |||
| LDA #1 ; A=1: check printer ready | ||||
| AFD0 | BIT ws_0d6a ; Test printer server workspace flag | |||
| AFD3 | BNE done_ps_available ; Non-zero: printer available | |||
| AFD5 | JMP err_printer_busy ; Printer not available: error | |||
| AFD8 | .done_ps_available←1← AFD3 BNE | |||
| JSR init_spool_drive ; Initialise spool drive | ||||
| AFDB | JSR save_ptr_to_spool_buf ; Save pointer to spool buffer | |||
| AFDE | LDA (fs_options),y ; Get first argument character | |||
| AFE0 | CMP #&0d ; End of command line? | |||
| AFE2 | BEQ no_ps_name_given ; Yes: no argument given | |||
| AFE4 | CLV ; Clear V (= explicit PS name given) | |||
| AFE5 | JSR is_decimal_digit ; Is first char a decimal digit? | |||
| AFE8 | BCC save_ps_cmd_ptr ; Yes: station number, skip PS name | |||
| AFEA | TYA ; PS name follows | |||
| AFEB | PHA ; Save Y | |||
| AFEC | JSR load_ps_server_addr ; Load PS server address | |||
| AFEF | PLA ; Restore Y | |||
| AFF0 | TAY ; Back to Y register | |||
| AFF1 | JSR parse_fs_ps_args ; Parse FS/PS arguments | |||
| AFF4 | JMP store_ps_station ; Jump to store station address | |||
Copy printer server template at offset &1CSets Y=&1C and falls through to copy_ps_data. Called during workspace initialisation (svc_2_private_workspace) to set up the printer server template at the standard offset. |
|
| AFF7 | .copy_ps_data_y1c←1← 8EE0 JSR |
| LDY #&1c ; Start at offset &1C | |
| fall through ↓ | |
Copy 8-byte printer server template to RX bufferCopies 8 bytes of default printer server data into the RX buffer at the current Y offset. Uses indexed addressing: LDA ps_template_base,X with X starting at &F8, so the effective read address is ps_template_base+&F8 = ps_template_data (&8E43). This 6502 trick reaches data 248 bytes past the base label using a single instruction. |
|
| AFF9 | .copy_ps_data←1← B1D4 JSR |
| LDX #&f8 ; X=&F8: offset into template | |
| AFFB | .loop_copy_ps_tmpl←1← B002 BNE |
| LDA ps_template_base,x ; Get template byte | |
| AFFE | STA (net_rx_ptr),y ; Store in RX buffer |
| B000 | INY ; Next destination offset |
| B001 | INX ; Next source offset |
| B002 | BNE loop_copy_ps_tmpl ; Loop until X wraps to 0 |
| B004 | RTS ; Return |
| B005 | .no_ps_name_given←1← AFE2 BEQ |
| BIT bit_test_ff ; Set V (= no explicit PS name) | |
| B008 | .save_ps_cmd_ptr←1← AFE8 BCC |
| STY ws_ptr_hi ; Save command line pointer | |
| B00A | BVS done_ps_name_parse ; V set: skip PS name parsing |
| B00C | LDX #6 ; Max 6 characters for PS name |
| B00E | LDY #&1c ; Buffer offset &1C for PS name |
| B010 | LDA #&20 ; Space character |
| B012 | .loop_pad_ps_name←1← B016 BNE |
| STA (net_rx_ptr),y ; Fill buffer with space | |
| B014 | INY ; Next position |
| B015 | DEX ; Count down |
| B016 | BNE loop_pad_ps_name ; Loop until 6 spaces filled |
| B018 | JSR save_ptr_to_os_text ; Save text pointer |
| B01B | LDY ws_ptr_hi ; Restore command line pointer |
| B01D | JSR gsinit ; Initialise string reading |
| B020 | BEQ done_ps_name_parse ; Empty string: skip to send |
| B022 | LDX #6 ; Max 6 characters |
| B024 | STY ws_ptr_hi ; Save updated string pointer |
| B026 | LDY #&1c ; Buffer offset for PS name |
| B028 | STY table_idx ; Save buffer position |
| B02A | .loop_read_ps_char←1← B036 BNE |
| LDY ws_ptr_hi ; Restore string pointer | |
| B02C | JSR gsread ; Read next character |
| B02F | STY ws_ptr_hi ; Save updated pointer |
| B031 | BCS done_ps_name_parse ; End of string: go to send |
| B033 | JSR store_char_uppercase ; Store char uppercased in buffer |
| B036 | BNE loop_read_ps_char ; Loop for more characters |
| B038 | .done_ps_name_parse←3← B00A BVS← B020 BEQ← B031 BCS |
| JSR reverse_ps_name_to_tx ; Copy reversed PS name to TX | |
| B03B | JSR send_net_packet ; Send PS status request |
| B03E | JSR pop_requeue_ps_scan ; Pop and requeue PS scan |
| B041 | JSR load_ps_server_addr ; Load PS server address |
| B044 | LDA #0 ; A=0 |
| B046 | TAX ; X=&00 |
| B047 | LDY #&24 ; Offset &24 in buffer |
| B049 | STA (net_rx_ptr),y ; Clear PS status byte |
| B04B | .loop_pop_ps_slot←1← B073 BNE |
| PLA ; Get slot offset from stack | |
| B04C | BEQ done_ps_scan ; Zero: all slots done |
| B04E | PHA ; Save slot offset |
| B04F | TAY ; Transfer to Y |
| B050 | LDA (nfs_workspace),y ; Read slot status |
| B052 | BPL done_ps_slot_mark ; Bit 7 clear: slot inactive |
| B054 | JSR advance_y_by_4 ; Advance Y by 4 (to status page) |
| B057 | LDA (nfs_workspace),y ; Read status page pointer |
| B059 | STA work_ae ; Store pointer low |
| B05B | LDA (work_ae,x) ; Read printer status byte |
| B05D | BNE done_ps_slot_mark ; Non-zero (busy): skip |
| B05F | DEY ; Back to network number |
| B060 | LDA (nfs_workspace),y ; Read network number |
| B062 | STA fs_work_6 ; Store network number |
| B064 | DEY ; Back to station number |
| B065 | LDA (nfs_workspace),y ; Read station number |
| B067 | STA fs_work_5 ; Store station low |
| B069 | LDY #&24 ; Offset &24 in buffer |
| B06B | STA (net_rx_ptr),y ; Store ready station in buffer |
| B06D | .done_ps_slot_mark←2← B052 BPL← B05D BNE |
| PLA ; Retrieve slot offset | |
| B06E | TAY ; Transfer to Y |
| B06F | LDA #&3f ; Mark slot as processed (&3F) |
| B071 | STA (nfs_workspace),y ; Write marker to workspace |
| B073 | BNE loop_pop_ps_slot ; ALWAYS branch |
| B075 | .done_ps_scan←1← B04C BEQ |
| JSR print_printer_server_is ; Print 'Printer server is ' | |
| B078 | LDY #&24 ; Offset &24: PS station number |
| B07A | LDA (net_rx_ptr),y ; Get stored station number |
| B07C | BNE print_ps_now ; Non-zero: server changed |
| B07E | JSR print_inline ; Print 'still ' |
| B081 | EQUS "still " |
| B087 | CLV ; Clear V |
| B088 | BVC done_ps_status_msg ; ALWAYS branch |
| B08A | .print_ps_now←1← B07C BNE |
| JSR print_inline ; Print 'now ' | |
| B08D | EQUS "now " |
| B091 | NOP ; Padding |
| B092 | .done_ps_status_msg←1← B088 BVC |
| JSR print_fs_info_newline ; Print FS info and newline | |
| B095 | .store_ps_station←1← AFF4 JMP |
| LDY #2 ; Workspace offset 2 | |
| B097 | LDA fs_work_5 ; Get station low |
| B099 | STA (nfs_workspace),y ; Store in workspace |
| B09B | INY ; Y=&03 |
| B09C | LDA fs_work_6 ; Get network number |
| B09E | STA (nfs_workspace),y ; Store in workspace |
| B0A0 | RTS ; Return |
Print 'File server ' prefixUses print_inline to output 'File' then falls through to the shared ' server is ' suffix at print_printer_server_is. |
|
| B0A1 | .print_file_server_is←1← A083 JSR |
| JSR print_inline ; Print 'File' | |
| B0A4 | EQUS "File" |
| B0A8 | CLV ; Clear V |
| B0A9 | BVC print_server_is_suffix ; ALWAYS branch |
Print 'Printer server is ' prefixUses print_inline to output the full label 'Printer server is ' with trailing space. |
|
| B0AB | .print_printer_server_is←2← B075 JSR← B21C JSR |
| JSR print_inline ; Print 'Printer' | |
| B0AE | EQUS "Printer" |
| B0B5 | NOP ; Padding |
| B0B6 | .print_server_is_suffix←1← B0A9 BVC |
| JSR print_inline ; Print ' server is ' | |
| B0B9 | EQUS " server is " |
| B0C4 | NOP ; Padding |
| B0C5 | RTS ; Return |
Load printer server address from workspaceReads the station and network bytes from workspace offsets 2 and 3 into the station/network variables. |
|
| B0C6 | .load_ps_server_addr←4← AFEC JSR← B041 JSR← B1BF JSR← B21F JSR |
| LDY #2 ; Workspace offset 2 | |
| B0C8 | LDA (nfs_workspace),y ; Read station low |
| B0CA | STA fs_work_5 ; Store station low |
| B0CC | INY ; Y=&03 |
| B0CD | LDA (nfs_workspace),y ; Read network number |
| B0CF | STA fs_work_6 ; Store network number |
| B0D1 | RTS ; Return |
Pop return address and requeue PS slot scanConverts the PS slot flags to a workspace index, writes slot data, and jumps back into the PS scan loop to continue processing. |
|
| B0D2 | .pop_requeue_ps_scan←2← B03E JSR← B219 JSR |
| PLA ; Pop return address low | |
| B0D3 | STA osword_flag ; Save return address low |
| B0D5 | PLA ; Pop return address high |
| B0D6 | STA ws_ptr_lo ; Save return address high |
| B0D8 | LDA #0 ; Push 0 as end-of-list marker |
| B0DA | PHA ; Push it |
| B0DB | LDA #&84 ; Start scanning from offset &84 |
| B0DD | STA ws_ptr_hi ; Store scan position |
| B0DF | LSR econet_flags ; Shift PS slot flags right |
| B0E2 | LDA #3 ; Counter: 3 PS slots |
| B0E4 | .loop_scan_ps_slots←1← B0F6 BNE |
| JSR byte_to_2bit_index ; Convert to 2-bit workspace index | |
| B0E7 | BCS done_ps_slot_scan ; Carry set: no more slots |
| B0E9 | LSR ; Shift right twice |
| B0EA | LSR ; To get slot offset |
| B0EB | TAX ; Transfer to X |
| B0EC | LDA (nfs_workspace),y ; Read slot status byte |
| B0EE | BEQ done_ps_slot_scan ; Zero: empty slot, done |
| B0F0 | CMP #&3f ; Is it processed marker (&3F)? |
| B0F2 | BEQ reinit_ps_slot ; Yes: re-initialise this slot |
| B0F4 | .skip_next_ps_slot←1← B11D JMP |
| INX ; Try next slot | |
| B0F5 | TXA ; Transfer slot index to A |
| B0F6 | BNE loop_scan_ps_slots ; Loop for more slots |
| B0F8 | .reinit_ps_slot←1← B0F2 BEQ |
| TYA ; Y = workspace offset of slot | |
| B0F9 | PHA ; Push slot offset for scan list |
| B0FA | LDA #&7f ; Set active status (&7F) |
| B0FC | STA (nfs_workspace),y ; Write status byte |
| B0FE | INY ; Next byte |
| B0FF | LDA #&9e ; Low byte: workspace page |
| B101 | STA (nfs_workspace),y ; Write workspace pointer low |
| B103 | LDA #0 ; A=0 |
| B105 | JSR write_two_bytes_inc_y ; Write two zero bytes + advance Y |
| B108 | LDA ws_ptr_hi ; Get current scan page |
| B10A | STA (nfs_workspace),y ; Write RX buffer page low |
| B10C | CLC ; Clear carry for addition |
| B10D | PHP ; Save processor status |
| B10E | ADC #3 ; Advance by 3 pages |
| B110 | PLP ; Restore processor status |
| B111 | STA ws_ptr_hi ; Update scan position |
| B113 | JSR write_ps_slot_byte_ff ; Write buffer page + &FF bytes |
| B116 | LDA ws_ptr_hi ; Get updated scan position |
| B118 | STA (nfs_workspace),y ; Write RX buffer page high |
| B11A | .write_ps_slot_hi_link |
| JSR write_ps_slot_byte_ff ; Write another page + &FF bytes | |
| B11D | JMP skip_next_ps_slot ; Continue scanning slots |
| B120 | .done_ps_slot_scan←2← B0E7 BCS← B0EE BEQ |
| ASL econet_flags ; Shift PS slot flags back | |
| B123 | LDA ws_ptr_lo ; Restore return address high |
| B125 | PHA ; Push onto stack |
| B126 | LDA osword_flag ; Restore return address low |
| B128 | PHA ; Push onto stack |
| B129 | LDA #&0a ; Delay counter: 10 |
| B12B | TAY ; Y=&0a |
| B12C | TAX ; X=&0a |
| B12D | STA fs_work_4 ; Outer loop counter = 10 |
| B12F | .loop_ps_delay←3← B130 BNE← B133 BNE← B137 BNE |
| DEY ; Decrement Y (inner loop) | |
| B130 | BNE loop_ps_delay ; Inner loop: 10 iterations |
| B132 | DEX ; Decrement X (middle loop) |
| B133 | BNE loop_ps_delay ; Middle loop: 10 iterations |
| B135 | DEC fs_work_4 ; Decrement outer counter |
| B137 | BNE loop_ps_delay ; Outer loop: ~1000 delay cycles |
| B139 | RTS ; Return |
Write buffer page byte and two &FF markersStores the buffer page byte at the current Y offset in workspace, followed by two &FF sentinel bytes. Advances Y after each write. |
|
| B13A | .write_ps_slot_byte_ff←2← B113 JSR← B11A JSR |
| INY ; Advance Y | |
| B13B | LDA addr_work ; Get buffer page |
| B13D | STA (nfs_workspace),y ; Store in workspace |
| B13F | LDA #&ff ; A=&FF |
| fall through ↓ | |
Write A to two consecutive workspace bytesStores A at the current Y offset via (nfs_workspace),Y then again at Y+1, advancing Y after each write.
|
||||||
| B141 | .write_two_bytes_inc_y←1← B105 JSR | |||||
| INY ; Advance Y | ||||||
| B142 | STA (nfs_workspace),y ; Write byte to workspace | |||||
| B144 | INY ; Advance Y | |||||
| B145 | STA (nfs_workspace),y ; Write byte to workspace | |||||
| B147 | INY ; Advance Y | |||||
| B148 | RTS ; Return | |||||
Reverse-copy printer server name to TX bufferCopies 8 bytes from the RX buffer (offsets &1C-&23) to the TX buffer (offsets &13-&1B) in reversed byte order, pushing onto the stack then popping back. |
|
| B149 | .reverse_ps_name_to_tx←2← B038 JSR← B1A6 JSR |
| LDY #&1c ; Start of PS name at offset &1C | |
| B14B | .loop_push_ps_name←1← B151 BNE |
| LDA (net_rx_ptr),y ; Load byte from RX buffer | |
| B14D | PHA ; Push to stack (for reversal) |
| B14E | INY ; Next source byte |
| B14F | CPY #&24 ; End of PS name field (&24)? |
| B151 | BNE loop_push_ps_name ; No: continue pushing |
| B153 | LDY #&1b ; End of TX name field at &1B |
| B155 | .loop_pop_ps_name←1← B15B BNE |
| PLA ; Pop byte (reversed order) | |
| B156 | STA (net_rx_ptr),y ; Store in RX buffer |
| B158 | DEY ; Previous position |
| B159 | CPY #&13 ; Start of TX field (&13)? |
| B15B | BNE loop_pop_ps_name ; No: continue popping |
| B15D | LDA net_rx_ptr_hi ; Copy RX page to TX |
| B15F | STA net_tx_ptr_hi ; Set TX pointer high |
| B161 | LDA #&10 ; TX offset &10 |
| B163 | STA net_tx_ptr ; Set TX pointer low |
| B165 | LDY #3 ; Copy 4 header bytes |
| B167 | .loop_copy_tx_hdr←1← B16D BPL |
| LDA ps_tx_header_template,y ; Get header template byte | |
| B16A | STA (net_tx_ptr),y ; Store in TX buffer |
| B16C | DEY ; Previous byte |
| B16D | BPL loop_copy_tx_hdr ; Loop until all 4 copied |
| B16F | RTS ; Return |
| ; Printer server TX header template | |
| ; 4-byte header copied to the TX control block by | |
| ; reverse_ps_name_to_tx. Sets up an immediate | |
| ; transmit on port &9F (PS port) to any station. | |
| B170 | .ps_tx_header_template←1← B167 LDA |
| EQUB &80 ; Control byte &80 (immediate TX) | |
| B171 | EQUB &9F ; Port &9F (printer server) |
| B172 | EQUB &FF ; Station &FF (any) |
| B173 | EQUB &FF ; Network &FF (any) |
Print station address as decimal net.stationIf the network number is zero, prints only the station number. Otherwise prints network.station separated by a dot. V flag controls padding with leading spaces for column alignment. |
|
| B174 | .print_station_addr←4← A089 JSR← B225 JSR← B25D JSR← B2B5 JSR |
| PHP ; Save V flag (controls padding) | |
| B175 | LDA fs_work_6 ; Get network number |
| B177 | BEQ skip_if_local_net ; Zero: no network prefix |
| B179 | JSR print_decimal_3dig ; Print network as 3 digits |
| B17C | LDA #&2e ; '.' separator |
| B17E | JSR osasci ; Write character 46 |
| B181 | BIT bit_test_ff ; Set V (suppress station padding) |
| B184 | .skip_if_local_net←1← B177 BEQ |
| BVS print_station_only ; V set: skip padding spaces | |
| B186 | JSR print_inline ; Print 4 spaces (padding) |
| B189 | EQUS " " |
| B18D | .print_station_only←1← B184 BVS |
| LDA fs_work_5 ; Get station number | |
| B18F | PLP ; Restore flags |
| B190 | JMP print_decimal_3dig ; Print station as 3 digits |
| ; PS slot transmit control block template | |
| ; 12-byte Econet TXCB initialisation template for | |
| ; printer server slot buffers. Not referenced by | |
| ; label; accessed indirectly by init_ps_slot_from_rx | |
| ; via LDA write_ps_slot_link_addr,Y where the base | |
| ; address write_ps_slot_hi_link+1 plus Y offset &78 computes to ; &B193. | |
| ; Structure: 4-byte header (control, port, station, | |
| ; network) followed by two 4-byte buffer descriptors | |
| ; (lo address, hi page, end lo, end hi). The hi page | |
| ; bytes at positions 5 and 9 are overwritten with | |
| ; net_rx_ptr_hi during the copy to point into the | |
| ; actual RX buffer page. End bytes &FF are | |
| ; placeholders filled in later by the caller. | |
| B193 | .ps_slot_txcb_template |
| EQUB &80 ; Control byte &80 (immediate TX) | |
| B194 | EQUB &9F ; Port &9F (printer server) |
| B195 | EQUB &00 ; Station 0 (filled in later) |
| B196 | EQUB &00 ; Network 0 (filled in later) |
| B197 | EQUB &14 ; Data buffer start lo (&14) |
| B198 | EQUB &00 ; Data buffer start hi (= rx page) |
| B199 | EQUB &FF ; Data buffer end lo (placeholder) |
| B19A | EQUB &FF ; Data buffer end hi (placeholder) |
| B19B | EQUB &1C ; Reply buffer start lo (&1C) |
| B19C | EQUB &00 ; Reply buffer start hi (= rx page) |
| B19D | EQUB &FF ; Reply buffer end lo (placeholder) |
| B19E | EQUB &FF ; Reply buffer end hi (placeholder) |
*Pollps command handlerInitialises the spool drive, copies the PS name to the TX buffer, and parses an optional station number or PS name argument. Sends a poll request, then prints the server address and name. Iterates through PS slots, displaying each station's status as 'ready', 'busy' (with client station), or 'jammed'. Marks processed slots with &3F.
|
||||
| B19F | .cmd_pollps | |||
| STY ws_ptr_hi ; Save command line pointer high | ||||
| B1A1 | JSR init_spool_drive ; Initialise spool/print drive | |||
| B1A4 | STA fs_work_4 ; Save spool drive number | |||
| B1A6 | JSR reverse_ps_name_to_tx ; Copy PS name to TX buffer | |||
| B1A9 | JSR init_ps_slot_from_rx ; Init PS slot from RX data | |||
| B1AC | LDY ws_ptr_hi ; Restore command line pointer | |||
| B1AE | JSR save_ptr_to_spool_buf ; Save pointer to spool buffer | |||
| B1B1 | LDA (fs_options),y ; Get first argument character | |||
| B1B3 | CMP #&0d ; End of command line? | |||
| B1B5 | BEQ no_poll_name_given ; Yes: no argument given | |||
| B1B7 | CLV ; Clear V (= explicit PS name given) | |||
| B1B8 | JSR is_decimal_digit ; Is first char a decimal digit? | |||
| B1BB | BCC skip_if_no_poll_arg ; Yes: station number, skip PS name | |||
| B1BD | TYA ; PS name follows | |||
| B1BE | PHA ; Save Y | |||
| B1BF | JSR load_ps_server_addr ; Load PS server address | |||
| B1C2 | PLA ; Restore Y | |||
| B1C3 | TAY ; Back to Y register | |||
| B1C4 | JSR parse_fs_ps_args ; Parse FS/PS arguments | |||
| B1C7 | LDY #&7a ; Offset &7A in slot buffer | |||
| B1C9 | LDA fs_work_5 ; Get parsed station low | |||
| B1CB | STA (work_ae),y ; Store station number low | |||
| B1CD | INY ; Y=&7b | |||
| B1CE | LDA fs_work_6 ; Get parsed network number | |||
| B1D0 | STA (work_ae),y ; Store station number high | |||
| B1D2 | LDY #&14 ; Offset &14 in TX buffer | |||
| B1D4 | JSR copy_ps_data ; Copy PS data to TX buffer | |||
| B1D7 | LDA addr_work ; Get buffer page high | |||
| B1D9 | STA net_tx_ptr_hi ; Set TX pointer high byte | |||
| B1DB | LDA #&78 ; Offset &78 in buffer | |||
| B1DD | STA net_tx_ptr ; Set TX pointer low byte | |||
| B1DF | BNE done_poll_name_parse ; ALWAYS branch | |||
| B1E1 | .no_poll_name_given←1← B1B5 BEQ | |||
| BIT bit_test_ff ; Set V (= no explicit PS name) | ||||
| B1E4 | .skip_if_no_poll_arg←1← B1BB BCC | |||
| BVS done_poll_name_parse ; V set (no arg): skip to send | ||||
| B1E6 | LDX #6 ; Max 6 characters for PS name | |||
| B1E8 | LDY #&14 ; Buffer offset for PS name | |||
| B1EA | LDA #&20 ; Space character | |||
| B1EC | .loop_pad_poll_name←1← B1F0 BNE | |||
| STA (net_rx_ptr),y ; Fill buffer position with space | ||||
| B1EE | INY ; Next position | |||
| B1EF | DEX ; Count down | |||
| B1F0 | BNE loop_pad_poll_name ; Loop until 6 spaces filled | |||
| B1F2 | JSR save_ptr_to_os_text ; Save pointer to OS text | |||
| B1F5 | LDY ws_ptr_hi ; Restore command line pointer | |||
| B1F7 | JSR gsinit ; Initialise string reading | |||
| B1FA | BEQ done_poll_name_parse ; Empty string: skip to send | |||
| B1FC | LDX #6 ; Max 6 characters | |||
| B1FE | STY ws_ptr_hi ; Save updated string pointer | |||
| B200 | LDY #&14 ; Buffer offset for PS name | |||
| B202 | STY table_idx ; Save buffer position | |||
| B204 | .loop_read_poll_char←1← B210 BNE | |||
| LDY ws_ptr_hi ; Restore string pointer | ||||
| B206 | JSR gsread ; Read next char from string | |||
| B209 | STY ws_ptr_hi ; Save updated string pointer | |||
| B20B | BCS done_poll_name_parse ; End of string: go to send | |||
| B20D | JSR store_char_uppercase ; Store char uppercased in buffer | |||
| B210 | BNE loop_read_poll_char ; Loop if more chars to copy | |||
| B212 | .done_poll_name_parse←4← B1DF BNE← B1E4 BVS← B1FA BEQ← B20B BCS | |||
| LDA #&80 ; Enable escape checking | ||||
| B214 | STA escapable ; Set escapable flag | |||
| B216 | JSR send_net_packet ; Send the poll request packet | |||
| B219 | JSR pop_requeue_ps_scan ; Pop and requeue PS scan | |||
| B21C | JSR print_printer_server_is ; Print 'Printer server ' | |||
| B21F | JSR load_ps_server_addr ; Load PS server address | |||
| B222 | BIT bit_test_ff ; Set V and N flags | |||
| B225 | JSR print_station_addr ; Print station address | |||
| B228 | JSR print_inline ; Print ' "' | |||
| B22B | EQUS " "" | |||
| B22D | LDY #&1c ; Start of PS name in buffer | |||
| B22F | .loop_print_poll_name←1← B23B BNE | |||
| LDA (net_rx_ptr),y ; Get character from name field | ||||
| B231 | CMP #&20 ; Is it a space? | |||
| B233 | BEQ done_poll_name_print ; Yes: end of name | |||
| B235 | JSR osasci ; Write character | |||
| B238 | INY ; Next character | |||
| B239 | CPY #&22 ; Past end of name field? | |||
| B23B | BNE loop_print_poll_name ; No: continue printing name | |||
| B23D | .done_poll_name_print←1← B233 BEQ | |||
| JSR print_inline ; Print '"' + CR | ||||
| B240 | EQUS ""." | |||
| B242 | NOP ; Padding byte | |||
| B243 | .loop_pop_poll_slot←1← B2C1 BNE | |||
| PLA ; Get slot offset from stack | ||||
| B244 | BEQ return_from_poll_slots ; Zero: all slots done, return | |||
| B246 | PHA ; Save slot offset | |||
| B247 | TAY ; Transfer to Y | |||
| B248 | LDA (nfs_workspace),y ; Read slot status byte | |||
| B24A | BPL done_poll_slot_mark ; Bit 7 clear: slot inactive | |||
| B24C | INY ; Advance to station number | |||
| B24D | INY ; Offset+2 in slot | |||
| B24E | LDA (nfs_workspace),y ; Read station number low | |||
| B250 | STA fs_work_5 ; Store station low | |||
| B252 | INY ; Next byte (offset+3) | |||
| B253 | LDA (nfs_workspace),y ; Read network number | |||
| B255 | STA fs_work_6 ; Store network number | |||
| B257 | INY ; Next byte (offset+4) | |||
| B258 | LDA (nfs_workspace),y ; Read status page pointer | |||
| B25A | STA work_ae ; Store pointer low | |||
| B25C | CLV ; Clear V flag | |||
| B25D | JSR print_station_addr ; Print station address (V=0) | |||
| B260 | JSR print_inline ; Print ' is ' | |||
| B263 | EQUS " is " | |||
| B267 | LDX #0 ; X=0 for indirect indexed access | |||
| B269 | LDA (work_ae,x) ; Read printer status byte | |||
| B26B | BNE check_poll_jammed ; Non-zero: not ready | |||
| B26D | JSR print_inline ; Print 'ready' | |||
| B270 | EQUS "ready" | |||
| B275 | CLV ; Clear V | |||
| B276 | BVC done_poll_status_line ; ALWAYS branch | |||
| B278 | .check_poll_jammed←1← B26B BNE | |||
| CMP #2 ; Status = 2? | ||||
| B27A | BNE check_poll_busy ; No: check for busy | |||
| B27C | .print_poll_jammed←1← B28A BNE | |||
| JSR print_inline ; Print 'jammed' | ||||
| B27F | EQUS "jammed" | |||
| B285 | CLV ; Clear V | |||
| B286 | BVC done_poll_status_line ; ALWAYS branch | |||
| B288 | .check_poll_busy←1← B27A BNE | |||
| CMP #1 ; Status = 1? | ||||
| B28A | BNE print_poll_jammed ; Not 1 or 2: default to jammed | |||
| B28C | JSR print_inline ; Print 'busy' | |||
| B28F | EQUS "busy" | |||
| B293 | INC work_ae ; Advance past status byte | |||
| B295 | LDA (work_ae,x) ; Read client station number | |||
| B297 | STA fs_work_5 ; Store station low | |||
| B299 | BEQ done_poll_status_line ; Zero: no client info, skip | |||
| B29B | JSR print_inline ; Print ' with station ' | |||
| B29E | EQUS " with station " | |||
| B2AC | INC work_ae ; Advance past station low | |||
| B2AE | LDA (work_ae,x) ; Read client network number | |||
| B2B0 | STA fs_work_6 ; Store network number | |||
| B2B2 | BIT bit_test_ff ; Set V flag | |||
| B2B5 | JSR print_station_addr ; Print client station address | |||
| B2B8 | .done_poll_status_line←3← B276 BVC← B286 BVC← B299 BEQ | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| B2BB | .done_poll_slot_mark←1← B24A BPL | |||
| PLA ; Retrieve slot offset | ||||
| B2BC | TAY ; Transfer to Y | |||
| B2BD | LDA #&3f ; Mark slot as processed (&3F) | |||
| B2BF | STA (nfs_workspace),y ; Write marker to workspace | |||
| B2C1 | BNE loop_pop_poll_slot ; ALWAYS branch | |||
| B2C3 | .return_from_poll_slots←1← B244 BEQ | |||
| RTS ; Return | ||||
Initialise PS slot buffer from template dataCopies the 12-byte ps_slot_txcb_template (&B193) into workspace at offsets &78-&83 via indexed addressing from write_ps_slot_link_addr (write_ps_slot_hi_link+1). Substitutes net_rx_ptr_hi at offsets &7D and &81 (the hi bytes of the two buffer pointers) so they point into the current RX buffer page. |
|
| B2C4 | .init_ps_slot_from_rx←1← B1A9 JSR |
| LDY #&78 ; Start at offset &78 | |
| B2C6 | .loop_copy_slot_tmpl←1← B2D8 BNE |
| LDA write_ps_slot_link_addr,y ; Load template byte | |
| B2C9 | CPY #&7d ; At offset &7D? |
| B2CB | BEQ subst_rx_page_byte ; Yes: substitute RX page |
| B2CD | CPY #&81 ; At offset &81? |
| B2CF | BNE store_slot_tmpl_byte ; No: use template byte |
| B2D1 | .subst_rx_page_byte←1← B2CB BEQ |
| LDA net_rx_ptr_hi ; Use RX buffer page instead | |
| B2D3 | .store_slot_tmpl_byte←1← B2CF BNE |
| STA (work_ae),y ; Store byte in slot buffer | |
| B2D5 | INY ; Next offset |
| B2D6 | CPY #&84 ; Past end of slot (&84)? |
| B2D8 | BNE loop_copy_slot_tmpl ; No: continue copying |
| B2DA | RTS ; Return |
Convert to uppercase and store in RX bufferIf the character in A is lowercase (&61-&7A), converts to uppercase by clearing bit 5. Stores the result in the RX buffer at the current position, advances the buffer pointer, and decrements the character count.
|
||||
| B2DB | .store_char_uppercase←2← B033 JSR← B20D JSR | |||
| LDY table_idx ; Y = current buffer position | ||||
| B2DD | AND #&7f ; Strip high bit | |||
| B2DF | CMP #&61 ; Is it lowercase 'a' or above? | |||
| B2E1 | BCC done_uppercase_store ; Below 'a': not lowercase | |||
| B2E3 | CMP #&7b ; Above 'z'? | |||
| B2E5 | BCS done_uppercase_store ; Yes: not lowercase | |||
| B2E7 | AND #&5f ; Convert to uppercase | |||
| B2E9 | .done_uppercase_store←2← B2E1 BCC← B2E5 BCS | |||
| STA (net_rx_ptr),y ; Store in RX buffer | ||||
| B2EB | INY ; Next buffer position | |||
| B2EC | STY table_idx ; Update buffer position | |||
| B2EE | DEX ; Decrement character count | |||
| B2EF | RTS ; Return (Z set if count=0) | |||
*Prot command handlerWith no arguments, sets all protection bits (&FF). Otherwise parses attribute keywords via match_fs_cmd with table offset &D3, accumulating bits via ORA. Stores the final protection mask in ws_0d68 and ws_0d69.
|
||||
| B2F0 | .cmd_prot | |||
| LDA (fs_crc_lo),y ; Get next char from command line | ||||
| B2F2 | EOR #&0d ; Compare with CR (end of line) | |||
| B2F4 | BNE parse_prot_keywords ; Not CR: attribute keywords follow | |||
| B2F6 | LDA #&ff ; A=&FF: protect all attributes | |||
| B2F8 | BNE store_prot_mask ; ALWAYS branch | |||
| B2FA | .parse_prot_keywords←1← B2F4 BNE | |||
| LDA ws_0d68 ; Load current protection mask | ||||
| B2FD | PHA ; Save as starting value | |||
| B2FE | .loop_match_prot_attr←1← B30E BNE | |||
| LDX #&d3 ; X=&D3: attribute keyword table offset | ||||
| B300 | LDA (fs_crc_lo),y ; Get next char from command line | |||
| B302 | STA ws_page ; Save for end-of-args check | |||
| B304 | JSR match_fs_cmd ; Match attribute keyword in table | |||
| B307 | BCS prot_check_arg_end ; No match: check if end of arguments | |||
| B309 | PLA ; Retrieve accumulated mask | |||
| B30A | ORA cmd_table_fs_lo,x ; OR in attribute bit for keyword | |||
| B30D | PHA ; Save updated mask | |||
| B30E | BNE loop_match_prot_attr ; Always non-zero after ORA: loop | |||
| B310 | .prot_check_arg_end←2← B307 BCS← B334 BCS | |||
| LDA ws_page ; Get the unmatched character | ||||
| B312 | EOR #&0d ; Is it CR? | |||
| B314 | BEQ done_prot_args ; Yes: arguments ended correctly | |||
| B316 | JMP error_bad_command ; No: invalid attribute keyword | |||
| B319 | .done_prot_args←1← B314 BEQ | |||
| PLA ; Retrieve final protection mask | ||||
| B31A | .store_prot_mask←3← A734 JMP← B2F8 BNE← B325 BEQ | |||
| STA ws_0d68 ; Store protection mask | ||||
| B31D | STA ws_0d69 ; Store protection mask copy | |||
| B320 | RTS ; Return | |||
*Unprot command handlerWith no arguments, clears all protection bits (EOR yields 0). Otherwise parses attribute keywords, clearing bits via AND with the complement. Shares the protection mask storage path with cmd_prot. Falls through to cmd_wipe.
|
||||
| B321 | .cmd_unprot | |||
| LDA (fs_crc_lo),y ; Get next char from command line | ||||
| B323 | EOR #&0d ; Compare with CR (end of line) | |||
| B325 | BEQ store_prot_mask ; No args: A=0 clears all protection | |||
| B327 | LDA ws_0d68 ; Load current protection mask | |||
| B32A | PHA ; Save as starting value | |||
| B32B | .loop_match_unprot_attr←1← B33B BCC | |||
| LDX #&d3 ; X=&D3: attribute keyword table offset | ||||
| B32D | LDA (fs_crc_lo),y ; Get next char from command line | |||
| B32F | STA ws_page ; Save for end-of-args check | |||
| B331 | JSR match_fs_cmd ; Match attribute keyword in table | |||
| B334 | BCS prot_check_arg_end ; No match: check if end of arguments | |||
| B336 | PLA ; Retrieve accumulated mask | |||
| B337 | AND cmd_table_fs_hi,x ; AND to clear matched attribute bit | |||
| B33A | PHA ; Save updated mask | |||
| B33B | BCC loop_match_unprot_attr ; ALWAYS branch | |||
*Wipe command handlerMasks owner access, parses a wildcard filename, and loops sending examine requests to the file server. Skips locked files and non-empty directories. Shows each filename with a '(Y/N/?) ' prompt — '?' shows full file info with a '(Y/N) ' reprompt, 'Y' builds the delete command in the TX buffer. Falls through to flush_and_read_char on completion.
|
||||
| B33D | .cmd_wipe | |||
| JSR mask_owner_access ; Mask owner access flags to 5 bits | ||||
| B340 | LDA #0 ; Initialise file index to 0 | |||
| B342 | STA fs_work_5 ; Store file counter | |||
| B344 | JSR save_ptr_to_os_text ; Save pointer to command text | |||
| B347 | JSR parse_filename_arg ; Parse wildcard filename argument | |||
| B34A | INX ; Advance past CR terminator | |||
| B34B | STX fs_work_6 ; Save end-of-argument buffer position | |||
| B34D | .request_next_wipe←1← B389 JMP | |||
| LDA #1 ; Command code 1 = examine directory | ||||
| B34F | STA fs_cmd_data ; Store command in TX buffer byte 0 | |||
| B352 | STA fs_data_count ; Store flag in TX buffer byte 2 | |||
| B355 | LDX fs_work_5 ; Load current file index | |||
| B357 | STX fs_func_code ; Store file index in TX buffer byte 1 | |||
| B35A | LDX #3 ; X=3: copy from TX buffer offset 3 | |||
| B35C | JSR copy_arg_to_buf ; Copy filename argument to TX buffer | |||
| B35F | LDY #3 ; Function code 3 = examine | |||
| B361 | LDA #&80 ; Flag &80 = escapable | |||
| B363 | STA escapable ; Mark operation as escapable | |||
| B365 | JSR save_net_tx_cb ; Send examine request to file server | |||
| B368 | LDA fs_cmd_data ; Get server response status | |||
| B36B | BNE check_wipe_attr ; Non-zero: file found, process it | |||
| B36D | LDA #osbyte_flush_buffer_class ; OSBYTE &0F: flush buffer class | |||
| B36F | LDX #1 ; X=1: flush input buffers | |||
| B371 | JSR osbyte ; Flush keyboard buffer Flush input buffers (X non-zero) | |||
| B374 | LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: keyboard scan from 16 | |||
| B376 | JSR osbyte ; Scan keyboard to clear state Keyboard scan starting from key 16 | |||
| B379 | LDY #0 ; Y=0: no key pressed Y=key | |||
| B37B | LDA #osbyte_write_keys_pressed ; OSBYTE &78: write keys pressed | |||
| B37D | JMP osbyte ; Clear keyboard state and return Write current keys pressed (X and Y) | |||
| B380 | .check_wipe_attr←1← B36B BNE | |||
| LDA fs_exam_attr_char ; Load first attribute char of response | ||||
| B383 | .loop_check_if_locked←1← B393 BNE | |||
| CMP #&4c ; Is file locked? | ||||
| B385 | BNE check_wipe_dir ; No: check if directory | |||
| B387 | .skip_wipe_locked←1← B40E JMP | |||
| INC fs_work_5 ; Skip locked file, advance index | ||||
| B389 | JMP request_next_wipe ; Request next file from server | |||
| B38C | .check_wipe_dir←1← B385 BNE | |||
| CMP #&44 ; Is it a directory entry? | ||||
| B38E | BNE show_wipe_prompt ; No: regular file, show prompt | |||
| B390 | LDA fs_exam_dir_flag ; Check directory contents flag | |||
| B393 | BNE loop_check_if_locked ; Non-empty dir: treat as locked, skip | |||
| B395 | .show_wipe_prompt←1← B38E BNE | |||
| LDX #1 ; X=1: start from response byte 1 | ||||
| B397 | LDY fs_work_6 ; Y = destination index in delete buffer | |||
| B399 | .loop_copy_wipe_name←1← B3A6 BNE | |||
| LDA fs_func_code,x ; Load filename char from response | ||||
| B39C | JSR osasci ; Print filename character to screen Write character | |||
| B39F | STA fs_filename_buf,y ; Store in delete command buffer too | |||
| B3A2 | INY ; Advance destination index | |||
| B3A3 | INX ; Advance source index | |||
| B3A4 | CPX #&0c ; Copied all 11 filename characters? | |||
| B3A6 | BNE loop_copy_wipe_name ; No: continue copying | |||
| B3A8 | JSR print_inline ; Print '(Y/N/?) ' prompt | |||
| B3AB | EQUS "(Y/N/?) " | |||
| B3B3 | NOP ; Inline string terminator (NOP) | |||
| B3B4 | JSR flush_and_read_char ; Read user response character | |||
| B3B7 | CMP #&3f ; User pressed '?'? | |||
| B3B9 | BNE check_wipe_response ; No: check for Y/N response | |||
| B3BB | LDA #&0d ; Carriage return before full info | |||
| B3BD | JSR oswrch ; Print CR Write character 13 | |||
| B3C0 | LDX #2 ; X=2: start from response byte 2 | |||
| B3C2 | .loop_print_wipe_info←1← B3CB BNE | |||
| LDA fs_cmd_data,x ; Load file info character | ||||
| B3C5 | JSR osasci ; Print file info character Write character | |||
| B3C8 | INX ; Advance to next character | |||
| B3C9 | CPX #&3e ; Printed all &3C info bytes? | |||
| B3CB | BNE loop_print_wipe_info ; No: continue printing | |||
| B3CD | JSR print_inline ; Print ' (Y/N) ' prompt (no '?') | |||
| B3D0 | EQUS " (Y/N) " | |||
| B3D7 | NOP ; Inline string terminator (NOP) | |||
| B3D8 | JSR flush_and_read_char ; Read user response (Y/N only) | |||
| B3DB | .check_wipe_response←1← B3B9 BNE | |||
| AND #&df ; Force uppercase | ||||
| B3DD | CMP #&59 ; User said 'Y' (yes)? | |||
| B3DF | BNE skip_wipe_to_next ; No: print newline, skip to next file | |||
| B3E1 | JSR osasci ; Echo 'Y' to screen Write character | |||
| B3E4 | LDX #0 ; X=0: start of stored filename | |||
| B3E6 | LDA fs_filename_buf,x ; Check first byte of stored name | |||
| B3E9 | CMP #&0d ; Is first byte CR (empty first field)? | |||
| B3EB | BEQ use_wipe_leaf_name ; Yes: use second filename field | |||
| B3ED | .loop_build_wipe_cmd←1← B402 BNE | |||
| LDA fs_filename_buf,x ; Load byte from stored filename | ||||
| B3F0 | CMP #&0d ; Is it CR (field separator)? | |||
| B3F2 | BNE skip_if_not_space ; No: check for space | |||
| B3F4 | LDA #&2e ; Replace CR with '.' directory sep | |||
| B3F6 | .skip_if_not_space←1← B3F2 BNE | |||
| CMP #&20 ; Is it a space (name terminator)? | ||||
| B3F8 | BNE store_wipe_tx_char ; No: keep character as-is | |||
| B3FA | .set_wipe_cr_end←1← B41D BEQ | |||
| LDA #&0d ; Replace space with CR (end of name) | ||||
| B3FC | .store_wipe_tx_char←1← B3F8 BNE | |||
| STA fs_cmd_data,x ; Store in delete command TX buffer | ||||
| B3FF | INX ; Advance to next character | |||
| B400 | CMP #&0d ; Was it the CR terminator? | |||
| B402 | BNE loop_build_wipe_cmd ; No: continue building delete command | |||
| B404 | LDY #&14 ; Function code &14 = delete file | |||
| B406 | JSR save_net_tx_cb ; Send delete request to file server | |||
| B409 | DEC fs_work_5 ; Adjust file index after deletion | |||
| B40B | .skip_wipe_to_next←1← B3DF BNE | |||
| JSR osnewl ; Print newline after user response Write newline (characters 10 and 13) | ||||
| B40E | JMP skip_wipe_locked ; Advance index, process next file | |||
| B411 | .use_wipe_leaf_name←1← B3EB BEQ | |||
| DEX ; DEX to offset following INX | ||||
| B412 | .loop_copy_wipe_leaf←1← B41B BNE | |||
| INX ; Advance to next byte | ||||
| B413 | LDA fs_filename_buf_1,x ; Load byte from second field | |||
| B416 | STA fs_cmd_data,x ; Store in delete command TX buffer | |||
| B419 | CMP #&20 ; Is it a space (field terminator)? | |||
| B41B | BNE loop_copy_wipe_leaf ; No: continue copying second field | |||
| B41D | BEQ set_wipe_cr_end ; Space found: terminate with CR ALWAYS branch | |||
Flush keyboard buffer and read one characterCalls OSBYTE &0F to flush the input buffer, then OSRDCH to read a single character. Raises an escape error if escape was pressed (carry set on return). |
|
| B41F | .flush_and_read_char←2← B3B4 JSR← B3D8 JSR |
| LDA #osbyte_flush_buffer_class ; OSBYTE &0F: flush buffer class | |
| B421 | LDX #1 ; X=1: flush input buffers |
| B423 | JSR osbyte ; Flush keyboard buffer before read Flush input buffers (X non-zero) |
| B426 | JSR osrdch ; Read character from input stream Read a character from the current input stream |
| B429 | BCC return_from_flush_read ; C clear: character read OK |
| B42B | JMP raise_escape_error ; Escape pressed: raise error |
| B42E | .return_from_flush_read←1← B429 BCC |
| RTS ; Return with character in A | |
Dead code: clear 120 bytes of workspaceUnreferenced subroutine. Zeroes offsets &00-&77 (120 bytes) of the workspace page pointed to by l00cc. Superseded by loop_zero_workspace (&8ED5) which clears a full 256-byte page via both l00cc and nfs_workspace pointers. |
|
| B42F | .unused_clear_ws_78 |
| LDA #0 ; A=0: clear value | |
| B431 | LDY #&78 ; Y=&78: clear offsets &00-&77 |
| B433 | .loop_clear_ws_78←1← B436 BNE |
| DEY ; Decrement index | |
| B434 | STA (fs_ws_ptr),y ; Clear workspace byte via l00cc |
| B436 | BNE loop_clear_ws_78 ; Loop until Y=0 |
| B438 | RTS ; Return |
Initialise channel allocation tableClears all 256 bytes of the table, then marks available channel slots based on the count from the receive buffer. Sets the first slot to &C0 (active channel marker). |
|
| B439 | .init_channel_table←1← 8B6A JSR |
| LDA #0 ; A=0: clear value | |
| B43B | TAY ; Y=0: start index Y=&00 |
| B43C | .loop_clear_chan_table←1← B440 BNE |
| STA fcb_count_lo,y ; Clear channel table entry | |
| B43F | INY ; Next entry |
| B440 | BNE loop_clear_chan_table ; Loop until all 256 bytes cleared |
| B442 | LDY #&0f ; Offset &0F in receive buffer |
| B444 | LDA (net_rx_ptr),y ; Get number of available channels |
| B446 | SEC ; Prepare subtraction |
| B447 | SBC #&5a ; Subtract 'Z' to get negative count |
| B449 | TAY ; Y = negative channel count (index) |
| B44A | LDA #&40 ; Channel marker &40 (available) |
| B44C | .loop_mark_chan_avail←1← B452 BPL |
| STA fcb_count_lo,y ; Mark channel slot as available | |
| B44F | DEY ; Previous channel slot |
| B450 | CPY #&b8 ; Reached start of channel range? |
| B452 | BPL loop_mark_chan_avail ; No: continue marking channels |
| B454 | INY ; Point to first channel slot |
| B455 | LDA #&c0 ; Active channel marker &C0 |
| B457 | STA fcb_count_lo,y ; Mark first channel as active |
| B45A | RTS ; Return |
Convert channel attribute to table indexSubtracts &20 from the attribute byte and clamps to the range 0-&0F. Returns &FF if out of range. Preserves processor flags via PHP/PLP.
|
|||||||
| B45B | .attr_to_chan_index←5← 92A9 JSR← 92C0 JSR← 9C60 JSR← 9C9E JSR← B745 JSR | ||||||
| PHP ; Save flags | |||||||
| B45C | SEC ; Prepare subtraction | ||||||
| B45D | SBC #&20 ; Subtract &20 to get table index | ||||||
| B45F | BMI error_chan_out_of_range ; Negative: out of valid range | ||||||
| B461 | CMP #&10 ; Above maximum channel index &0F? | ||||||
| B463 | BCC return_chan_index ; In range: valid index | ||||||
| B465 | .error_chan_out_of_range←1← B45F BMI | ||||||
| LDA #&ff ; Out of range: return &FF (invalid) | |||||||
| B467 | .return_chan_index←1← B463 BCC | ||||||
| PLP ; Restore flags | |||||||
| B468 | TAX ; X = channel index (or &FF) | ||||||
| B469 | RTS ; Return | ||||||
Validate channel character and look up entryCharacters below '0' are looked up directly in the channel table. Characters '0' and above are converted to a table index via attr_to_chan_index. Raises 'Net channel' error if invalid.
|
||||
| B46A | .check_chan_char←2← 9D97 JSR← B4E3 JSR | |||
| CMP #&20 ; Below space? | ||||
| B46C | BCC err_net_chan_invalid ; Yes: invalid channel character | |||
| B46E | CMP #&30 ; Below '0'? | |||
| B470 | BCC lookup_chan_by_char ; In range &20-&2F: look up channel | |||
| B472 | .err_net_chan_invalid←2← 9BC6 JMP← B46C BCC | |||
| PHA ; Save channel character | ||||
| B473 | .error_chan_not_found←1← B4A5 BEQ | |||
| LDA #&de ; Error code &DE | ||||
| B475 | .err_net_chan_not_found | |||
| JSR error_inline_log ; Generate 'Net channel' error | ||||
| B478 | EQUS "Net channel." | |||
| B484 | JSR false_ref_6f6e ; Error string continuation (unreachable) | |||
| B487 | EQUS "t on this file server" | |||
| B49C | EQUB &00 | |||
| fall through ↓ | ||||
Look up channel by character codeConverts the character to a table index via attr_to_chan_index, checks the station/network match via match_station_net, and returns the channel flags in A.
|
|||||||
| B49D | .lookup_chan_by_char←2← 9EC4 JSR← B470 BCC | ||||||
| PHA ; Save channel character | |||||||
| B49E | SEC ; Prepare subtraction | ||||||
| B49F | SBC #&20 ; Convert char to table index | ||||||
| B4A1 | TAX ; X = channel table index | ||||||
| B4A2 | LDA fcb_net_or_port,x ; Look up network number for channel | ||||||
| B4A5 | BEQ error_chan_not_found ; Zero: channel not found, raise error | ||||||
| B4A7 | JSR match_station_net ; Check station/network matches current | ||||||
| B4AA | BNE error_chan_not_here ; No match: build detailed error msg | ||||||
| B4AC | PLA ; Discard saved channel character | ||||||
| B4AD | LDA chan_status,x ; Load channel status flags | ||||||
| B4B0 | RTS ; Return; A = channel flags | ||||||
| B4B1 | .error_chan_not_here←1← B4AA BNE | ||||||
| LDA #&de ; Error code &DE | |||||||
| B4B3 | STA error_text ; Store error code in error block | ||||||
| B4B6 | LDA #0 ; BRK opcode | ||||||
| B4B8 | STA error_block ; Store BRK at start of error block | ||||||
| B4BB | TAX ; X=0: copy index X=&00 | ||||||
| B4BC | .loop_copy_chan_err_str←1← B4C3 BNE | ||||||
| INX ; Advance copy position | |||||||
| B4BD | LDA net_channel_err_string,x ; Load 'Net channel' string byte | ||||||
| B4C0 | STA error_text,x ; Copy to error text | ||||||
| B4C3 | BNE loop_copy_chan_err_str ; Continue until NUL terminator | ||||||
| B4C5 | STX fs_load_addr_2 ; Save end-of-string position | ||||||
| B4C7 | STX fs_work_4 ; Save for suffix append | ||||||
| B4C9 | PLA ; Retrieve channel character | ||||||
| B4CA | JSR append_space_and_num ; Append ' N' (channel number) | ||||||
| B4CD | LDY fs_work_4 ; Load 'Net channel' end position | ||||||
| B4CF | .loop_append_err_suffix←1← B4D7 BNE | ||||||
| INY ; Skip past NUL to suffix string | |||||||
| B4D0 | INX ; Advance destination position | ||||||
| B4D1 | LDA net_channel_err_string,y ; Load ' not on this...' suffix byte | ||||||
| B4D4 | STA error_text,x ; Append to error message | ||||||
| B4D7 | BNE loop_append_err_suffix ; Continue until NUL | ||||||
| B4D9 | JMP error_block ; Raise the constructed error | ||||||
Store channel attribute and check not directoryWrites the current channel attribute to the receive buffer, then tests the directory flag (bit 1). Raises 'Is a dir.' error if the attribute refers to a directory rather than a file. |
|
| B4DC | .store_result_check_dir←2← B7D4 JSR← B85C JSR |
| LDA cur_chan_attr ; Load current channel attribute | |
| B4DF | LDY #&0e ; Offset &0E in receive buffer |
| B4E1 | STA (net_rx_ptr),y ; Store attribute in receive buffer |
| fall through ↓ | |
Validate channel is not a directoryCalls check_chan_char to validate the channel, then tests the directory flag (bit 1). Raises 'Is a dir.' error if the channel refers to a directory. |
|
| B4E3 | .check_not_dir←2← 9BF9 JSR← 9E47 JSR |
| JSR check_chan_char ; Validate and look up channel | |
| B4E6 | AND #2 ; Test directory flag (bit 1) |
| B4E8 | BEQ return_from_dir_check ; Not a directory: return OK |
| B4EA | LDA #&a8 ; Error code &A8 |
| B4EC | JSR error_inline_log ; Generate 'Is a dir.' error |
| B4EF | EQUS "Is a dir.." |
| B4F9 | .return_from_dir_check←1← B4E8 BEQ |
| RTS ; Return | |
Allocate a free file control block slotScans FCB slots &20-&2F for an empty entry. Returns Z=0 with X=slot index on success, or Z=1 with A=0 if all slots are occupied.
|
||||||
| B4FA | .alloc_fcb_slot←7← 9D3D JSR← 9D72 JSR← A268 JSR← A307 JSR← A332 JSR← A369 JSR← B531 JSR | |||||
| PHA ; Save channel attribute | ||||||
| B4FB | LDX #&20 ; Start scanning from FCB slot &20 | |||||
| B4FD | .loop_scan_fcb_slots←1← B505 BNE | |||||
| LDA fcb_attr_or_count_mid,x ; Load FCB station byte | ||||||
| B500 | BEQ done_found_free_slot ; Zero: slot is free, use it | |||||
| B502 | INX ; Try next slot | |||||
| B503 | CPX #&30 ; Past last FCB slot &2F? | |||||
| B505 | BNE loop_scan_fcb_slots ; No: check next slot | |||||
| B507 | PLA ; No free slot: discard saved attribute | |||||
| B508 | LDA #0 ; A=0: return failure (Z set) | |||||
| B50A | RTS ; Return | |||||
| B50B | .done_found_free_slot←1← B500 BEQ | |||||
| PLA ; Restore channel attribute | ||||||
| B50C | STA fcb_attr_or_count_mid,x ; Store attribute in FCB slot | |||||
| B50F | LDA #0 ; A=0: clear value | |||||
| B511 | STA fcb_xfer_count_lo,x ; Clear FCB transfer count low | |||||
| B514 | STA fcb_xfer_count_mid,x ; Clear FCB transfer count mid | |||||
| B517 | STA fcb_count_lo,x ; Clear FCB transfer count high | |||||
| B51A | LDA fs_server_stn ; Load current station number | |||||
| B51D | STA fcb_station_or_count_hi,x ; Store station in FCB | |||||
| B520 | LDA fs_server_net ; Load current network number | |||||
| B523 | STA fcb_net_or_port,x ; Store network in FCB | |||||
| B526 | TXA ; Get FCB slot index | |||||
| B527 | PHA ; Save slot index | |||||
| B528 | SEC ; Prepare subtraction | |||||
| B529 | SBC #&20 ; Convert slot to channel index (0-&0F) | |||||
| B52B | TAX ; X = channel index | |||||
| B52C | PLA ; Restore A = FCB slot index | |||||
| B52D | RTS ; Return; A=slot, X=channel, Z clear | |||||
Allocate FCB slot or raise errorCalls alloc_fcb_slot and raises 'No more FCBs' if no free slot is available. Preserves the caller's argument on the stack. |
|
| B52E | .alloc_fcb_or_error←2← 9CEB JSR← A1D5 JSR |
| PHA ; Save argument | |
| B52F | LDA #0 ; A=0: allocate any available slot |
| B531 | JSR alloc_fcb_slot ; Try to allocate an FCB slot |
| B534 | BNE return_alloc_success ; Success: slot allocated |
| B536 | LDA #&c0 ; Error code &C0 |
| B538 | JSR error_inline_log ; Generate 'No more FCBs' error |
| B53B | EQUS "No more FCBs." |
| B548 | .return_alloc_success←1← B534 BNE |
| PLA ; Restore argument | |
| B549 | RTS ; Return |
Close all network channels for current stationScans FCB slots &0F down to 0, closing those matching the current station. C=0 closes all matching entries; C=1 closes with write-flush.
|
||||
| B54A | .close_all_net_chans←3← 9494 JSR← 951B JSR← A379 JSR | |||
| CLC ; C=0: close all matching channels | ||||
| B54B | BCC skip_set_carry ; Branch always to scan entry ALWAYS branch | |||
| B54D | SEC ; C=1: close with write-flush | |||
| B54E | .skip_set_carry←1← B54B BCC | |||
| BIT bit_test_ff ; Set V flag via BIT (alternate mode) | ||||
| fall through ↓ | ||||
Scan FCB slot flags from &10 downwardIterates through FCB slots starting at &10, checking each slot's flags byte. Returns when all slots have been processed. |
|
| B551 | .scan_fcb_flags←1← 9DAC JSR |
| LDX #&10 ; Start from FCB slot &10 | |
| B553 | .loop_scan_fcb_down←4← B55F BVC← B569 BVS← B56E BNE← B578 BEQ |
| DEX ; Previous FCB slot | |
| B554 | BPL skip_if_slots_done ; More slots to check |
| B556 | RTS ; All FCB slots processed, return |
| B557 | .skip_if_slots_done←1← B554 BPL |
| LDA chan_status,x ; Load channel flags for this slot | |
| B55A | TAY ; Save flags in Y |
| B55B | AND #2 ; Test active flag (bit 1) |
| B55D | BEQ done_check_station ; Not active: check station match |
| B55F | BVC loop_scan_fcb_down ; V clear (close all): next slot |
| B561 | BCC done_check_station ; C clear: check station match |
| B563 | TYA ; Restore original flags |
| B564 | AND #&df ; Clear write-pending flag (bit 5) |
| B566 | STA chan_status,x ; Update channel flags |
| B569 | BVS loop_scan_fcb_down ; Next slot (V always set here) ALWAYS branch |
| B56B | .done_check_station←2← B55D BEQ← B561 BCC |
| JSR match_station_net ; Check if channel belongs to station | |
| B56E | BNE loop_scan_fcb_down ; No match: skip to next slot |
| B570 | LDA #0 ; A=0: clear channel |
| B572 | STA chan_status,x ; Clear channel flags (close it) |
| B575 | STA fcb_net_or_port,x ; Clear network number |
| B578 | BEQ loop_scan_fcb_down ; Continue to next slot ALWAYS branch |
Check FCB slot matches current station/networkCompares the station and network numbers in the FCB at slot X against the current values using EOR. Returns Z=1 if both match, Z=0 if either differs.
|
|||||||
| B57A | .match_station_net←7← A2EE JSR← A319 JSR← A350 JSR← A69E JSR← AC2E JSR← B4A7 JSR← B56B JSR | ||||||
| LDA fcb_flags,x ; Load FCB station number | |||||||
| B57D | EOR fs_server_stn ; Compare with current station (EOR) | ||||||
| B580 | BNE return_from_match_stn ; Different: Z=0, no match | ||||||
| B582 | LDA fcb_net_num,x ; Load FCB network number | ||||||
| B585 | EOR fs_server_net ; Compare with current network (EOR) | ||||||
| B588 | .return_from_match_stn←1← B580 BNE | ||||||
| RTS ; Return; Z=1 if match, Z=0 if not | |||||||
Find next open FCB slot for current connectionScans from the current index, wrapping around at the end. On the first pass finds active entries matching the station; on the second pass finds empty slots for new allocations. |
|
| B589 | .find_open_fcb←2← B68A JSR← B788 JSR |
| LDX cur_fcb_index ; Load current FCB index | |
| B58C | BIT bit_test_ff ; Set V flag (first pass marker) |
| B58F | .loop_find_fcb←4← B59E BVC← B5A4 BPL← B5C1 BVS← B5C8 BNE |
| INX ; Next FCB slot | |
| B590 | CPX #&10 ; Past end of table (&10)? |
| B592 | BNE skip_if_no_wrap ; No: continue checking |
| B594 | LDX #0 ; Wrap around to slot 0 |
| B596 | .skip_if_no_wrap←1← B592 BNE |
| CPX cur_fcb_index ; Back to starting slot? | |
| B599 | BNE done_check_fcb_status ; No: check this slot |
| B59B | BVC loop_scan_empty_fcb ; V clear (second pass): scan empties |
| B59D | CLV ; Clear V for second pass |
| B59E | BVC loop_find_fcb ; Continue scanning ALWAYS branch |
| B5A0 | .done_check_fcb_status←1← B599 BNE |
| LDA fcb_status,x ; Load FCB status flags | |
| B5A3 | ROL ; Shift bit 7 (in-use) into carry |
| B5A4 | BPL loop_find_fcb ; Not in use: skip |
| B5A6 | AND #4 ; Test bit 2 (modified flag) |
| B5A8 | BNE skip_if_modified_fcb ; Modified: check further conditions |
| B5AA | .done_select_fcb←1← B5CA BEQ |
| DEX ; Adjust for following INX | |
| B5AB | .loop_scan_empty_fcb←2← B59B BVC← B5B6 BPL |
| INX ; Next FCB slot | |
| B5AC | CPX #&10 ; Past end of table? |
| B5AE | BNE done_test_empty_slot ; No: continue |
| B5B0 | LDX #0 ; Wrap around to slot 0 |
| B5B2 | .done_test_empty_slot←1← B5AE BNE |
| LDA fcb_status,x ; Load FCB status flags | |
| B5B5 | ROL ; Shift bit 7 into carry |
| B5B6 | BPL loop_scan_empty_fcb ; Not in use: continue scanning |
| B5B8 | SEC ; Set carry for ROR restore |
| B5B9 | ROR ; Restore original flags |
| B5BA | STA fcb_status,x ; Save flags back (mark as found) |
| B5BD | LDX cur_fcb_index ; Restore original FCB index |
| B5C0 | RTS ; Return with found slot in X |
| B5C1 | .skip_if_modified_fcb←1← B5A8 BNE |
| BVS loop_find_fcb ; V set (first pass): skip modified | |
| B5C3 | LDA fcb_status,x ; Load FCB status flags |
| B5C6 | AND #&20 ; Test bit 5 (offset pending) |
| B5C8 | BNE loop_find_fcb ; Bit 5 set: skip this slot |
| B5CA | BEQ done_select_fcb ; Use this slot ALWAYS branch |
Initialise byte counters for wipe/transferClears the pass counter, byte counter, offset counter, and transfer flag. Stores &FF sentinels in l10cd/l10ce. Returns with X/Y pointing at workspace offset &10CA.
|
||||||
| B5CC | .init_wipe_counters←2← B60F JSR← B6CC JSR | |||||
| LDY #1 ; Initial pass count = 1 | ||||||
| B5CE | STY xfer_pass_count ; Store pass counter | |||||
| B5D1 | DEY ; Y=0 Y=&00 | |||||
| B5D2 | STY xfer_count_lo ; Clear byte counter low | |||||
| B5D5 | STY xfer_offset ; Clear offset counter | |||||
| B5D8 | STY xfer_flag ; Clear transfer flag | |||||
| B5DB | TYA ; A=0 A=&00 | |||||
| B5DC | LDX #2 ; Clear 3 counter bytes | |||||
| B5DE | .loop_clear_counters←1← B5E2 BPL | |||||
| STA xfer_counter,x ; Clear counter byte | ||||||
| B5E1 | DEX ; Next byte | |||||
| B5E2 | BPL loop_clear_counters ; Loop for indices 2, 1, 0 | |||||
| B5E4 | STX xfer_sentinel_1 ; Store &FF as sentinel in l10cd | |||||
| B5E7 | STX xfer_sentinel_2 ; Store &FF as sentinel in l10ce | |||||
| B5EA | LDX #&ca ; X=&CA: workspace offset | |||||
| B5EC | LDY #&10 ; Y=&10: page &10 | |||||
| B5EE | RTS ; Return; X/Y point to &10CA | |||||
Start wipe pass for current FCBVerifies the workspace checksum, saves the station context (pushing station low/high), initialises transfer counters via init_wipe_counters, and sends the initial request via send_and_receive. Clears the active and offset flags on completion.
|
||||
| B5EF | .start_wipe_pass←2← B681 JSR← B7BA JSR | |||
| JSR verify_ws_checksum ; Verify workspace checksum integrity | ||||
| B5F2 | STX cur_fcb_index ; Save current FCB index | |||
| B5F5 | LDA fcb_status,x ; Load FCB status flags | |||
| B5F8 | ROR ; Shift bit 0 (active) into carry | |||
| B5F9 | BCC done_clear_fcb_active ; Not active: clear status and return | |||
| B5FB | LDA work_stn_lo ; Save current station low to stack | |||
| B5FE | PHA ; Push station low | |||
| B5FF | LDA work_stn_hi ; Save current station high | |||
| B602 | PHA ; Push station high | |||
| B603 | LDA fcb_stn_lo,x ; Load FCB station low | |||
| B606 | STA work_stn_lo ; Set as working station low | |||
| B609 | LDA fcb_stn_hi,x ; Load FCB station high | |||
| B60C | STA work_stn_hi ; Set as working station high | |||
| B60F | JSR init_wipe_counters ; Reset transfer counters | |||
| B612 | DEC xfer_offset ; Set offset to &FF (no data yet) | |||
| B615 | DEC xfer_pass_count ; Set pass counter to 0 (flush mode) | |||
| B618 | LDX cur_fcb_index ; Reload FCB index | |||
| B61B | TXA ; Transfer to A | |||
| B61C | CLC ; Prepare addition | |||
| B61D | ADC #&11 ; Add &11 for buffer page offset | |||
| B61F | STA fcb_buf_page ; Store buffer address high byte | |||
| B622 | LDA fcb_status,x ; Load FCB status flags | |||
| B625 | AND #&20 ; Test bit 5 (has saved offset) | |||
| B627 | BEQ done_restore_offset ; No offset: skip restore | |||
| B629 | LDA fcb_buf_offset,x ; Load saved byte offset | |||
| B62C | STA xfer_offset ; Restore offset counter | |||
| B62F | .done_restore_offset←1← B627 BEQ | |||
| LDA fcb_attr_ref,x ; Load FCB attribute reference | ||||
| B632 | STA cur_attr_ref ; Store as current reference | |||
| B635 | TAX ; Transfer to X | |||
| B636 | LDY #&0e ; Offset &0E in receive buffer | |||
| B638 | LDA (net_rx_ptr),y ; Save current receive attribute | |||
| B63A | PHA ; Push to stack | |||
| B63B | TXA ; Restore attribute to A | |||
| B63C | STA (net_rx_ptr),y ; Set attribute in receive buffer | |||
| B63E | LDX #&ca ; X=&CA: workspace offset | |||
| B640 | LDY #&10 ; Y=&10: page &10 | |||
| B642 | LDA #0 ; A=0: standard transfer mode | |||
| B644 | JSR send_and_receive ; Send data and receive response | |||
| B647 | LDX cur_fcb_index ; Reload FCB index | |||
| B64A | PLA ; Restore saved receive attribute | |||
| B64B | LDY #&0e ; Offset &0E | |||
| B64D | STA (net_rx_ptr),y ; Restore receive attribute | |||
| B64F | PLA ; Restore station high | |||
| B650 | STA work_stn_hi ; Store station high | |||
| B653 | PLA ; Restore station low | |||
| B654 | STA work_stn_lo ; Store station low | |||
| B657 | .done_clear_fcb_active←1← B5F9 BCC | |||
| LDA #&dc ; Mask &DC: clear bits 0, 1, 5 | ||||
| B659 | AND fcb_status,x ; Clear active and offset flags | |||
| B65C | STA fcb_status,x ; Update FCB status | |||
| B65F | RTS ; Return | |||
Save FCB context and process pending slotsCopies 13 bytes from the TX buffer (&0F00) and fs_load_addr workspace to temporary storage at &10D9. If Y=0, skips to the restore loop. Otherwise scans for pending FCB slots (bits 7+6 set), flushes each via start_wipe_pass, allocates new slots via find_open_fcb, and sends directory requests. Falls through to restore_catalog_entry.
|
||||
| B660 | .save_fcb_context←2← B735 JSR← B8A2 JSR | |||
| LDX #&0c ; Copy 13 bytes (indices 0 to &0C) | ||||
| B662 | .loop_save_tx_context←1← B66C BPL | |||
| LDA txcb_reply_port,x ; Load TX buffer byte | ||||
| B665 | STA fcb_ctx_save,x ; Save to context buffer at &10D9 | |||
| B668 | LDA fs_load_addr,x ; Load workspace byte from fs_load_addr | |||
| B66A | PHA ; Save to stack | |||
| B66B | DEX ; Next byte down | |||
| B66C | BPL loop_save_tx_context ; Loop for all 13 bytes | |||
| B66E | CPY #0 ; Y=0? (no FCB to process) | |||
| B670 | BNE done_save_context ; Non-zero: scan and process FCBs | |||
| B672 | JMP loop_restore_workspace ; Y=0: skip to restore workspace | |||
| B675 | .done_save_context←1← B670 BNE | |||
| PHP ; Save flags | ||||
| B676 | LDX #&ff ; X=&FF: start scanning from -1 | |||
| B678 | .loop_find_pending_fcb←2← B67C BPL← B67F BPL | |||
| INX ; Next FCB slot | ||||
| B679 | LDA fcb_status,x ; Load FCB status flags | |||
| B67C | BPL loop_find_pending_fcb ; Bit 7 clear: not pending, skip | |||
| B67E | ASL ; Shift bit 6 to bit 7 | |||
| B67F | BPL loop_find_pending_fcb ; Bit 6 clear: skip | |||
| B681 | JSR start_wipe_pass ; Flush this FCB's pending data | |||
| B684 | LDA #&40 ; Pending marker &40 | |||
| B686 | STA fcb_status,x ; Mark FCB as pending-only | |||
| B689 | PHP ; Save flags | |||
| B68A | JSR find_open_fcb ; Find next available FCB slot | |||
| B68D | PLP ; Restore flags | |||
| B68E | LDA cur_chan_attr ; Load current channel attribute | |||
| B691 | STA cur_attr_ref ; Store as current reference | |||
| B694 | PHA ; Save attribute | |||
| B695 | TAY ; Y = attribute index | |||
| B696 | LDA fcb_attr_or_count_mid,y ; Load station for this attribute | |||
| B699 | STA fs_cmd_data ; Store station in TX buffer | |||
| B69C | PLA ; Restore attribute | |||
| B69D | STA fcb_attr_ref,x ; Store attribute in FCB slot | |||
| B6A0 | LDA work_stn_lo ; Load working station low | |||
| B6A3 | STA fs_reply_cmd ; Store in TX buffer | |||
| B6A6 | STA fcb_stn_lo,x ; Store station low in FCB | |||
| B6A9 | LDA work_stn_hi ; Load working station high | |||
| B6AC | STA fs_load_vector ; Store in TX buffer | |||
| B6AF | STA fcb_stn_hi,x ; Store station high in FCB | |||
| B6B2 | TXA ; Get FCB slot index | |||
| B6B3 | CLC ; Prepare addition | |||
| B6B4 | ADC #&11 ; Add &11 for buffer page offset | |||
| B6B6 | STA fcb_buf_page ; Store buffer address high byte | |||
| B6B9 | PLP ; Restore flags | |||
| B6BA | BVC done_init_wipe ; V clear: skip directory request | |||
| B6BC | LDX #0 ; Command byte = 0 | |||
| B6BE | STX fs_func_code ; Store in TX buffer | |||
| B6C1 | INX ; X=1: flag byte X=&01 | |||
| B6C2 | STX fs_data_count ; Store in TX buffer | |||
| B6C5 | LDY #&0d ; Function code &0D | |||
| B6C7 | LDX #5 ; X=5: copy 5 bytes to TX | |||
| B6C9 | JSR save_net_tx_cb ; Send directory request to server | |||
| B6CC | .done_init_wipe←1← B6BA BVC | |||
| JSR init_wipe_counters ; Reset transfer counters | ||||
| B6CF | LDY #&0e ; Offset &0E | |||
| B6D1 | LDA (net_rx_ptr),y ; Save current receive attribute | |||
| B6D3 | PHA ; Push to stack | |||
| B6D4 | LDA cur_attr_ref ; Load current reference | |||
| B6D7 | STA (net_rx_ptr),y ; Set in receive buffer | |||
| B6D9 | LDY #&10 ; Y=&10: page &10 | |||
| B6DB | LDA #2 ; A=2: transfer mode 2 | |||
| B6DD | JSR send_and_receive ; Send and receive data | |||
| B6E0 | PLA ; Restore receive attribute | |||
| B6E1 | LDY #&0e ; Offset &0E | |||
| B6E3 | STA (net_rx_ptr),y ; Restore receive attribute | |||
| B6E5 | LDX cur_fcb_index ; Reload FCB index | |||
| B6E8 | LDA xfer_pass_count ; Load pass counter | |||
| B6EB | BNE done_calc_offset ; Non-zero: data received, calc offset | |||
| B6ED | LDA xfer_offset ; Load offset counter | |||
| B6F0 | BEQ done_set_fcb_active ; Zero: no data received at all | |||
| B6F2 | .done_calc_offset←1← B6EB BNE | |||
| LDA xfer_offset ; Load offset counter | ||||
| B6F5 | EOR #&ff ; Negate (ones complement) | |||
| B6F7 | CLC ; Clear carry for add | |||
| B6F8 | ADC #1 ; Complete twos complement negation | |||
| B6FA | STA fcb_buf_offset,x ; Store negated offset in FCB | |||
| B6FD | LDA #&20 ; Set bit 5 (has saved offset) | |||
| B6FF | ORA fcb_status,x ; Add to FCB flags | |||
| B702 | STA fcb_status,x ; Update FCB status | |||
| B705 | LDA fcb_buf_page ; Load buffer address high byte | |||
| B708 | STA fs_load_addr_3 ; Set pointer high byte | |||
| B70A | LDA #0 ; A=0: pointer low byte and clear val | |||
| B70C | STA fs_load_addr_2 ; Set pointer low byte | |||
| B70E | LDY fcb_buf_offset,x ; Load negated offset (start of clear) | |||
| B711 | .loop_clear_buffer←1← B714 BNE | |||
| STA (fs_load_addr_2),y ; Clear buffer byte | ||||
| B713 | INY ; Next byte | |||
| B714 | BNE loop_clear_buffer ; Loop until page boundary | |||
| B716 | .done_set_fcb_active←1← B6F0 BEQ | |||
| LDA #2 ; Set bit 1 (active flag) | ||||
| B718 | ORA fcb_status,x ; Add active flag to status | |||
| B71B | STA fcb_status,x ; Update FCB status | |||
| B71E | LDY #0 ; Y=0: start restoring workspace | |||
| B720 | .loop_restore_workspace←2← B672 JMP← B727 BNE | |||
| PLA ; Restore workspace byte from stack | ||||
| B721 | STA fs_load_addr,y ; Store to fs_load_addr workspace | |||
| B724 | INY ; Next byte | |||
| B725 | CPY #&0d ; Restored all 13 bytes? | |||
| B727 | BNE loop_restore_workspace ; No: continue restoring | |||
| fall through ↓ | ||||
| B729 | .restore_catalog_entry←1← B8D3 JSR |
| LDY #&0c ; Copy 13 bytes (indices 0 to &0C) | |
| B72B | .loop_restore_tx_buf←1← B732 BPL |
| LDA fcb_ctx_save,y ; Load saved catalog byte from &10D9 | |
| B72E | STA txcb_reply_port,y ; Restore to TX buffer |
| B731 | DEY ; Next byte down |
| B732 | BPL loop_restore_tx_buf ; Loop for all bytes |
| B734 | RTS ; Return |
| B735 | .loop_save_before_match←1← B754 JMP |
| JSR save_fcb_context ; Save current context first | |
| fall through ↓ | |
Find FCB slot matching channel attributeScans FCB slots 0-&0F for an active entry whose attribute reference matches l10c9. Converts the attribute to a channel index, then verifies the station and network numbers. On the first scan past slot &0F, saves context via save_fcb_context and restarts. Returns Z=0 if the FCB has saved offset data (bit 5 set).
|
||||||
| B738 | .find_matching_fcb←3← 9DEC JSR← B7EF JSR← B88E JSR | |||||
| LDX #&ff ; X=&FF: start scanning from -1 | ||||||
| B73A | .loop_reload_attr←2← B774 BNE← B77C BNE | |||||
| LDY cur_chan_attr ; Load channel attribute to match | ||||||
| B73D | .loop_next_fcb_slot←2← B75C BEQ← B762 BNE | |||||
| INX ; Next FCB slot | ||||||
| B73E | CPX #&10 ; Past end of table (&10)? | |||||
| B740 | BNE done_test_fcb_active ; No: check this slot | |||||
| B742 | LDA cur_chan_attr ; Load channel attribute | |||||
| B745 | JSR attr_to_chan_index ; Convert to channel index | |||||
| B748 | LDA fcb_station_or_count_hi,x ; Load station for this channel | |||||
| B74B | STA work_stn_hi ; Store as match target station high | |||||
| B74E | LDA fcb_attr_or_count_mid,x ; Load port for this channel | |||||
| B751 | STA work_stn_lo ; Store as match target station low | |||||
| B754 | JMP loop_save_before_match ; Save context and rescan from start | |||||
| B757 | .done_test_fcb_active←1← B740 BNE | |||||
| LDA fcb_status,x ; Load FCB status flags | ||||||
| B75A | AND #2 ; Test active flag (bit 1) | |||||
| B75C | BEQ loop_next_fcb_slot ; Not active: skip to next | |||||
| B75E | TYA ; Get attribute to match | |||||
| B75F | CMP fcb_attr_ref,x ; Compare with FCB attribute ref | |||||
| B762 | BNE loop_next_fcb_slot ; No attribute match: skip | |||||
| B764 | STX cur_fcb_index ; Save matching FCB index | |||||
| B767 | SEC ; Prepare subtraction | |||||
| B768 | SBC #&20 ; Convert attribute to channel index | |||||
| B76A | TAY ; Y = channel index | |||||
| B76B | LDX cur_fcb_index ; Reload FCB index | |||||
| B76E | LDA fcb_attr_or_count_mid,y ; Load channel station byte | |||||
| B771 | CMP fcb_stn_lo,x ; Compare with FCB station | |||||
| B774 | BNE loop_reload_attr ; Station mismatch: try next | |||||
| B776 | LDA fcb_station_or_count_hi,y ; Load channel network byte | |||||
| B779 | CMP fcb_stn_hi,x ; Compare with FCB network | |||||
| B77C | BNE loop_reload_attr ; Network mismatch: try next | |||||
| B77E | LDA fcb_status,x ; Load FCB flags | |||||
| B781 | BPL return_test_offset ; Bit 7 clear: no pending flush | |||||
| B783 | AND #&7f ; Clear pending flag (bit 7) | |||||
| B785 | STA fcb_status,x ; Update FCB status | |||||
| B788 | JSR find_open_fcb ; Find new open FCB slot | |||||
| B78B | LDA fcb_status,x ; Reload FCB flags | |||||
| B78E | .return_test_offset←1← B781 BPL | |||||
| AND #&20 ; Test bit 5 (has offset data) | ||||||
| B790 | RTS ; Return; Z=1 no offset, Z=0 has data | |||||
Increment 3-byte FCB transfer countIncrements l1000+X (low), cascading overflow to l1010+X (mid) and l1020+X (high).
|
||||
| B791 | .inc_fcb_byte_count←2← B836 JSR← B910 JSR | |||
| INC fcb_count_lo,x ; Increment byte count low | ||||
| B794 | BNE return_from_inc_fcb_count ; No overflow: done | |||
| B796 | INC fcb_attr_or_count_mid,x ; Increment byte count mid | |||
| B799 | BNE return_from_inc_fcb_count ; No overflow: done | |||
| B79B | INC fcb_station_or_count_hi,x ; Increment byte count high | |||
| B79E | .return_from_inc_fcb_count←2← B794 BNE← B799 BNE | |||
| RTS ; Return | ||||
Process all active FCB slotsSaves fs_options, fs_block_offset, and X/Y on the stack, then scans FCB slots &0F down to 0. Calls start_wipe_pass for each active entry matching the filter attribute in Y (0=match all). Restores all saved context on completion. Also contains the OSBGET/OSBPUT inline logic for reading and writing bytes through file channels.
|
||||
| B79F | .process_all_fcbs←9← 8D7C JSR← 8F87 JSR← 948C JSR← 9BCD JSR← 9C13 JSR← 9CB6 JSR← 9D7E JSR← 9E4C JSR← A67D JSR | |||
| TXA ; Save X | ||||
| B7A0 | PHA ; Push X to stack | |||
| B7A1 | TYA ; Save Y | |||
| B7A2 | PHA ; Push Y to stack | |||
| B7A3 | LDA fs_options ; Save fs_options | |||
| B7A5 | PHA ; Push fs_options | |||
| B7A6 | LDA fs_block_offset ; Save fs_block_offset | |||
| B7A8 | PHA ; Push fs_block_offset | |||
| B7A9 | LDX #&0f ; Start from FCB slot &0F | |||
| B7AB | STX cur_fcb_index ; Store as current FCB index | |||
| B7AE | .loop_process_fcb←1← B7C2 BPL | |||
| LDX cur_fcb_index ; Load current FCB index | ||||
| B7B1 | TYA ; Get filter attribute | |||
| B7B2 | BEQ done_flush_fcb ; Zero: process all FCBs | |||
| B7B4 | CMP fcb_attr_ref,x ; Compare with FCB attribute ref | |||
| B7B7 | BNE done_advance_fcb ; No match: skip this FCB | |||
| B7B9 | .done_flush_fcb←1← B7B2 BEQ | |||
| PHA ; Save filter attribute | ||||
| B7BA | JSR start_wipe_pass ; Flush pending data for this FCB | |||
| B7BD | PLA ; Restore filter | |||
| B7BE | TAY ; Y = filter attribute | |||
| B7BF | .done_advance_fcb←1← B7B7 BNE | |||
| DEC cur_fcb_index ; Previous FCB index | ||||
| B7C2 | BPL loop_process_fcb ; More slots: continue loop | |||
| B7C4 | PLA ; Restore fs_block_offset | |||
| B7C5 | STA fs_block_offset ; Store fs_block_offset | |||
| B7C7 | PLA ; Restore fs_options | |||
| B7C8 | STA fs_options ; Store fs_options | |||
| B7CA | PLA ; Restore Y | |||
| B7CB | TAY ; Y restored | |||
| B7CC | PLA ; Restore X | |||
| B7CD | TAX ; X restored | |||
| B7CE | RTS ; Return | |||
| B7CF | STY cur_chan_attr ; Save channel attribute | |||
| B7D2 | TXA ; Save caller's X | |||
| B7D3 | PHA ; Push X | |||
| B7D4 | JSR store_result_check_dir ; Store result and check not directory | |||
| B7D7 | LDA chan_status,x ; Load channel flags | |||
| B7DA | AND #&20 ; Test write-only flag (bit 5) | |||
| B7DC | BEQ done_read_fcb_byte ; Not write-only: proceed with read | |||
| B7DE | LDA #&d4 ; Error code &D4 | |||
| B7E0 | JSR error_inline_log ; Generate 'Write only' error | |||
| B7E3 | EQUS "Write only." | |||
| B7EE | .done_read_fcb_byte←1← B7DC BEQ | |||
| CLV ; Clear V (first-pass matching) | ||||
| B7EF | JSR find_matching_fcb ; Find FCB matching this channel | |||
| B7F2 | BEQ done_load_from_buf ; No offset: read byte from buffer | |||
| B7F4 | LDA fcb_count_lo,y ; Load byte count for matching FCB | |||
| B7F7 | CMP fcb_buf_offset,x ; Compare with buffer offset limit | |||
| B7FA | BCC done_load_from_buf ; Below offset: data available | |||
| B7FC | LDA chan_status,y ; Load channel flags for FCB | |||
| B7FF | TAX ; Transfer to X for testing | |||
| B800 | AND #&40 ; Test bit 6 (EOF already signalled) | |||
| B802 | BNE error_end_of_file ; EOF already set: raise error | |||
| B804 | TXA ; Restore flags | |||
| B805 | ORA #&40 ; Set EOF flag (bit 6) | |||
| B807 | STA chan_status,y ; Update channel flags with EOF | |||
| B80A | LDA #0 ; A=0: clear receive attribute | |||
| B80C | LDY #&0e ; Offset &0E | |||
| B80E | STA (net_rx_ptr),y ; Clear attribute in receive buffer | |||
| B810 | PLA ; Restore caller's X | |||
| B811 | TAX ; X restored | |||
| B812 | LDA #&fe ; A=&FE: EOF marker byte | |||
| B814 | LDY cur_chan_attr ; Restore channel attribute | |||
| B817 | SEC ; C=1: end of file | |||
| B818 | RTS ; Return | |||
| B819 | .error_end_of_file←1← B802 BNE | |||
| LDA #&df ; Error code &DF | ||||
| B81B | JSR error_inline_log ; Generate 'End of file' error | |||
| B81E | EQUS "End of file." | |||
| B82A | .done_load_from_buf←2← B7F2 BEQ← B7FA BCC | |||
| LDA fcb_count_lo,y ; Load current byte count (= offset) | ||||
| B82D | PHA ; Save byte count | |||
| B82E | TYA ; Get FCB slot index | |||
| B82F | TAX ; X = FCB slot for byte count inc | |||
| B830 | LDA #0 ; A=0: clear receive attribute | |||
| B832 | LDY #&0e ; Offset &0E | |||
| B834 | STA (net_rx_ptr),y ; Clear attribute in receive buffer | |||
| B836 | JSR inc_fcb_byte_count ; Increment byte count for this FCB | |||
| B839 | PLA ; Restore byte count (= buffer offset) | |||
| B83A | TAY ; Y = offset into data buffer | |||
| B83B | LDA cur_fcb_index ; Load current FCB index | |||
| B83E | CLC ; Prepare addition | |||
| B83F | ADC #&11 ; Add &11 for buffer page offset | |||
| B841 | STA fs_load_addr_3 ; Set pointer high byte | |||
| B843 | LDA #0 ; A=0: pointer low byte | |||
| B845 | STA fs_load_addr_2 ; Set pointer low byte | |||
| B847 | PLA ; Restore caller's X | |||
| B848 | TAX ; X restored | |||
| B849 | LDA (fs_load_addr_2),y ; Read data byte from buffer | |||
| B84B | LDY cur_chan_attr ; Restore channel attribute | |||
| B84E | CLC ; C=0: byte read successfully | |||
| B84F | RTS ; Return; A = data byte | |||
| B850 | STY cur_chan_attr ; Save channel attribute | |||
| B853 | PHA ; Save data byte | |||
| B854 | TAY ; Y = data byte | |||
| B855 | TXA ; Save caller's X | |||
| B856 | PHA ; Push X | |||
| B857 | TYA ; Restore data byte to A | |||
| B858 | PHA ; Push data byte for later | |||
| B859 | STA osbput_saved_byte ; Save data byte in workspace | |||
| B85C | JSR store_result_check_dir ; Store result and check not directory | |||
| B85F | LDA chan_status,x ; Load channel flags | |||
| B862 | BMI done_test_write_flag ; Bit 7 set: channel open, proceed | |||
| B864 | LDA #&c1 ; Error &C1: Not open for update | |||
| B866 | JSR error_inline_log ; Raise error with inline string | |||
| B869 | EQUS "Not open for update." | |||
| B87D | .done_test_write_flag←1← B862 BMI | |||
| AND #&20 ; Test write flag (bit 5) | ||||
| B87F | BEQ done_find_write_fcb ; Not write-capable: use buffer path | |||
| B881 | LDY fcb_net_or_port,x ; Load reply port for this channel | |||
| B884 | PLA ; Restore data byte | |||
| B885 | JSR send_wipe_request ; Send byte directly to server | |||
| B888 | JMP done_inc_byte_count ; Update byte count and return | |||
| B88B | .done_find_write_fcb←1← B87F BEQ | |||
| BIT bit_test_ff ; Set V flag (alternate match mode) | ||||
| B88E | JSR find_matching_fcb ; Find matching FCB for channel | |||
| B891 | LDA fcb_count_lo,y ; Load byte count for FCB | |||
| B894 | CMP #&ff ; Buffer full (&FF bytes)? | |||
| B896 | BNE done_check_buf_offset ; No: store byte in buffer | |||
| B898 | TXA ; Save X | |||
| B899 | PHA ; Push X | |||
| B89A | TYA ; Save Y (FCB slot) | |||
| B89B | PHA ; Push Y | |||
| B89C | LDA fcb_net_or_port,y ; Load reply port for FCB | |||
| B89F | PHA ; Save reply port | |||
| B8A0 | LDY #0 ; Y=0: no nested context | |||
| B8A2 | JSR save_fcb_context ; Save context and flush FCB data | |||
| B8A5 | PLA ; Restore reply port | |||
| B8A6 | STA fs_cmd_data ; Store reply port in TX buffer | |||
| B8A9 | TAX ; X = reply port | |||
| B8AA | PLA ; Restore Y (FCB slot) | |||
| B8AB | TAY ; Y restored | |||
| B8AC | PHA ; Save Y again for later restore | |||
| B8AD | TXA ; A = reply port | |||
| B8AE | PHA ; Save reply port for send | |||
| B8AF | LDX #0 ; Command byte = 0 | |||
| B8B1 | STX fs_func_code ; Store in TX buffer | |||
| B8B4 | DEX ; X=&FF: flag byte X=&ff | |||
| B8B5 | STX fs_data_count ; Store &FF in TX buffer | |||
| B8B8 | LDA fcb_attr_or_count_mid,y ; Load station for FCB | |||
| B8BB | STA fs_reply_cmd ; Store in TX buffer | |||
| B8BE | LDA fcb_station_or_count_hi,y ; Load network for FCB | |||
| B8C1 | STA fs_load_vector ; Store in TX buffer | |||
| B8C4 | LDY #&0d ; Function code &0D | |||
| B8C6 | LDX #5 ; X=5: copy 5 bytes to TX | |||
| B8C8 | JSR save_net_tx_cb ; Send flush request to server | |||
| B8CB | PLA ; Restore reply port | |||
| B8CC | TAY ; Y = reply port | |||
| B8CD | LDA osbput_saved_byte ; Load saved data byte | |||
| B8D0 | JSR send_wipe_request ; Send data byte to server | |||
| B8D3 | JSR restore_catalog_entry ; Restore TX buffer from saved context | |||
| B8D6 | PLA ; Restore Y (FCB slot) | |||
| B8D7 | TAY ; Y restored | |||
| B8D8 | PLA ; Restore X | |||
| B8D9 | TAX ; X restored | |||
| B8DA | LDA fcb_count_lo,y ; Reload byte count after flush | |||
| B8DD | .done_check_buf_offset←1← B896 BNE | |||
| CMP fcb_buf_offset,x ; Compare count with buffer offset | ||||
| B8E0 | BCC done_set_dirty_flag ; Below offset: skip offset update | |||
| B8E2 | ADC #0 ; Add carry (count + 1) | |||
| B8E4 | STA fcb_buf_offset,x ; Update buffer offset in FCB | |||
| B8E7 | BNE done_set_dirty_flag ; Non-zero: keep offset flag | |||
| B8E9 | LDA #&df ; Mask &DF: clear bit 5 | |||
| B8EB | AND fcb_status,x ; Clear offset flag | |||
| B8EE | STA fcb_status,x ; Update FCB status | |||
| B8F1 | .done_set_dirty_flag←2← B8E0 BCC← B8E7 BNE | |||
| LDA #1 ; Set bit 0 (dirty/active) | ||||
| B8F3 | ORA fcb_status,x ; Add to FCB flags | |||
| B8F6 | STA fcb_status,x ; Update FCB status | |||
| B8F9 | LDA fcb_count_lo,y ; Load byte count (= write position) | |||
| B8FC | PHA ; Save count | |||
| B8FD | TYA ; Get FCB slot index | |||
| B8FE | TAX ; X = FCB slot | |||
| B8FF | PLA ; Restore byte count | |||
| B900 | TAY ; Y = buffer write offset | |||
| B901 | LDA cur_fcb_index ; Load current FCB index | |||
| B904 | CLC ; Prepare addition | |||
| B905 | ADC #&11 ; Add &11 for buffer page offset | |||
| B907 | STA fs_load_addr_3 ; Set pointer high byte | |||
| B909 | LDA #0 ; A=0: pointer low byte | |||
| B90B | STA fs_load_addr_2 ; Set pointer low byte | |||
| B90D | PLA ; Restore data byte | |||
| B90E | STA (fs_load_addr_2),y ; Write data byte to buffer | |||
| B910 | .done_inc_byte_count←1← B888 JMP | |||
| JSR inc_fcb_byte_count ; Increment byte count for this FCB | ||||
| B913 | LDA #0 ; A=0: clear receive attribute | |||
| B915 | LDY #&0e ; Offset &0E | |||
| B917 | STA (net_rx_ptr),y ; Clear attribute in receive buffer | |||
| B919 | PLA ; Restore caller's X | |||
| B91A | TAX ; X restored | |||
| B91B | PLA ; Discard saved data byte | |||
| B91C | LDY cur_chan_attr ; Restore channel attribute | |||
| B91F | RTS ; Return | |||
Send wipe/close request packetSets up the TX control block with function code &90, the reply port from Y, and the data byte from A. Sends via send_disconnect_reply, then checks the error code — raises the server error if non-zero.
|
||||||
| B920 | .send_wipe_request←2← B885 JSR← B8D0 JSR | |||||
| STY fs_handle_mask ; Store reply port | ||||||
| B923 | STA fs_error_flags ; Store data byte | |||||
| B926 | TYA ; Save Y | |||||
| B927 | PHA ; Push Y to stack | |||||
| B928 | TXA ; Save X | |||||
| B929 | PHA ; Push X to stack | |||||
| B92A | LDA #&90 ; Function code &90 | |||||
| B92C | STA fs_putb_buf ; Store in send buffer | |||||
| B92F | JSR init_txcb ; Initialise TX control block | |||||
| B932 | LDA #&dc ; TX start address low = &DC | |||||
| B934 | STA txcb_start ; Set TX start in control block | |||||
| B936 | LDA #&e0 ; TX end address low = &E0 | |||||
| B938 | STA txcb_end ; Set TX end in control block | |||||
| B93A | LDA #9 ; Expected reply port = 9 | |||||
| B93C | STA fs_getb_buf ; Store reply port in buffer | |||||
| B93F | LDX #&c0 ; TX control = &C0 | |||||
| B941 | LDY #0 ; Y=0: no timeout | |||||
| B943 | LDA fs_handle_mask ; Load reply port for addressing | |||||
| B946 | JSR send_disconnect_reply ; Send packet to server | |||||
| B949 | LDA fs_getb_buf ; Load reply status | |||||
| B94C | BEQ done_toggle_station ; Zero: success | |||||
| B94E | STA fs_last_error ; Store error code | |||||
| B951 | LDX #0 ; X=0: copy index | |||||
| B953 | .loop_copy_wipe_err_msg←1← B95E BNE | |||||
| LDA fs_putb_buf,x ; Load error message byte | ||||||
| B956 | STA error_block,x ; Copy to error block | |||||
| B959 | CMP #&0d ; Is it CR (end of message)? | |||||
| B95B | BEQ done_terminate_wipe_err ; Yes: terminate string | |||||
| B95D | INX ; Next byte | |||||
| B95E | BNE loop_copy_wipe_err_msg ; Continue copying error message | |||||
| B960 | .done_terminate_wipe_err←1← B95B BEQ | |||||
| LDA #0 ; NUL terminator | ||||||
| B962 | STA error_block,x ; Terminate error string in block | |||||
| B965 | DEX ; Back up position for error check | |||||
| B966 | JMP check_net_error_code ; Process and raise network error | |||||
| B969 | .done_toggle_station←1← B94C BEQ | |||||
| LDX cur_chan_attr ; Load channel attribute index | ||||||
| B96C | LDA fcb_flags,x ; Load station number for channel | |||||
| B96F | EOR #1 ; Toggle bit 0 (alternate station) | |||||
| B971 | STA fcb_flags,x ; Update station number | |||||
| B974 | PLA ; Restore X | |||||
| B975 | TAX ; X restored | |||||
| B976 | PLA ; Restore Y | |||||
| B977 | TAY ; Y restored | |||||
| B978 | RTS ; Return | |||||
Set up FS options and transfer workspaceCalls set_options_ptr to configure the FS options pointer, then jumps to setup_transfer_workspace to initialise the transfer and send the request.
|
||||||||
| B979 | .send_and_receive←2← B644 JSR← B6DD JSR | |||||||
| JSR set_options_ptr ; Set up FS options pointer | ||||||||
| B97C | JMP setup_transfer_workspace ; Set up transfer workspace and return | |||||||
*Close command handlerLoads A=0 and Y=0, then jumps to OSFIND to close all open files on the current file server (equivalent to CLOSE#0). Files open on other file servers are not affected. |
|
| B97F | .cmd_close |
| LDA #osfind_close ; A=0: close all open files | |
| B981 | TAY ; Y=&00 |
| B982 | JMP osfind ; Close all files (Y=0) |
*Type command handlerClears V and branches to the shared open_and_read_file entry in cmd_print. The V-clear state selects line- ending normalisation mode: CR, LF, CR+LF, and LF+CR are all treated as a single newline. Designed for displaying text files.
|
||||
| B985 | .cmd_type | |||
| CLV ; Clear V for unconditional BVC | ||||
| B986 | BVC open_and_read_file ; ALWAYS branch | |||
*Print command handlerSets V flag (distinguishing from *Type which clears V), then opens the file for reading. Loops reading bytes via OSBGET, checking for escape between each. In type mode (V clear), normalises CR/LF pairs to single newlines by tracking the previous character. In print mode (V set), outputs all bytes raw via OSWRCH. Closes the file and prints a final newline on EOF.
|
||||
| B988 | .cmd_print | |||
| BIT bit_test_ff ; Set V flag (= print mode) | ||||
| B98B | .open_and_read_file←1← B986 BVC | |||
| JSR open_file_for_read ; Open file for reading | ||||
| B98E | LDY ws_page ; Y=file handle | |||
| B990 | LDA #0 ; A = 0 | |||
| B992 | STA table_idx ; Clear previous-character tracker | |||
| B994 | PHP ; Save V flag (print/type mode) | |||
| B995 | .loop_read_print_byte←3← B9B5 JMP← B9D5 JMP← B9E8 BEQ | |||
| JSR osbget ; Read a single byte from an open file Y | ||||
| B998 | BCC done_print_escape ; Branch if not end of file | |||
| B99A | PLP ; EOF: restore processor status | |||
| B99B | JSR close_ws_file ; Close the file | |||
| B99E | JMP osnewl ; Write newline (characters 10 and 13) | |||
| B9A1 | .done_print_escape←1← B998 BCC | |||
| JSR abort_if_escape ; Check for escape key pressed | ||||
| B9A4 | PLP ; Restore V (print/type mode) | |||
| B9A5 | PHP ; Re-save for next iteration | |||
| B9A6 | BVS done_store_prev_char ; Print mode: skip CR/LF handling | |||
| B9A8 | CMP #&0d ; Is it a carriage return? | |||
| B9AA | BEQ done_handle_line_end ; Yes: handle line ending | |||
| B9AC | CMP #&0a ; Is it a line feed? | |||
| B9AE | BEQ done_handle_line_end ; Yes: handle line ending | |||
| B9B0 | .done_store_prev_char←1← B9A6 BVS | |||
| STA table_idx ; Save as previous character | ||||
| B9B2 | .loop_write_char←1← B9C3 BNE | |||
| JSR oswrch ; Write character | ||||
| B9B5 | JMP loop_read_print_byte ; Loop for next byte | |||
| B9B8 | .done_handle_line_end←2← B9AA BEQ← B9AE BEQ | |||
| PHA ; Save the CR or LF character | ||||
| B9B9 | LDX vdu_queue_count ; Check output destination flag | |||
| B9BC | BEQ done_normalise_crlf ; Zero: normalise line endings | |||
| B9BE | LDA #0 ; Non-zero: output raw | |||
| B9C0 | STA table_idx ; Clear previous-character tracker | |||
| B9C2 | PLA ; Retrieve CR/LF | |||
| B9C3 | BNE loop_write_char ; Output it directly; ALWAYS branch | |||
| B9C5 | .done_normalise_crlf←1← B9BC BEQ | |||
| LDA table_idx ; Get previous character | ||||
| B9C7 | CMP #&0d ; Was previous a CR? | |||
| B9C9 | BEQ done_check_cr_lf ; Yes: check for CR+LF pair | |||
| B9CB | CMP #&0a ; Was previous a LF? | |||
| B9CD | BEQ done_check_lf_cr ; Yes: check for LF+CR pair | |||
| B9CF | PLA ; Retrieve CR/LF from stack | |||
| B9D0 | STA table_idx ; Save as previous character | |||
| B9D2 | .done_write_newline←2← B9DD BNE← B9E2 BNE | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| B9D5 | JMP loop_read_print_byte ; Loop for next byte | |||
| B9D8 | .done_check_cr_lf←1← B9C9 BEQ | |||
| PLA ; Retrieve current character | ||||
| B9D9 | CMP #&0a ; Is it LF? (CR+LF pair) | |||
| B9DB | BEQ done_consume_pair ; Yes: consume LF, no extra newline | |||
| B9DD | BNE done_write_newline ; No: output extra newline ALWAYS branch | |||
| B9DF | .done_check_lf_cr←1← B9CD BEQ | |||
| PLA ; Retrieve current character | ||||
| B9E0 | CMP #&0d ; Is it CR? (LF+CR pair) | |||
| B9E2 | BNE done_write_newline ; No: output extra newline | |||
| B9E4 | .done_consume_pair←1← B9DB BEQ | |||
| LDA #0 ; Pair consumed: A = 0 | ||||
| B9E6 | STA table_idx ; Clear previous-character tracker | |||
| B9E8 | BEQ loop_read_print_byte ; Loop for next byte; ALWAYS branch ALWAYS branch | |||
Test escape flag and abort if pressedChecks the escape flag byte; returns immediately if bit 7 is clear. If escape has been pressed, falls through to the escape abort handler which acknowledges the escape via OSBYTE &7E. |
|
| B9EA | .abort_if_escape←2← B9A1 JSR← BA1E JSR |
| BIT escape_flag ; Test bit 7 of escape flag | |
| B9EC | BMI error_escape_pressed ; Escape pressed: handle abort |
| B9EE | RTS ; No escape: return |
| B9EF | .error_escape_pressed←1← B9EC BMI |
| JSR close_ws_file ; Close the open file | |
| B9F2 | JSR osnewl ; Write newline (characters 10 and 13) |
| B9F5 | LDA #osbyte_acknowledge_escape ; Acknowledge escape condition |
| B9F7 | JSR osbyte ; Clear escape condition and perform escape effects |
| B9FA | LDA #&11 ; Error number &11 |
| B9FC | JSR error_inline ; Generate 'Escape' BRK error |
| B9FF | EQUS "Escape." |
| fall through ↓ | |
*Dump command handlerOpens the file via open_file_for_read, allocates a 21-byte buffer on the stack, and parses the address range via init_dump_buffer. Loops reading 16 bytes per line, printing each as a 4-byte hex address, 16 hex bytes with spaces, and a 16-character ASCII column (non-printable chars shown as '.'). Prints a column header at every 256-byte boundary.
|
||||
| BA06 | .cmd_dump | |||
| JSR open_file_for_read ; Open file for reading, set ws_page | ||||
| BA09 | LDX #&14 ; 21 bytes to push (0-&14) | |||
| BA0B | LDA #0 ; Zero fill value | |||
| BA0D | .loop_push_zero_buf←1← BA0F BPL | |||
| PHA ; Push zero onto stack | ||||
| BA0E | DEX ; Count down | |||
| BA0F | BPL loop_push_zero_buf ; Loop until all 21 bytes pushed | |||
| BA11 | TSX ; X = stack pointer (buffer base - 1) | |||
| BA12 | JSR init_dump_buffer ; Set up buffer pointer and parse args | |||
| BA15 | LDA (work_ae),y ; Load display address low byte | |||
| BA17 | AND #&f0 ; Test high nibble | |||
| BA19 | BEQ loop_dump_line ; Skip header if 16-byte aligned | |||
| BA1B | JSR print_dump_header ; Print column header for offset start | |||
| BA1E | .loop_dump_line←2← BA19 BEQ← BAC6 JMP | |||
| JSR abort_if_escape ; Check for Escape key | ||||
| BA21 | LDA #&ff ; Start byte counter at -1 | |||
| BA23 | STA osword_flag ; Reset counter | |||
| BA25 | .loop_read_dump_byte←1← BA34 BNE | |||
| LDY ws_page ; Y=file handle | ||||
| BA27 | JSR osbget ; Read a single byte from an open file Y | |||
| BA2A | BCS done_check_dump_eof ; C=1 from OSBGET: end of file | |||
| BA2C | INC osword_flag ; Increment byte counter (0-15) | |||
| BA2E | LDY osword_flag ; Use counter as buffer index | |||
| BA30 | STA (work_ae),y ; Store byte in data buffer | |||
| BA32 | CPY #&0f ; Read 16 bytes? (index 0-15) | |||
| BA34 | BNE loop_read_dump_byte ; No: read next byte | |||
| BA36 | CLC ; C=0: not EOF, full line read | |||
| BA37 | .done_check_dump_eof←1← BA2A BCS | |||
| PHP ; Save C: EOF status | ||||
| BA38 | LDA osword_flag ; Check byte counter | |||
| BA3A | BPL done_check_boundary ; Counter >= 0: have data to display | |||
| BA3C | LDX #&15 ; 22 bytes to pop (21 buffer + PHP) | |||
| BA3E | .loop_pop_stack_buf←2← BA40 BPL← BACB JMP | |||
| PLA ; Pop one byte from stack | ||||
| BA3F | DEX ; Count down | |||
| BA40 | BPL loop_pop_stack_buf ; Loop until stack cleaned up | |||
| BA42 | JMP close_ws_file ; Close file and return | |||
| BA45 | .done_check_boundary←1← BA3A BPL | |||
| LDY #&10 ; Point to display address low byte | ||||
| BA47 | LDA (work_ae),y ; Load display address low byte | |||
| BA49 | AND #&f0 ; Test high nibble | |||
| BA4B | BNE done_start_dump_addr ; Non-zero: header already current | |||
| BA4D | JSR print_dump_header ; Crossed 256-byte boundary: new header | |||
| BA50 | .done_start_dump_addr←1← BA4B BNE | |||
| LDY #&13 ; Start from highest address byte | ||||
| BA52 | .loop_print_addr_byte←1← BA5C BNE | |||
| LDA (work_ae),y ; Load address byte | ||||
| BA54 | PHA ; Save for address increment later | |||
| BA55 | JSR print_hex_byte ; Print as two hex digits | |||
| BA58 | PLA ; Restore address byte | |||
| BA59 | DEY ; Next byte down | |||
| BA5A | CPY #&0f ; Printed all 4 address bytes? | |||
| BA5C | BNE loop_print_addr_byte ; No: print next address byte | |||
| BA5E | INY ; Y=&10: point to address byte 0 | |||
| BA5F | CLC ; Prepare for 16-byte add | |||
| BA60 | ADC #&10 ; Add 16 to lowest address byte | |||
| BA62 | PHP ; Save carry for propagation | |||
| BA63 | .loop_inc_dump_addr←1← BA6E BNE | |||
| PLP ; Restore carry from previous byte | ||||
| BA64 | STA (work_ae),y ; Store updated address byte | |||
| BA66 | INY ; Next address byte up | |||
| BA67 | LDA (work_ae),y ; Load next address byte | |||
| BA69 | ADC #0 ; Add carry | |||
| BA6B | PHP ; Save carry for next byte | |||
| BA6C | CPY #&14 ; Past all 4 address bytes? | |||
| BA6E | BNE loop_inc_dump_addr ; No: continue propagation | |||
| BA70 | PLP ; Discard final carry | |||
| BA71 | JSR print_inline ; Print address/data separator | |||
| BA74 | EQUS " : " | |||
| BA77 | LDY #0 ; Start from first data byte | |||
| BA79 | LDX osword_flag ; X = bytes read (counter for display) | |||
| BA7B | .loop_print_dump_hex←1← BA8B BPL | |||
| LDA (work_ae),y ; Load data byte from buffer | ||||
| BA7D | JSR print_hex_byte ; Print as two hex digits | |||
| BA80 | LDA #&20 ; Space separator | |||
| BA82 | JSR osasci ; Print space between hex bytes Write character 32 | |||
| BA85 | .loop_next_dump_col←1← BA98 JMP | |||
| INY ; Next column | ||||
| BA86 | CPY #&10 ; All 16 columns done? | |||
| BA88 | BEQ done_print_separator ; Yes: go to ASCII separator | |||
| BA8A | DEX ; Decrement remaining data bytes | |||
| BA8B | BPL loop_print_dump_hex ; More data: print next hex byte | |||
| BA8D | TYA ; Save column position | |||
| BA8E | PHA ; Preserve Y across print | |||
| BA8F | JSR print_inline ; Print 3-space padding | |||
| BA92 | EQUS " " | |||
| BA95 | NOP ; Inline string terminator (NOP) | |||
| BA96 | PLA ; Restore column position | |||
| BA97 | TAY ; Back to Y | |||
| BA98 | JMP loop_next_dump_col ; Check next column | |||
| BA9B | .done_print_separator←1← BA88 BEQ | |||
| DEX ; Adjust X for advance_x_by_8 | ||||
| BA9C | JSR print_inline ; Print hex/ASCII separator | |||
| BA9F | EQUS ": " | |||
| BAA1 | NOP ; Inline string terminator (NOP) | |||
| BAA2 | JSR advance_x_by_8 ; X += 16: restore byte count for ASCII | |||
| BAA5 | LDY #0 ; Start from first data byte | |||
| BAA7 | .loop_print_dump_ascii←1← BABE BPL | |||
| LDA (work_ae),y ; Load data byte | ||||
| BAA9 | AND #&7f ; Strip high bit | |||
| BAAB | CMP #&20 ; Printable? (>= space) | |||
| BAAD | BCS done_test_del ; Yes: check for DEL | |||
| BAAF | .skip_non_printable←1← BAB3 BEQ | |||
| LDA #&2e ; Non-printable: substitute '.' | ||||
| BAB1 | .done_test_del←1← BAAD BCS | |||
| CMP #&7f ; Is it DEL (&7F)? | ||||
| BAB3 | BEQ skip_non_printable ; Yes: substitute '.' | |||
| BAB5 | JSR osasci ; Print ASCII character Write character | |||
| BAB8 | INY ; Next column | |||
| BAB9 | CPY #&10 ; All 16 columns done? | |||
| BABB | BEQ done_end_dump_line ; Yes: end of line | |||
| BABD | DEX ; Decrement remaining data bytes | |||
| BABE | BPL loop_print_dump_ascii ; More data: print next ASCII char | |||
| BAC0 | .done_end_dump_line←1← BABB BEQ | |||
| JSR osnewl ; Print newline Write newline (characters 10 and 13) | ||||
| BAC3 | PLP ; Restore EOF status from &BA37 | |||
| BAC4 | BCS done_dump_eof ; C=1: EOF reached, clean up | |||
| BAC6 | JMP loop_dump_line ; Not EOF: continue with next line | |||
| BAC9 | .done_dump_eof←1← BAC4 BCS | |||
| LDX #&14 ; 21 bytes to pop (buffer only, PHP done) | ||||
| BACB | JMP loop_pop_stack_buf ; Reuse stack cleanup loop | |||
Print hex dump column header lineOutputs the starting address followed by 16 hex column numbers (00-0F), each separated by a space. Provides the column alignment header for *Dump output. |
|
| BACE | .print_dump_header←2← BA1B JSR← BA4D JSR |
| LDA (work_ae),y ; Load display address low byte | |
| BAD0 | PHA ; Save as starting column number |
| BAD1 | JSR print_inline ; Print header label with leading CR |
| BAD4 | EQUS ".Address : " |
| BAE0 | NOP ; Inline string terminator (NOP) |
| BAE1 | PLA ; Restore starting column number |
| BAE2 | LDX #&0f ; 16 column headers to print |
| BAE4 | .loop_print_col_num←1← BAF4 BPL |
| PHA ; Save current column number | |
| BAE5 | JSR print_hex_byte ; Print as two hex digits |
| BAE8 | LDA #&20 ; Space separator |
| BAEA | JSR osasci ; Print space after column number Write character 32 |
| BAED | PLA ; Restore column number |
| BAEE | SEC ; SEC for +1 via ADC |
| BAEF | ADC #0 ; Increment column number (SEC+ADC 0=+1) |
| BAF1 | AND #&0f ; Wrap to low nibble (0-F) |
| BAF3 | DEX ; Count down |
| BAF4 | BPL loop_print_col_num ; Loop for all 16 columns |
| BAF6 | JSR print_inline ; Print trailer with ASCII label |
| BAF9 | EQUS ": ASCII data.." |
| BB0A | NOP ; Inline string terminator (NOP) |
| BB0B | RTS ; Return |
Close file handle stored in workspaceLoads the file handle from ws_page and closes it via OSFIND with A=0. |
|
| BB0C | .close_ws_file←6← B99B JSR← B9EF JSR← BA42 JMP← BBAF JSR← BBE9 JSR← BC56 JSR |
| LDY ws_page ; Y = file handle from ws_page | |
| BB0E | LDA #osfind_close ; A=0: close file |
| BB10 | JMP osfind ; Close file and return Close one or all files |
Open file for reading via OSFINDComputes the filename address from the command text pointer plus the Y offset, calls OSFIND with A=&40 (open for input). Stores the handle in ws_page. Raises 'Not found' if the returned handle is zero. |
|
| BB13 | .open_file_for_read←2← B98B JSR← BA06 JSR |
| PHP ; Save processor flags | |
| BB14 | TYA ; A = filename offset |
| BB15 | CLC ; Add to command text pointer |
| BB16 | ADC os_text_ptr ; Low byte of filename address |
| BB18 | PHA ; Save on stack for later restore |
| BB19 | TAX ; X = filename address low |
| BB1A | LDA #0 ; Carry into high byte |
| BB1C | ADC os_text_ptr_hi ; High byte of filename address |
| BB1E | PHA ; Save on stack for later restore |
| BB1F | TAY ; Y = filename address high |
| BB20 | LDA #osfind_open_input ; Open for input |
| BB22 | JSR osfind ; OSFIND: open file Open file for input (A=64) |
| BB25 | TAY ; A=file handle (or zero on failure) |
| BB26 | STA ws_page ; Store file handle |
| BB28 | BNE done_restore_text_ptr ; Non-zero: file opened OK |
| BB2A | LDA #&d6 ; Error number &D6 |
| BB2C | JSR error_inline ; Generate 'Not found' error |
| BB2F | EQUS "Not found." |
| BB39 | .done_restore_text_ptr←1← BB28 BNE |
| PLA ; Restore saved text pointer high | |
| BB3A | STA os_text_ptr_hi ; Restore os_text_ptr high byte |
| BB3C | PLA ; Restore saved text pointer low |
| BB3D | STA os_text_ptr ; Restore os_text_ptr low byte |
| BB3F | LDY #0 ; Start scanning from offset 0 |
| BB41 | .loop_skip_filename←1← BB4A BNE |
| INY ; Advance past current char | |
| BB42 | LDA (os_text_ptr),y ; Load next char from command line |
| BB44 | CMP #&0d ; CR: end of command line |
| BB46 | BEQ return_with_fn_offset ; Yes: done scanning |
| BB48 | CMP #&20 ; Space: end of filename |
| BB4A | BNE loop_skip_filename ; No: keep scanning filename |
| BB4C | .loop_skip_fn_spaces←1← BB51 BEQ |
| INY ; Advance past space | |
| BB4D | LDA (os_text_ptr),y ; Load next char |
| BB4F | CMP #&20 ; Still a space? |
| BB51 | BEQ loop_skip_fn_spaces ; Yes: skip it |
| BB53 | .return_with_fn_offset←1← BB46 BEQ |
| PLP ; Restore processor flags | |
| BB54 | RTS ; Return; Y = offset to next argument |
Parse hex address for dump rangeReads up to 4 hex digits from the command line into a 4-byte accumulator, stopping at CR or space. Each digit shifts the accumulator left by 4 bits before ORing in the new nybble. |
|
| BB55 | .parse_dump_range←2← BBC5 JSR← BC51 JSR |
| TYA ; Save command line offset to X | |
| BB56 | TAX ; X tracks current position |
| BB57 | LDA #0 ; Zero for clearing accumulator |
| BB59 | TAY ; Y=0 for buffer indexing Y=&00 |
| BB5A | .loop_clear_hex_accum←1← BB5F BNE |
| STA (work_ae),y ; Clear accumulator byte | |
| BB5C | INY ; Next byte |
| BB5D | CPY #4 ; All 4 bytes cleared? |
| BB5F | BNE loop_clear_hex_accum ; No: clear next |
| BB61 | .loop_parse_hex_digit←1← BBA8 JMP |
| TXA ; Restore pre-increment offset to A | |
| BB62 | INX ; Advance X to next char position |
| BB63 | TAY ; Y = pre-increment offset for indexing |
| BB64 | LDA (os_text_ptr),y ; Load character from command line |
| BB66 | CMP #&0d ; CR: end of input |
| BB68 | BEQ done_test_hex_space ; Done: skip trailing spaces |
| BB6A | CMP #&20 ; Space: end of this parameter |
| BB6C | BEQ done_test_hex_space ; Done: skip trailing spaces |
| BB6E | CMP #&30 ; Below '0'? |
| BB70 | BCC error_bad_hex_value ; Yes: not a hex digit, error |
| BB72 | CMP #&3a ; Below ':'? (i.e. '0'-'9') |
| BB74 | BCC done_mask_hex_digit ; Yes: is a decimal digit |
| BB76 | AND #&5f ; Force uppercase for A-F |
| BB78 | ADC #&b8 ; Map 'A'-'F' → &FA-&FF (C=0 here) |
| BB7A | BCS error_bad_hex_value ; Carry set: char > 'F', error |
| BB7C | CMP #&fa ; Below &FA? (i.e. was < 'A') |
| BB7E | BCC error_bad_hex_value ; Yes: gap between '9' and 'A', error |
| BB80 | .done_mask_hex_digit←1← BB74 BCC |
| AND #&0f ; Mask to low nibble (0-15) | |
| BB82 | PHA ; Save hex digit value |
| BB83 | TXA ; Save current offset |
| BB84 | PHA ; Preserve on stack |
| BB85 | LDX #4 ; 4 bits to shift in |
| BB87 | .loop_shift_nibble←1← BB9D BNE |
| LDY #0 ; Start from byte 0 (LSB) | |
| BB89 | TYA ; Clear A; C from PHA/PLP below A=&00 |
| BB8A | .loop_rotate_hex_accum←1← BB96 BNE |
| PHA ; Transfer carry bit to flags via stack | |
| BB8B | PLP ; PLP: C = bit shifted out of prev iter |
| BB8C | LDA (work_ae),y ; Load accumulator byte |
| BB8E | ROL ; Rotate left through carry |
| BB8F | STA (work_ae),y ; Store shifted byte |
| BB91 | PHP ; Save carry for next byte |
| BB92 | PLA ; Transfer to A for PHA/PLP trick |
| BB93 | INY ; Next accumulator byte |
| BB94 | CPY #4 ; All 4 bytes rotated? |
| BB96 | BNE loop_rotate_hex_accum ; No: rotate next byte |
| BB98 | PHA ; Transfer carry to flags |
| BB99 | PLP ; C = overflow bit |
| BB9A | BCS error_hex_overflow ; Overflow: address too large |
| BB9C | DEX ; Count bits shifted |
| BB9D | BNE loop_shift_nibble ; 4 bits shifted? No: shift again |
| BB9F | PLA ; Restore command line offset |
| BBA0 | TAX ; Back to X |
| BBA1 | PLA ; Restore hex digit value |
| BBA2 | LDY #0 ; Point to LSB of accumulator |
| BBA4 | ORA (work_ae),y ; OR digit into low nibble |
| BBA6 | STA (work_ae),y ; Store updated LSB |
| BBA8 | JMP loop_parse_hex_digit ; Parse next character |
| BBAB | .error_hex_overflow←1← BB9A BCS |
| PLA ; Discard saved offset | |
| BBAC | PLA ; Discard saved digit |
| BBAD | SEC ; C=1: overflow |
| BBAE | RTS ; Return with C=1 |
| BBAF | .error_bad_hex_value←3← BB70 BCC← BB7A BCS← BB7E BCC |
| JSR close_ws_file ; Close open file before error | |
| BBB2 | JMP err_bad_hex ; Generate 'Bad hex' error |
| BBB5 | .loop_skip_hex_spaces←1← BBBA BEQ |
| INY ; Advance past space | |
| BBB6 | .done_test_hex_space←2← BB68 BEQ← BB6C BEQ |
| LDA (os_text_ptr),y ; Load next char | |
| BBB8 | CMP #&20 ; Space? |
| BBBA | BEQ loop_skip_hex_spaces ; Yes: skip it |
| BBBC | CLC ; C=0: valid parse (no overflow) |
| BBBD | RTS ; Return; Y past trailing spaces |
Initialise dump buffer and parse address rangeParses the start and end addresses from the command line via parse_dump_range. If no end address is given, defaults to the file extent. Validates both addresses against the file size, raising 'Outside file' if either exceeds the extent. |
|
| BBBE | .init_dump_buffer←1← BA12 JSR |
| INX ; X+1: first byte of buffer | |
| BBBF | STX work_ae ; Set buffer pointer low byte |
| BBC1 | LDX #1 ; Buffer is on stack in page 1 |
| BBC3 | STX addr_work ; Set buffer pointer high byte |
| BBC5 | JSR parse_dump_range ; Parse start offset from command line |
| BBC8 | BCS error_outside_file ; Overflow: 'Outside file' error |
| BBCA | TYA ; A = command line offset after parse |
| BBCB | PHA ; Save for later (past start addr) |
| BBCC | LDY ws_page ; Y=file handle |
| BBCE | LDX #&aa ; X=zero page address for result |
| BBD0 | LDA #2 ; A=2: read file extent (length) |
| BBD2 | JSR osargs ; Get length of file into zero page address X (A=2) |
| BBD5 | LDY #3 ; Check from MSB down |
| BBD7 | .loop_cmp_file_length←1← BBDF BPL |
| LDA osword_flag,y ; Load file length byte | |
| BBDA | CMP (work_ae),y ; Compare with start offset byte |
| BBDC | BNE done_check_outside ; Mismatch: check which is larger |
| BBDE | DEY ; Next byte down |
| BBDF | BPL loop_cmp_file_length ; More bytes to compare |
| BBE1 | BMI done_advance_start ; All equal: start = length, within file ALWAYS branch |
| BBE3 | .done_check_outside←1← BBDC BNE |
| BCC error_outside_file ; Length < start: outside file | |
| BBE5 | LDY #&ff ; Y=&FF: length > start, flag for later |
| BBE7 | BNE done_advance_start ; Continue to copy start address ALWAYS branch |
| BBE9 | .error_outside_file←2← BBC8 BCS← BBE3 BCC |
| JSR close_ws_file ; Close file before error | |
| BBEC | LDA #&b7 ; Error number &B7 |
| BBEE | JSR error_inline ; Generate 'Outside file' error |
| BBF1 | EQUS "Outside file." |
| BBFE | .loop_copy_start_addr←1← BC06 BNE |
| LDA (work_ae),y ; Load start address byte from buffer | |
| BC00 | STA osword_flag,y ; Store to osword_flag (&AA-&AD) |
| BC03 | .done_advance_start←2← BBE1 BMI← BBE7 BNE |
| INY ; Next byte | |
| BC04 | CPY #4 ; All 4 bytes copied? |
| BC06 | BNE loop_copy_start_addr ; No: copy next byte |
| BC08 | LDX #&aa ; X=zero page address to write from |
| BC0A | LDY ws_page ; Y=file handle |
| BC0C | LDA #1 ; A=1: write file pointer |
| BC0E | JSR osargs ; OSARGS: set file pointer Write sequential file pointer from zero page address X (A=1) |
| BC11 | PLA ; Restore saved command line offset |
| BC12 | TAY ; Back to Y for command line indexing |
| BC13 | LDA (os_text_ptr),y ; Load next char from command line |
| BC15 | CMP #&0d ; End of command? (CR) |
| BC17 | BNE done_parse_disp_base ; No: parse display base address |
| BC19 | LDY #1 ; Copy 2 bytes: os_text_ptr to buffer |
| BC1B | .loop_copy_osfile_ptr←1← BC21 BPL |
| LDA os_text_ptr,y ; Load os_text_ptr byte | |
| BC1E | STA (work_ae),y ; Store as filename pointer in OSFILE CB |
| BC20 | DEY ; Next byte |
| BC21 | BPL loop_copy_osfile_ptr ; Copy both low and high bytes |
| BC23 | LDA #osfile_read_catalogue_info ; Read catalogue information |
| BC25 | LDX work_ae ; X = control block low |
| BC27 | LDY addr_work ; Y = control block high |
| BC29 | JSR osfile ; OSFILE: read file info Read catalogue information (A=5) |
| BC2C | LDY #2 ; Start at OSFILE +2 (load addr byte 0) |
| BC2E | .loop_shift_osfile_data←1← BC39 BNE |
| LDA (work_ae),y ; Load from OSFILE result offset | |
| BC30 | DEY ; Y-2: destination is 2 bytes earlier |
| BC31 | DEY ; Continue decrement |
| BC32 | STA (work_ae),y ; Store to buf[Y-2] |
| BC34 | INY ; Y += 3: advance source index |
| BC35 | INY ; (continued) |
| BC36 | INY ; (continued) |
| BC37 | CPY #6 ; Copied all 4 load address bytes? |
| BC39 | BNE loop_shift_osfile_data ; No: copy next byte |
| BC3B | DEY ; Y=6 after loop exit |
| BC3C | DEY ; Y=4: check from buf[4] downward |
| BC3D | .loop_check_ff_addr←1← BC44 BPL |
| LDA (work_ae),y ; Load address byte | |
| BC3F | CMP #&ff ; Is it &FF? |
| BC41 | BNE done_add_disp_base ; No: valid load address, use it |
| BC43 | DEY ; Check next byte down |
| BC44 | BPL loop_check_ff_addr ; More bytes to check |
| BC46 | LDY #3 ; Clear all 4 bytes |
| BC48 | LDA #0 ; Zero value |
| BC4A | .loop_zero_load_addr←1← BC4D BPL |
| STA (work_ae),y ; Clear byte | |
| BC4C | DEY ; Next byte down |
| BC4D | BPL loop_zero_load_addr ; Loop for all 4 bytes |
| BC4F | BMI done_add_disp_base ; Continue to compute display address ALWAYS branch |
| BC51 | .done_parse_disp_base←1← BC17 BNE |
| JSR parse_dump_range ; Parse second hex parameter | |
| BC54 | BCC done_add_disp_base ; Valid: use as display base |
| BC56 | JSR close_ws_file ; Invalid: close file before error |
| BC59 | LDA #&fc ; Error number &FC |
| BC5B | JSR error_bad_inline ; Generate 'Bad address' error |
| BC5E | EQUS "address." |
| BC66 | .done_add_disp_base←3← BC41 BNE← BC4F BMI← BC54 BCC |
| LDY #0 ; Start from LSB | |
| BC68 | LDX #4 ; 4 bytes to add |
| BC6A | CLC ; Clear carry for addition |
| BC6B | .loop_add_disp_bytes←1← BC75 BNE |
| LDA (work_ae),y ; Load display base byte | |
| BC6D | ADC osword_flag,y ; Add start offset byte |
| BC70 | STA osword_flag,y ; Store result in osword_flag |
| BC73 | INY ; Next byte |
| BC74 | DEX ; Count down |
| BC75 | BNE loop_add_disp_bytes ; Loop for all 4 bytes |
| BC77 | LDY #&14 ; Point past end of address area |
| BC79 | LDX #3 ; Start from MSB (byte 3) |
| BC7B | .loop_store_disp_addr←1← BC81 BPL |
| DEY ; Pre-decrement Y | |
| BC7C | LDA osword_flag,x ; Load computed display address byte |
| BC7E | STA (work_ae),y ; Store to buf[&10-&13] |
| BC80 | DEX ; Next byte down |
| BC81 | BPL loop_store_disp_addr ; Loop for all 4 bytes |
| BC83 | RTS ; Return; Y=&10 (address low byte) |
Advance X by 8 via nested JSR chainCalls advance_x_by_4 (which itself JSRs inx4 then falls through to inx4), then falls through to inx4 for a total of 4+4=8 INX operations. |
|
| BC84 | .advance_x_by_8←3← 9BDE JSR← A8BB JSR← BAA2 JSR |
| JSR advance_x_by_4 ; JSR+fall-through: 8+8=16 INXs total | |
| fall through ↓ | |
Advance X by 4 via JSR and fall-throughJSRs to inx4 for 4 INX operations, then falls through to inx4 for another 4 — but when called directly (not from advance_x_by_8), the caller returns after the first inx4, yielding X+4. |
|
| BC87 | .advance_x_by_4←1← BC84 JSR |
| JSR inx4 ; JSR+fall-through: 4+4=8 INXs | |
| fall through ↓ | |
Increment X four timesFour consecutive INX instructions. Used as a building block by advance_x_by_4 and advance_x_by_8 via JSR/fall-through chaining. |
||
| ROM | Exec | |
|---|---|---|
| BC8A | .inx4←1← BC87 JSR | |
| INX ; X += 4 | ||
| BC8B | INX ; (continued) | |
| BC8C | INX ; (continued) | |
| BC8D | INX ; (continued) | |
| BC8E | RTS ; Return | |
| BC8F | EQUB &FF ; Padding; next byte is reloc_p5_src | |
| BC90 | 0500 | .tube_r2_dispatch_table←2← 0050 JMP← BEA3 STA |
| EQUW tube_osrdch ; R2 cmd 0: OSRDCH | ||
| BC92 | 0502 | EQUW tube_oscli ; R2 cmd 1: OSCLI |
| BC94 | 0504 | EQUW tube_osbyte_2param ; R2 cmd 2: OSBYTE (2-param) |
| BC96 | 0506 | EQUW tube_osbyte_long ; R2 cmd 3: OSBYTE (3-param) |
| BC98 | 0508 | EQUW tube_osword ; R2 cmd 4: OSWORD |
| BC9A | 050A | EQUW tube_osword_rdln ; R2 cmd 5: OSWORD 0 (read line) |
| BC9C | 050C | EQUW tube_osargs ; R2 cmd 6: OSARGS |
| BC9E | 050E | EQUW tube_osbget ; R2 cmd 7: OSBGET |
| BCA0 | 0510 | EQUW tube_osbput ; R2 cmd 8: OSBPUT |
| BCA2 | 0512 | EQUW tube_osfind ; R2 cmd 9: OSFIND |
| BCA4 | 0514 | EQUW tube_osfile ; R2 cmd 10: OSFILE |
| BCA6 | 0516 | EQUW tube_osgbpb ; R2 cmd 11: OSGBPB |
| ; Tube ULA control register values, indexed by transfer | ||
| ; type (0-7). Written to &FEE0 after clearing V+M with | ||
| ; &18. Bit layout: S=set/clear, T=reset regs, P=PRST, | ||
| ; V=2-byte R3, M=PNMI(R3), J=PIRQ(R4), I=PIRQ(R1), | ||
| ; Q=HIRQ(R4). Bits 1-7 select flags; bit 0 (S) is the | ||
| ; value to set or clear. | ||
| BCA8 | 0518 | .tube_ctrl_values←1← 0453 LDA |
| EQUB &86 ; Type 0: set I+J (1-byte R3, parasite to host) | ||
| BCA9 | 0519 | EQUB &88 ; Type 1: set M (1-byte R3, host to parasite) |
| BCAA | 051A | EQUB &96 ; Type 2: set V+I+J (2-byte R3, parasite to host) |
| BCAB | 051B | EQUB &98 ; Type 3: set V+M (2-byte R3, host to parasite) |
| BCAC | 051C | EQUB &18 ; Type 4: clear V+M (execute code at address) |
| BCAD | 051D | EQUB &18 ; Type 5: clear V+M (release address claim) |
| BCAE | 051E | EQUB &82 ; Type 6: set I (define event handler) |
| BCAF | 051F | EQUB &18 ; Type 7: clear V+M (transfer and release) |
| ; Tube OSBPUT handler (R2 cmd 8) | ||
| ; Reads file handle and data byte from R2, then | ||
| ; calls OSBPUT (&FFD4) to write the byte. Falls through | ||
| ; to tube_reply_ack to send &7F acknowledgement. | ||
| BCB0 | 0520 | .tube_osbput |
| JSR tube_read_r2 ; Read channel handle from R2 for BPUT | ||
| BCB3 | 0523 | TAY ; Y=channel handle for OSBPUT |
| BCB4 | 0524 | JSR tube_read_r2 ; Read data byte from R2 for OSBPUT |
| BCB7 | 0527 | .tube_poll_r1_wrch |
| JSR osbput ; Write a single byte A to an open file Y | ||
| BCBA | 052A | JMP tube_reply_ack ; Reply with &7F ack after OSBPUT |
| ; Tube OSBGET handler (R2 cmd 7) | ||
| ; Reads file handle from R2, calls OSBGET (&FFD7) | ||
| ; to read a byte, then falls through to tube_rdch_reply | ||
| ; which encodes the carry flag (error) into bit 7 and | ||
| ; sends the result byte via R2. | ||
| BCBD | 052D | .tube_osbget |
| JSR tube_read_r2 ; Read channel handle from R2 for BGET | ||
| BCC0 | 0530 | TAY ; Y=file handle |
| BCC1 | 0531 | JSR osbget ; Read a single byte from an open file Y |
| BCC4 | 0534 | JMP tube_rdch_reply ; Reply with carry+byte via RDCH protocol |
| ; Tube OSRDCH handler (R2 cmd 0) | ||
| ; Calls OSRDCH (&FFE0) to read a character from | ||
| ; the current input stream, then falls through to | ||
| ; tube_rdch_reply which encodes the carry flag (error) | ||
| ; into bit 7 and sends the result byte via R2. | ||
| BCC7 | 0537 | .tube_osrdch |
| JSR osrdch ; Read a character from the current input stream | ||
| BCCA | 053A | .tube_rdch_reply←2← 0534 JMP← 05EF JMP |
| ROR ; ROR A: encode carry (error flag) into bit 7 | ||
| BCCB | 053B | JSR tube_send_r2 ; Send carry+data byte to Tube R2 |
| BCCE | 053E | ROL ; ROL A: restore carry flag |
| BCCF | 053F | JMP tube_reply_byte ; Return via tube_reply_byte |
| ; Tube OSFIND handler (R2 cmd 9) | ||
| ; Reads open mode from R2. If zero, reads a file | ||
| ; handle and closes that file. Otherwise saves the mode, | ||
| ; reads a filename string into &0700 via tube_read_string, | ||
| ; then calls OSFIND (&FFCE) to open the file. Sends the | ||
| ; resulting file handle (or &00) via tube_reply_byte. | ||
| BCD2 | 0542 | .tube_osfind |
| JSR tube_read_r2 ; Read open mode from R2 for OSFIND | ||
| BCD5 | 0545 | BEQ tube_osfind_close ; Mode=0: close file(s) |
| BCD7 | 0547 | PHA ; Save open mode on stack |
| BCD8 | 0548 | JSR tube_read_string ; Read filename string from R2 |
| BCDB | 054B | PLA ; Restore open mode |
| BCDC | 054C | JSR osfind ; Open or close file(s) |
| BCDF | 054F | JMP tube_reply_byte ; Reply with file handle via R2 |
| BCE2 | 0552 | .tube_osfind_close←1← 0545 BEQ |
| JSR tube_read_r2 ; OSFIND close: read handle from R2 | ||
| BCE5 | 0555 | TAY ; Transfer handle to Y |
| BCE6 | 0556 | LDA #osfind_close ; A=0: close file |
| BCE8 | 0558 | JSR osfind ; Close one or all files |
| BCEB | 055B | JMP tube_reply_ack ; Reply with acknowledgement via R2 |
| ; Tube OSARGS handler (R2 cmd 6) | ||
| ; Reads file handle from R2 into Y, then reads | ||
| ; a 4-byte argument and reason code into zero page. | ||
| ; Calls OSARGS (&FFDA), sends the result A and 4-byte | ||
| ; return value via R2, then returns to the main loop. | ||
| BCEE | 055E | .tube_osargs |
| JSR tube_read_r2 ; Read file handle from R2 for OSARGS | ||
| BCF1 | 0561 | TAY ; Y=file handle for OSARGS |
| BCF2 | 0562 | .tube_read_params |
| LDX #4 ; Read 4-byte arg + reason from R2 into ZP | ||
| BCF4 | 0564 | .read_osargs_params←1← 056A BNE |
| JSR tube_read_r2 ; Read next param byte from R2 | ||
| BCF7 | 0567 | STA escape_flag,x ; Store param at ZP+X (escape_flag downward) |
| BCF9 | 0569 | DEX ; Decrement index |
| BCFA | 056A | BNE read_osargs_params ; More params: continue reading |
| BCFC | 056C | JSR tube_read_r2 ; Read OSARGS reason code from R2 |
| BCFF | 056F | JSR osargs ; Read or write a file's attributes |
| BD02 | 0572 | JSR tube_send_r2 ; Send result A via R2 |
| BD05 | 0575 | LDX #3 ; X=3: send 4 result bytes |
| BD07 | 0577 | .send_osargs_result←1← 057D BPL |
| LDA zp_ptr_lo,x ; Load result byte from zero page | ||
| BD09 | 0579 | JSR tube_send_r2 ; Send result byte via R2 |
| BD0C | 057C | DEX ; Decrement byte counter |
| BD0D | 057D | BPL send_osargs_result ; More bytes: continue sending |
| BD0F | 057F | JMP tube_main_loop ; Return to Tube main loop |
| ; Read string from Tube R2 into buffer | ||
| ; Loops reading bytes from tube_read_r2 into the | ||
| ; string buffer at &0700, storing at string_buf+Y. | ||
| ; Terminates on CR (&0D) or when Y wraps to zero | ||
| ; (256-byte overflow). Returns with X=0, Y=7 so that | ||
| ; XY = &0700, ready for OSCLI or OSFIND dispatch. | ||
| ; Called by the Tube OSCLI and OSFIND handlers. | ||
| ; On Exit: | ||
| ; X: 0 (low byte of &0700) | ||
| ; Y: 7 (high byte of &0700) | ||
| BD12 | 0582 | .tube_read_string←3← 0548 JSR← 0596 JSR← 05B3 JSR |
| LDX #0 ; X=0: initialise string buffer index | ||
| BD14 | 0584 | LDY #0 ; Y=0: initialise string offset |
| BD16 | 0586 | .strnh←1← 0591 BNE |
| JSR tube_read_r2 ; Read next string byte from R2 | ||
| BD19 | 0589 | STA string_buf,y ; Store in string buffer at &0700+Y |
| BD1C | 058C | INY ; Advance string index |
| BD1D | 058D | BEQ string_buf_done ; Buffer full (256 bytes): done |
| BD1F | 058F | CMP #&0d ; Check for CR terminator |
| BD21 | 0591 | BNE strnh ; Not CR: continue reading |
| BD23 | 0593 | .string_buf_done←1← 058D BEQ |
| LDY #7 ; Y=7: set XY=&0700 for OSCLI/OSFIND | ||
| BD25 | 0595 | RTS ; Return with XY pointing to string buffer |
| ; Tube OSCLI handler (R2 cmd 1) | ||
| ; Reads a command string from R2 into &0700 via | ||
| ; tube_read_string, then calls OSCLI (&FFF7) to execute | ||
| ; it. Falls through to tube_reply_ack to send &7F | ||
| ; acknowledgement. | ||
| BD26 | 0596 | .tube_oscli |
| JSR tube_read_string ; Read command string from R2 into &0700 | ||
| BD29 | 0599 | JSR oscli ; Execute command string via OSCLI |
| BD2C | 059C | .tube_reply_ack←3← 0489 JMP← 052A JMP← 055B JMP |
| LDA #&7f ; &7F = success acknowledgement | ||
| BD2E | 059E | .tube_reply_byte←4← 053F JMP← 054F JMP← 05A1 BVC← 067D JMP |
| BIT tube_status_register_2 ; Poll R2 status until ready | ||
| BD31 | 05A1 | BVC tube_reply_byte ; Bit 6 clear: not ready, loop |
| BD33 | 05A3 | STA tube_data_register_2 ; Write byte to R2 data register |
| BD36 | 05A6 | .mj←1← 05CF BEQ |
| JMP tube_main_loop ; Return to Tube main loop | ||
| ; Tube OSFILE handler (R2 cmd 10) | ||
| ; Reads a 16-byte control block into zero page, | ||
| ; a filename string into &0700 via tube_read_string, | ||
| ; and a reason code from R2. Calls OSFILE (&FFDD), | ||
| ; then sends the result A and updated 16-byte control | ||
| ; block back via R2. Returns to the main loop via mj. | ||
| BD39 | 05A9 | .tube_osfile |
| LDX #&10 ; Read 16-byte OSFILE control block from R2 | ||
| BD3B | 05AB | .argsw←1← 05B1 BNE |
| JSR tube_read_r2 ; Read next control block byte from R2 | ||
| BD3E | 05AE | STA zp_ptr_hi,x ; Store at ZP+X (control block) |
| BD40 | 05B0 | DEX ; Decrement index |
| BD41 | 05B1 | BNE argsw ; More bytes: continue reading |
| BD43 | 05B3 | JSR tube_read_string ; Read filename string from R2 |
| BD46 | 05B6 | STX zp_ptr_lo ; Set filename ptr low = 0 |
| BD48 | 05B8 | STY zp_ptr_hi ; Set filename ptr high = &07 |
| BD4A | 05BA | LDY #0 ; Y=0: OSFILE reason code index |
| BD4C | 05BC | JSR tube_read_r2 ; Read OSFILE reason code from R2 |
| BD4F | 05BF | JSR osfile ; Execute OSFILE |
| BD52 | 05C2 | JSR tube_send_r2 ; Send result A via R2 |
| BD55 | 05C5 | LDX #&10 ; X=&10: send 16 result bytes |
| BD57 | 05C7 | .send_osfile_ctrl_blk←1← 05CD BNE |
| LDA zp_ptr_hi,x ; Load control block byte | ||
| BD59 | 05C9 | JSR tube_send_r2 ; Send control block byte via R2 |
| BD5C | 05CC | DEX ; Decrement byte counter |
| BD5D | 05CD | BNE send_osfile_ctrl_blk ; More bytes: continue sending |
| BD5F | 05CF | BEQ mj ; ALWAYS branch |
| ; Tube OSGBPB handler (R2 cmd 11) | ||
| ; Reads a 13-byte control block and reason code | ||
| ; from R2 into zero page. Calls OSGBPB (&FFD1), then | ||
| ; sends 12 result bytes and the carry+result byte | ||
| ; (via tube_rdch_reply) back via R2. | ||
| BD61 | 05D1 | .tube_osgbpb |
| LDX #&0d ; X=&0D: read 13-byte OSGBPB ctrl block | ||
| BD63 | 05D3 | .read_osgbpb_ctrl_blk←1← 05D9 BNE |
| JSR tube_read_r2 ; Read next control block byte from R2 | ||
| BD66 | 05D6 | STA escape_flag,x ; Store at ZP+X (escape_flag downward) |
| BD68 | 05D8 | DEX ; Decrement index |
| BD69 | 05D9 | BNE read_osgbpb_ctrl_blk ; More bytes: continue reading |
| BD6B | 05DB | JSR tube_read_r2 ; Read OSGBPB reason code from R2 |
| BD6E | 05DE | LDY #0 ; Y=0: OSGBPB direction/count |
| BD70 | 05E0 | JSR osgbpb ; Read or write multiple bytes to an open file |
| BD73 | 05E3 | PHA ; Save result A on stack |
| BD74 | 05E4 | LDX #&0c ; X=&0C: send 12 result bytes |
| BD76 | 05E6 | .send_osgbpb_result←1← 05EC BPL |
| LDA zp_ptr_lo,x ; Load result byte from zero page | ||
| BD78 | 05E8 | JSR tube_send_r2 ; Send result byte via R2 |
| BD7B | 05EB | DEX ; Decrement byte counter |
| BD7C | 05EC | BPL send_osgbpb_result ; More bytes: continue sending |
| BD7E | 05EE | PLA ; Recover completion status from stack |
| BD7F | 05EF | JMP tube_rdch_reply ; Reply with RDCH-style result |
| ; Tube OSBYTE 2-param handler (R2 cmd 2) | ||
| ; Reads X and A from R2, calls OSBYTE (&FFF4) | ||
| ; with Y=0, then sends the result X via | ||
| ; tube_reply_byte. Used for OSBYTE calls that take | ||
| ; only A and X parameters. | ||
| BD82 | 05F2 | .tube_osbyte_2param |
| JSR tube_read_r2 ; Read X parameter from R2 | ||
| BD85 | 05F5 | TAX ; Transfer to X register |
| BD86 | 05F6 | JSR tube_read_r2 ; Read A (OSBYTE function code) from R2 |
| BD89 | 05F9 | JSR osbyte ; Execute OSBYTE A,X |
| BD8C | 05FC | .tube_poll_r2_result←2← 05FF ref← 0625 BVS |
| BIT tube_status_register_2 ; Poll R2 status for result send | ||
| BD8F | 05FF | EQUB &50 ; BVC: page 5/6 boundary straddle |
| BD90 | 0600 | .tube_osbyte_reply_block←1← BEA9 STA |
| EQUB &FB ; Send carry+status to co-processor via R2 | ||
| BD91 | 0601 | STX tube_data_register_2 ; Send X result for 2-param OSBYTE |
| BD94 | 0604 | .bytex←1← 0617 BEQ |
| JMP tube_main_loop ; Return to main event loop | ||
| ; Tube OSBYTE 3-param handler (R2 cmd 3) | ||
| ; Reads X, Y, and A from R2, calls OSBYTE | ||
| ; (&FFF4), then sends carry+Y and X as result bytes | ||
| ; via R2. Used for OSBYTE calls needing all three | ||
| ; parameters and returning both X and Y results. | ||
| BD97 | 0607 | .tube_osbyte_long |
| JSR tube_read_r2 ; Read X, Y, A from R2 for 3-param OSBYTE | ||
| BD9A | 060A | TAX ; Save in X |
| BD9B | 060B | JSR tube_read_r2 ; Read Y parameter from co-processor |
| BD9E | 060E | TAY ; Save in Y |
| BD9F | 060F | JSR tube_read_r2 ; Read A (OSBYTE function code) |
| BDA2 | 0612 | JSR osbyte ; Execute OSBYTE A,X,Y |
| BDA5 | 0615 | EOR #&9d ; Test for OSBYTE &9D (fast Tube BPUT) |
| BDA7 | 0617 | BEQ bytex ; OSBYTE &9D (fast Tube BPUT): no result needed |
| BDA9 | 0619 | ROR ; Encode carry (error flag) into bit 7 |
| BDAA | 061A | JSR tube_send_r2 ; Send carry+status byte via R2 |
| BDAD | 061D | .tube_osbyte_send_y←1← 0620 BVC |
| BIT tube_status_register_2 ; Poll R2 status for ready | ||
| BDB0 | 0620 | BVC tube_osbyte_send_y ; Not ready: keep polling |
| BDB2 | 0622 | STY tube_data_register_2 ; Send Y result, then fall through to send X |
| BDB5 | 0625 | .tube_osbyte_short |
| BVS tube_poll_r2_result ; BVS always: jump to send X via R2 | ||
| ; Tube OSWORD handler (R2 cmd 4) | ||
| ; Reads OSWORD number A and in-length from R2, | ||
| ; then reads the parameter block into &0128. Calls | ||
| ; OSWORD (&FFF1), then sends the out-length result | ||
| ; bytes from the parameter block back via R2. | ||
| ; Returns to the main loop via tube_return_main. | ||
| BDB7 | 0627 | .tube_osword |
| JSR tube_read_r2 ; Overlapping entry: &20 = JSR c06c5 (OSWORD) | ||
| BDBA | 062A | TAY ; Save OSWORD number in Y |
| BDBB | 062B | .tube_osword_read←1← 062E BPL |
| BIT tube_status_register_2 ; Poll R2 status for data ready | ||
| BDBE | 062E | BPL tube_osword_read ; Not ready: keep polling |
| BDC0 | 0630 | .tube_osbyte_send_x |
| LDX tube_data_register_2 ; Read param block length from R2 | ||
| BDC3 | 0633 | DEX ; DEX: length 0 means no params to read |
| BDC4 | 0634 | BMI skip_param_read ; No params (length=0): skip read loop |
| BDC6 | 0636 | .tube_osword_read_lp←2← 0639 BPL← 0642 BPL |
| BIT tube_status_register_2 ; Poll R2 status for data ready | ||
| BDC9 | 0639 | BPL tube_osword_read_lp ; Not ready: keep polling |
| BDCB | 063B | LDA tube_data_register_2 ; Read param byte from R2 |
| BDCE | 063E | STA tube_osword_pb,x ; Store param bytes into block at &0128 |
| BDD1 | 0641 | DEX ; Next param byte (descending) |
| BDD2 | 0642 | BPL tube_osword_read_lp ; Loop until all params read |
| BDD4 | 0644 | TYA ; Restore OSWORD number from Y |
| BDD5 | 0645 | .skip_param_read←1← 0634 BMI |
| LDX #<(tube_osword_pb) ; XY=&0128: param block address for OSWORD | ||
| BDD7 | 0647 | LDY #>(tube_osword_pb) ; Y=&01: param block at &0128 |
| BDD9 | 0649 | JSR osword ; Execute OSWORD with XY=&0128 |
| BDDC | 064C | .poll_r2_osword_result←1← 064F BPL |
| BIT tube_status_register_2 ; Poll R2 status for ready | ||
| BDDF | 064F | BPL poll_r2_osword_result ; Not ready: keep polling |
| BDE1 | 0651 | LDX tube_data_register_2 ; Read result block length from R2 |
| BDE4 | 0654 | DEX ; Decrement result byte counter |
| BDE5 | 0655 | BMI tube_return_main ; No results to send: return to main loop |
| BDE7 | 0657 | .tube_osword_write←1← 0663 BPL |
| LDY tube_osword_pb,x ; Send result block bytes from &0128 via R2 | ||
| BDEA | 065A | .tube_osword_write_lp←1← 065D BVC |
| BIT tube_status_register_2 ; Poll R2 status for ready | ||
| BDED | 065D | BVC tube_osword_write_lp ; Not ready: keep polling |
| BDEF | 065F | STY tube_data_register_2 ; Send result byte via R2 |
| BDF2 | 0662 | DEX ; Next result byte (descending) |
| BDF3 | 0663 | BPL tube_osword_write ; Loop until all results sent |
| BDF5 | 0665 | .tube_return_main←1← 0655 BMI |
| JMP tube_main_loop ; Return to main event loop | ||
| ; Tube OSWORD 0 handler (R2 cmd 5) | ||
| ; Handles OSWORD 0 (read line) specially. Reads | ||
| ; 4 parameter bytes from R2 into &0128 (max length, | ||
| ; min char, max char, flags). Calls OSWORD 0 (&FFF1) | ||
| ; to read a line, then sends &7F+CR or the input line | ||
| ; byte-by-byte via R2, followed by &80 (error/escape) | ||
| ; or &7F (success). | ||
| BDF8 | 0668 | .tube_osword_rdln |
| LDX #4 ; Read 5-byte OSWORD 0 control block from R2 | ||
| BDFA | 066A | .read_rdln_ctrl_block←1← 0670 BPL |
| JSR tube_read_r2 ; Read control block byte from R2 | ||
| BDFD | 066D | STA zp_ptr_lo,x ; Store in zero page params |
| BDFF | 066F | DEX ; Next byte (descending) |
| BE00 | 0670 | BPL read_rdln_ctrl_block ; Loop until all 5 bytes read |
| BE02 | 0672 | INX ; X=0 after loop, A=0 for OSWORD 0 (read line) |
| BE03 | 0673 | LDY #0 ; Y=0 for OSWORD 0 |
| BE05 | 0675 | TXA ; A=0: OSWORD 0 (read line) |
| BE06 | 0676 | JSR osword ; Read input line from keyboard |
| BE09 | 0679 | BCC tube_rdln_send_line ; C=0: line read OK; C=1: escape pressed |
| BE0B | 067B | LDA #&ff ; &FF = escape/error signal to co-processor |
| BE0D | 067D | JMP tube_reply_byte ; Escape: send &FF error to co-processor |
| BE10 | 0680 | .tube_rdln_send_line←1← 0679 BCC |
| LDX #0 ; X=0: start of input buffer at &0700 | ||
| BE12 | 0682 | LDA #&7f ; &7F = line read successfully |
| BE14 | 0684 | JSR tube_send_r2 ; Send &7F (success) to co-processor |
| BE17 | 0687 | .tube_rdln_send_loop←1← 0690 BNE |
| LDA string_buf,x ; Load char from input buffer | ||
| BE1A | 068A | .tube_rdln_send_byte |
| JSR tube_send_r2 ; Send char to co-processor | ||
| BE1D | 068D | INX ; Next character |
| BE1E | 068E | CMP #&0d ; Check for CR terminator |
| BE20 | 0690 | BNE tube_rdln_send_loop ; Loop until CR terminator sent |
| BE22 | 0692 | JMP tube_main_loop ; Return to main event loop |
| ; Send byte to Tube data register R2 | ||
| ; Polls Tube status register 2 until bit 6 (TDRA) | ||
| ; is set, then writes A to the data register. Uses a | ||
| ; tight BIT/BVC polling loop. Called by 12 sites | ||
| ; across the Tube host code for all R2 data | ||
| ; transmission: command responses, file data, OSBYTE | ||
| ; results, and control block bytes. | ||
| ; On Entry: | ||
| ; A: byte to send | ||
| ; On Exit: | ||
| ; A: preserved (value written) | ||
| BE25 | 0695 | .tube_send_r2←14← 0020 JSR← 0026 JSR← 002C JSR← 0474 JSR← 053B JSR← 0572 JSR← 0579 JSR← 05C2 JSR← 05C9 JSR← 05E8 JSR← 061A JSR← 0684 JSR← 068A JSR← 0698 BVC |
| BIT tube_status_register_2 ; Poll R2 status (bit 6 = ready) | ||
| BE28 | 0698 | BVC tube_send_r2 ; Not ready: keep polling |
| BE2A | 069A | STA tube_data_register_2 ; Write A to Tube R2 data register |
| BE2D | 069D | RTS ; Return to caller |
| ; Send byte to Tube data register R4 | ||
| ; Polls Tube status register 4 until bit 6 is set, | ||
| ; then writes A to the data register. Uses a tight | ||
| ; BIT/BVC polling loop. R4 is the command/control | ||
| ; channel used for address claims (ADRR), data transfer | ||
| ; setup (SENDW), and release commands. Called by 7 | ||
| ; sites, primarily during tube_release_claim and | ||
| ; tube_transfer_setup sequences. | ||
| ; On Entry: | ||
| ; A: byte to send | ||
| ; On Exit: | ||
| ; A: preserved (value written) | ||
| BE2E | 069E | .tube_send_r4←8← 0018 JSR← 0418 JSR← 041D JSR← 043B JSR← 0443 JSR← 0448 JSR← 0463 JSR← 06A1 BVC |
| BIT tube_status_register_4_and_cpu_control ; Poll R4 status (bit 6 = ready) | ||
| BE31 | 06A1 | BVC tube_send_r4 ; Not ready: keep polling |
| BE33 | 06A3 | STA tube_data_register_4 ; Write A to Tube R4 data register |
| BE36 | 06A6 | RTS ; Return to caller |
| BE37 | 06A7 | .tube_escape_check←1← 0403 JMP |
| LDA escape_flag ; Check OS escape flag at &FF | ||
| BE39 | 06A9 | SEC ; SEC+ROR: put bit 7 of &FF into carry+bit 7 |
| BE3A | 06AA | ROR ; ROR: shift escape bit 7 to carry |
| BE3B | 06AB | BMI tube_send_r1 ; Escape set: forward to co-processor via R1 |
| BE3D | 06AD | .tube_event_handler |
| PHA ; EVNTV: forward event A, Y, X to co-processor | ||
| BE3E | 06AE | LDA #0 ; Send &00 prefix (event notification) |
| BE40 | 06B0 | JSR tube_send_r1 ; Send zero prefix via R1 |
| BE43 | 06B3 | TYA ; Y value for event |
| BE44 | 06B4 | JSR tube_send_r1 ; Send Y via R1 |
| BE47 | 06B7 | TXA ; X value for event |
| BE48 | 06B8 | JSR tube_send_r1 ; Send X via R1 |
| BE4B | 06BB | PLA ; Restore A (event type) |
| ; Send byte to Tube data register R1 | ||
| ; Polls Tube status register 1 until bit 6 is set, | ||
| ; then writes A to the data register. Uses a tight | ||
| ; BIT/BVC polling loop. R1 is used for asynchronous | ||
| ; event and escape notification to the co-processor. | ||
| ; Called by tube_event_handler to forward event type, | ||
| ; Y, and X parameters, and reached via BMI from | ||
| ; tube_escape_check when the escape flag is set. | ||
| ; On Entry: | ||
| ; A: byte to send | ||
| ; On Exit: | ||
| ; A: preserved (value written) | ||
| BE4C | 06BC | .tube_send_r1←5← 06AB BMI← 06B0 JSR← 06B4 JSR← 06B8 JSR← 06BF BVC |
| BIT tube_status_1_and_tube_control ; Poll R1 status (bit 6 = ready) | ||
| BE4F | 06BF | BVC tube_send_r1 ; Not ready: keep polling |
| BE51 | 06C1 | STA tube_data_register_1 ; Write A to Tube R1 data register |
| BE54 | 06C4 | RTS ; Return to caller |
| ; Read a byte from Tube data register R2 | ||
| ; Polls Tube status register 2 until bit 7 (RDA) | ||
| ; is set, then loads and returns the byte from Tube | ||
| ; data register 2. Uses a BIT/BPL polling loop (testing | ||
| ; the N flag). R2 is the primary data channel from the | ||
| ; co-processor. Called by 14 sites across the Tube host | ||
| ; code for command dispatch, OSFILE/OSGBPB control block | ||
| ; reads, string reads, and OSBYTE parameter reception. | ||
| ; On Exit: | ||
| ; A: byte read from R2 | ||
| BE55 | 06C5 | .tube_read_r2←21← 0520 JSR← 0524 JSR← 052D JSR← 0542 JSR← 0552 JSR← 055E JSR← 0564 JSR← 056C JSR← 0586 JSR← 05AB JSR← 05BC JSR← 05D3 JSR← 05DB JSR← 05F2 JSR← 05F6 JSR← 0607 JSR← 060B JSR← 060F JSR← 0627 JSR← 066A JSR← 06C8 BPL |
| BIT tube_status_register_2 ; Poll R2 status (bit 7 = ready) | ||
| BE58 | 06C8 | BPL tube_read_r2 ; Not ready: keep polling |
| BE5A | 06CA | LDA tube_data_register_2 ; Read data byte from R2 |
| BE5D | 06CD | RTS ; Return with byte in A |
| BE5E | 06CE | CMP #&fe ; Is byte &FE (VDU stream start)? |
| BE60 | 06D0 | BCC tube_vdu_normal_byte ; Below &FE: normal byte |
| BE62 | 06D2 | BNE setup_tube_vectors ; &FF: set up event/break vectors |
| BE64 | 06D4 | CPY #0 ; &FE: check Y parameter |
| BE66 | 06D6 | BEQ tube_vdu_normal_byte ; Y=0: treat as normal byte |
| BE68 | 06D8 | LDX #6 ; X=6: six extra pages |
| BE6A | 06DA | LDA #osbyte_explode_chars ; OSBYTE &14: explode char defs |
| BE6C | 06DC | JSR osbyte ; Explode character definition RAM (six extra pages), can redefine all characters 32-255 (X=6) |
| BE6F | 06DF | .loop_poll_r1_vdu←1← 06E2 BPL |
| BIT tube_status_1_and_tube_control ; Poll R1 status (bit 6 = ready) | ||
| BE72 | 06E2 | BPL loop_poll_r1_vdu ; Not ready: keep polling |
| BE74 | 06E4 | LDA tube_data_register_1 ; Read byte from Tube R1 |
| BE77 | 06E7 | BEQ tube_vdu_stream_end ; Zero: end of VDU stream |
| BE79 | 06E9 | JSR oswrch ; Write character |
| BE7C | 06EC | .svc_11_nmi_claim |
| JMP loop_poll_r1_vdu_rom ; Loop back to read next R1 byte | ||
| BE7F | 06EF | .setup_tube_vectors←1← 06D2 BNE |
| LDA #&ad ; EVNTV low byte (&AD) | ||
| BE81 | 06F1 | STA evntv ; Store in EVNTV vector low |
| BE84 | 06F4 | LDA #6 ; EVNTV high byte (page 6) |
| BE86 | 06F6 | STA evntv+1 ; Store in EVNTV vector high |
| BE89 | 06F9 | LDA #&16 ; BRKV low byte (&16) |
| BE8B | 06FB | STA brkv ; Store in BRKV vector |
| BE8E | 06FE | LDA #0 ; A=0 |
| ; Resume normal ROM address space | ||
| ; The preceding 512 bytes are the source data for | ||
| ; two relocated code blocks (see move() calls): | ||
| ; page 5 source -> &0500-&05FF (Tube host code) | ||
| ; page 6 source -> &0600-&06FF (Econet handlers) | ||
| ; py8dis assembles those blocks at their runtime | ||
| ; addresses (&0500/&0600) via org directives. This | ||
| ; org restores the origin to the actual ROM address | ||
| ; for the remaining non-relocated code. | ||
| BE90 | STA brkv+1 ; Store BRK vector high byte | |
| BE93 | LDA #&8e ; A=&8E: Tube control register value | |
| BE95 | STA tube_status_1_and_tube_control ; Write Tube control register | |
| BE98 | LDY #0 ; Y=0: copy 256 bytes per page | |
| BE9A | .loop_copy_reloc_pages←1← BEAD BNE | |
| LDA reloc_p4_src,y ; Load page 4 source byte | ||
| BE9D | STA tube_page4_vectors,y ; Store to page 4 destination | |
| BEA0 | LDA reloc_p5_src,y ; Load page 5 source byte | |
| BEA3 | STA tube_r2_dispatch_table,y ; Store to page 5 destination | |
| BEA6 | LDA reloc_p6_src,y ; Load page 6 source byte | |
| BEA9 | STA tube_osbyte_reply_block,y ; Store to page 6 destination | |
| BEAC | DEY ; Decrement byte counter | |
| BEAD | BNE loop_copy_reloc_pages ; Non-zero: continue copying | |
| BEAF | JSR clear_tube_claim ; Clear tube claim state | |
| BEB2 | LDX #&41 ; X=&41: copy 66 bytes of ZP workspace | |
| BEB4 | .loop_copy_zp_workspace←1← BEBA BPL | |
| LDA reloc_zp_src,x ; Load ZP source byte from ROM | ||
| BEB7 | STA nmi_workspace_start,x ; Store to NMI workspace at &16+X | |
| BEB9 | DEX ; Decrement byte counter | |
| BEBA | BPL loop_copy_zp_workspace ; More bytes: continue copying | |
| BEBC | LDA #0 ; A=0: return success | |
| BEBE | RTS ; Return to caller | |
| BEBF | 0016 | .nmi_workspace_start←1← BEB7 STA |
| LDA #&ff ; A=&FF: signal error to co-processor via R4 | ||
| BEC1 | 0018 | JSR tube_send_r4 ; Send &FF error signal to Tube R4 |
| BEC4 | 001B | LDA tube_data_register_2 ; Flush any pending R2 byte |
| BEC7 | 001E | LDA #0 ; A=0: send zero prefix to R2 |
| BEC9 | 0020 | .tube_send_zero_r2 |
| JSR tube_send_r2 ; Send zero prefix byte via R2 | ||
| BECC | 0023 | TAY ; Y=0: start of error block at (&FD) |
| BECD | 0024 | LDA (brk_ptr),y ; Load error number from (&FD),0 |
| BECF | 0026 | .tube_send_error_num |
| JSR tube_send_r2 ; Send error number via R2 | ||
| BED2 | 0029 | .tube_brk_send_loop←1← 0030 BNE |
| INY ; Advance to next error string byte | ||
| BED3 | 002A | .tube_send_error_byte |
| LDA (brk_ptr),y ; Load next error string byte | ||
| BED5 | 002C | JSR tube_send_r2 ; Send error string byte via R2 |
| BED8 | 002F | TAX ; Zero byte = end of error string |
| BED9 | 0030 | BNE tube_brk_send_loop ; Loop until zero terminator sent |
| BEDB | 0032 | .tube_reset_stack←1← 0477 JMP |
| LDX #&ff ; Reset stack pointer to top | ||
| BEDD | 0034 | TXS ; TXS: set stack pointer from X |
| BEDE | 0035 | CLI ; Enable interrupts for main loop |
| BEDF | 0036 | .tube_main_loop←6← 0044 BPL← 057F JMP← 05A6 JMP← 0604 JMP← 0665 JMP← 0692 JMP |
| BIT tube_status_1_and_tube_control ; BIT R1 status: check WRCH request | ||
| BEE2 | 0039 | BPL tube_poll_r2 ; R1 not ready: check R2 instead |
| BEE4 | 003B | .tube_handle_wrch←1← 0049 BMI |
| LDA tube_data_register_1 ; Read character from Tube R1 data | ||
| BEE7 | 003E | JSR oswrch ; Write character |
| BEEA | 0041 | .tube_poll_r2←1← 0039 BPL |
| BIT tube_status_register_2 ; BIT R2 status: check command byte | ||
| BEED | 0044 | BPL tube_main_loop ; R2 not ready: loop back to R1 check |
| BEEF | 0046 | BIT tube_status_1_and_tube_control ; Re-check R1: WRCH has priority over R2 |
| BEF2 | 0049 | BMI tube_handle_wrch ; R1 ready: handle WRCH first |
| BEF4 | 004B | LDX tube_data_register_2 ; Read command byte from Tube R2 data |
| BEF7 | 004E | STX tube_cmd_lo ; Self-modify JMP low byte for dispatch |
| BEF9 | 0050 | .tube_dispatch_cmd |
| JMP (tube_r2_dispatch_table) ; Dispatch to handler via indirect JMP | ||
| BEFC | 0053 | .tube_transfer_addr←2← 04DE STY← 04EE STA |
| EQUB &00 ; Tube transfer address low byte | ||
| BEFD | 0054 | .tube_xfer_page←3← 04B6 INC← 04D4 STA← 04F3 STA |
| EQUB &80 ; Tube transfer page (default &80) | ||
| BEFE | 0055 | .tube_xfer_addr_2←2← 04BA INC← 04FD STY |
| EQUB &00 ; Tube transfer address byte 2 | ||
| BEFF | 0056 | .tube_xfer_addr_3←2← 04BE INC← 04FB STA |
| EQUB &00 ; Tube transfer address byte 3 | ||
| BF00 | 0400 | .tube_page4_vectors←1← BE9D STA |
| JMP tube_begin ; JMP to BEGIN startup entry | ||
| BF03 | 0403 | JMP tube_escape_check ; JMP to tube_escape_check (&06A7) |
| ; Tube address/data dispatch | ||
| ; Called by 10 sites across the Tube host and Econet | ||
| ; code. Routes requests based on the value of A: | ||
| ; A < &80: data transfer setup (SENDW) at &0435 | ||
| ; &80 <= A < &C0: release -- maps A via ORA #&40 | ||
| ; and compares with tube_claimed_id; if we own | ||
| ; this address, falls through to tube_release_claim | ||
| ; A >= &C0: external address claim from another host | ||
| ; Falls through to tube_release_claim when releasing our | ||
| ; current claim. | ||
| ; On Entry: | ||
| ; A: request type (<&80 data, &80-&BF release, &C0+ claim) | ||
| ; X: transfer address low (data transfer only) | ||
| ; Y: transfer address high (data transfer only) | ||
| BF06 | 0406 | .tube_addr_data_dispatch←10← 049A JSR← 04CF JMP← 8371 JSR← 8445 JSR← 8929 JSR← 8931 JSR← 9FDA JSR← 9FF1 JSR← A05D JSR← A2D4 JMP |
| CMP #&80 ; A>=&80: address claim; A<&80: data transfer | ||
| BF08 | 0408 | BCC tube_transfer_setup ; A<&80: data transfer setup (SENDW) |
| BF0A | 040A | CMP #&c0 ; A>=&C0: new address claim from another host |
| BF0C | 040C | BCS addr_claim_external ; C=1: external claim, check ownership |
| BF0E | 040E | ORA #&40 ; Map &80-&BF range to &C0-&FF for comparison |
| BF10 | 0410 | CMP tube_claimed_id ; Is this for our currently-claimed address? |
| BF12 | 0412 | BNE return_tube_init ; Not our address: return |
| ; Release Tube address claim via R4 command 5 | ||
| ; Saves interrupt state (PHP/SEI) to protect the R4 | ||
| ; protocol sequence, sends R4 command 5 (release) followed | ||
| ; by the currently-claimed address from tube_claimed_id | ||
| ; (&15), then restores interrupts (PLP). Falls through to | ||
| ; clear_tube_claim to reset the claimed-address state to | ||
| ; the &80 sentinel. | ||
| BF14 | 0414 | .tube_release_claim←1← 0471 JSR |
| PHP ; PHP: save interrupt state for release | ||
| BF15 | 0415 | SEI ; SEI: disable interrupts during R4 protocol |
| BF16 | 0416 | LDA #5 ; R4 cmd 5: release our address claim |
| BF18 | 0418 | JSR tube_send_r4 ; Send release command to co-processor |
| BF1B | 041B | LDA tube_claimed_id ; Load our currently-claimed address |
| BF1D | 041D | JSR tube_send_r4 ; Send our address as release parameter |
| BF20 | 0420 | PLP ; Restore interrupt state |
| ; Reset Tube address claim state | ||
| ; Stores &80 into both tube_claimed_id (&15) and | ||
| ; tube_claim_flag (&14). The &80 sentinel indicates no | ||
| ; address is currently claimed and no claim is in | ||
| ; progress. Called after tube_release_claim (via | ||
| ; fall-through) and during initial workspace setup. | ||
| BF21 | 0421 | .clear_tube_claim←1← BEAF JSR |
| LDA #&80 ; &80 sentinel: clear address claim | ||
| BF23 | 0423 | STA tube_claimed_id ; &80 sentinel = no address currently claimed |
| BF25 | 0425 | STA tube_claim_flag ; Store to claim-in-progress flag |
| BF27 | 0427 | RTS ; Return from tube_post_init |
| BF28 | 0428 | .addr_claim_external←1← 040C BCS |
| ASL tube_claim_flag ; Another host claiming; check if we're owner | ||
| BF2A | 042A | BCS accept_new_claim ; C=1: we have an active claim |
| BF2C | 042C | CMP tube_claimed_id ; Compare with our claimed address |
| BF2E | 042E | BEQ return_tube_init ; Match: return (we already have it) |
| BF30 | 0430 | CLC ; Not ours: CLC = we don't own this address |
| BF31 | 0431 | RTS ; Return with C=0 (claim denied) |
| BF32 | 0432 | .accept_new_claim←1← 042A BCS |
| STA tube_claimed_id ; Accept new claim: update our address | ||
| BF34 | 0434 | .return_tube_init←2← 0412 BNE← 042E BEQ |
| RTS ; Return with address updated | ||
| BF35 | 0435 | .tube_transfer_setup←1← 0408 BCC |
| PHP ; PHP: save interrupt state | ||
| BF36 | 0436 | SEI ; SEI: disable interrupts for R4 protocol |
| BF37 | 0437 | .setup_data_transfer |
| STY tube_data_ptr_hi ; Save 16-bit transfer address from (X,Y) | ||
| BF39 | 0439 | STX tube_data_ptr ; Store address pointer low byte |
| BF3B | 043B | JSR tube_send_r4 ; Send transfer type byte to co-processor |
| BF3E | 043E | TAX ; X = transfer type for table lookup |
| BF3F | 043F | LDY #3 ; Y=3: send 4 bytes (address + claimed addr) |
| BF41 | 0441 | LDA tube_claimed_id ; Send our claimed address + 4-byte xfer addr |
| BF43 | 0443 | JSR tube_send_r4 ; Send transfer address byte |
| BF46 | 0446 | .send_xfer_addr_bytes←1← 044C BPL |
| LDA (tube_data_ptr),y ; Load transfer address byte from (X,Y) | ||
| BF48 | 0448 | JSR tube_send_r4 ; Send address byte to co-processor via R4 |
| BF4B | 044B | DEY ; Previous byte (big-endian: 3,2,1,0) |
| BF4C | 044C | BPL send_xfer_addr_bytes ; Loop for all 4 address bytes |
| BF4E | 044E | LDY #&18 ; Y=&18: enable Tube control register |
| BF50 | 0450 | STY tube_status_1_and_tube_control ; Enable Tube interrupt generation |
| BF53 | 0453 | LDA tube_ctrl_values,x ; Look up Tube control bits for this xfer type |
| BF56 | 0456 | STA tube_status_1_and_tube_control ; Apply transfer- specific control bits |
| BF59 | 0459 | LSR ; LSR: check bit 2 (2-byte flush needed?) |
| BF5A | 045A | LSR ; LSR: shift bit 2 to carry |
| BF5B | 045B | BCC skip_r3_flush ; C=0: no flush needed, skip R3 reads |
| BF5D | 045D | BIT tube_data_register_3 ; Dummy R3 reads: flush for 2-byte transfers |
| BF60 | 0460 | BIT tube_data_register_3 ; Second dummy read to flush R3 FIFO |
| BF63 | 0463 | .skip_r3_flush←1← 045B BCC |
| JSR tube_send_r4 ; Trigger co-processor ack via R4 | ||
| BF66 | 0466 | .poll_r4_copro_ack←1← 0469 BVC |
| BIT tube_status_register_4_and_cpu_control ; Poll R4 status for co- processo r response | ||
| BF69 | 0469 | BVC poll_r4_copro_ack ; Bit 6 clear: not ready, keep polling |
| BF6B | 046B | BCS copro_ack_nmi_check ; R4 bit 7: co-processor acknowledged transfer |
| BF6D | 046D | CPX #4 ; Type 4 = SENDW (host-to-parasite word xfer) |
| BF6F | 046F | BNE skip_nmi_release ; Not SENDW type: skip release path |
| BF71 | 0471 | .tube_sendw_complete←1← 0496 BEQ |
| JSR tube_release_claim ; SENDW complete: release, sync, restart | ||
| BF74 | 0474 | JSR tube_send_r2 ; Sync via R2 send |
| BF77 | 0477 | JMP tube_reset_stack ; Restart Tube main loop |
| BF7A | 047A | .copro_ack_nmi_check←1← 046B BCS |
| LSR ; LSR: check bit 0 (NMI used?) | ||
| BF7B | 047B | BCC skip_nmi_release ; C=0: NMI not used, skip NMI release |
| BF7D | 047D | LDY #&88 ; Release Tube NMI (transfer used interrupts) |
| BF7F | 047F | STY tube_status_1_and_tube_control ; Write &88 to Tube control to release NMI |
| BF82 | 0482 | .skip_nmi_release←2← 046F BNE← 047B BCC |
| PLP ; Restore interrupt state | ||
| BF83 | 0483 | .return_tube_xfer |
| RTS ; Return from transfer setup | ||
| ; Tube host startup entry (BEGIN) | ||
| ; Entry point via JMP from &0400. Enables interrupts, ; checks | ||
| ; break type via OSBYTE &FD: soft break re-initialises ; Tube and | ||
| ; restarts, hard break claims address &FF. Sends ROM ; contents | ||
| ; to co-processor page by page via SENDW, then claims the ; final | ||
| ; transfer address. | ||
| BF84 | 0484 | .tube_begin←1← 0400 JMP |
| CLI ; BEGIN: enable interrupts for Tube host code | ||
| BF85 | 0485 | BCS claim_addr_ff ; C=1: hard break, claim addr &FF |
| BF87 | 0487 | BNE check_break_type ; C=0, A!=0: re-init path |
| BF89 | 0489 | JMP tube_reply_ack ; Z=1 from C=0 path: just acknowledge |
| BF8C | 048C | .check_break_type←1← 0487 BNE |
| LDX #0 ; X=0 for OSBYTE | ||
| BF8E | 048E | LDY #&ff ; Y=&FF for OSBYTE |
| BF90 | 0490 | LDA #osbyte_read_write_last_break_type ; OSBYTE &FD: what type of reset was this? |
| BF92 | 0492 | JSR osbyte ; Read type of last reset |
| BF95 | 0495 | TXA ; X=value of type of last reset |
| BF96 | 0496 | BEQ tube_sendw_complete ; Soft break (X=0): re-init Tube and restart |
| BF98 | 0498 | .claim_addr_ff←2← 0485 BCS← 049D BCC |
| LDA #&ff ; Claim address &FF (startup = highest prio) | ||
| BF9A | 049A | JSR tube_addr_data_dispatch ; Request address claim from Tube system |
| BF9D | 049D | BCC claim_addr_ff ; C=0: claim failed, retry |
| BF9F | 049F | JSR tube_init_reloc ; Init reloc pointers from ROM header |
| BFA2 | 04A2 | .next_rom_page←1← 04C4 BVC |
| LDA #7 ; R4 cmd 7: SENDW to send ROM to parasite | ||
| BFA4 | 04A4 | JSR tube_claim_default ; Set up Tube for SENDW transfer |
| BFA7 | 04A7 | LDY #0 ; Y=0: start at beginning of page |
| BFA9 | 04A9 | STY zp_ptr_lo ; Store to zero page pointer low byte |
| BFAB | 04AB | .send_rom_page_bytes←1← 04B4 BNE |
| LDA (zp_ptr_lo),y ; Send 256-byte page via R3, byte at a time | ||
| BFAD | 04AD | STA tube_data_register_3 ; Write byte to Tube R3 data register |
| BFB0 | 04B0 | NOP ; Timing delay: Tube data register needs NOPs |
| BFB1 | 04B1 | NOP ; NOP delay (2) |
| BFB2 | 04B2 | NOP ; NOP delay (3) |
| BFB3 | 04B3 | INY ; Next byte in page |
| BFB4 | 04B4 | BNE send_rom_page_bytes ; Loop for all 256 bytes |
| BFB6 | 04B6 | INC tube_xfer_page ; Increment 24-bit destination addr |
| BFB8 | 04B8 | BNE skip_addr_carry ; No carry: skip higher bytes |
| BFBA | 04BA | INC tube_xfer_addr_2 ; Carry into second byte |
| BFBC | 04BC | BNE skip_addr_carry ; No carry: skip third byte |
| BFBE | 04BE | INC tube_xfer_addr_3 ; Carry into third byte |
| BFC0 | 04C0 | .skip_addr_carry←2← 04B8 BNE← 04BC BNE |
| INC zp_ptr_hi ; Increment page counter | ||
| BFC2 | 04C2 | BIT zp_ptr_hi ; Bit 6 set = all pages transferred |
| BFC4 | 04C4 | BVC next_rom_page ; More pages: loop back to SENDW |
| BFC6 | 04C6 | JSR tube_init_reloc ; Re-init reloc pointers for final claim |
| BFC9 | 04C9 | LDA #4 ; A=4: transfer type for final address claim |
| ; Claim default Tube transfer address | ||
| ; Sets Y=0, X=&53 (address &0053), then JMP ; tube_addr_claim | ||
| ; to initiate a Tube address claim for the default ; transfer | ||
| ; address. Called from the BEGIN startup path and after ; the | ||
| ; page transfer loop completes. | ||
| BFCB | 04CB | .tube_claim_default←1← 04A4 JSR |
| LDY #0 ; Y=0: transfer address low byte | ||
| BFCD | 04CD | LDX #&53 ; X=&53: transfer address high byte (&0053) |
| BFCF | 04CF | JMP tube_addr_data_dispatch ; Claim Tube address for transfer |
| ; Initialise relocation address for ROM transfer | ||
| ; Sets the Tube transfer source page to &8000 | ||
| ; (tube_xfer_page = &80) and the page counter to &80. | ||
| ; Checks ROM type bit 5 for a relocation address in the | ||
| ; ROM header. If set, scans past the null-terminated | ||
| ; copyright string and extracts the 4-byte relocation | ||
| ; address into tube_transfer_addr (&53), tube_xfer_page | ||
| ; (&54), tube_xfer_addr_2 (&55), and tube_xfer_addr_3 | ||
| ; (&56). If clear, uses the default &8000 start address. | ||
| ; Called twice during tube_begin: once for initial setup | ||
| ; and once after each page transfer completes. | ||
| BFD2 | 04D2 | .tube_init_reloc←2← 049F JSR← 04C6 JSR |
| LDA #&80 ; Init: start sending from &8000 | ||
| BFD4 | 04D4 | STA tube_xfer_page ; Store &80 as source page high byte |
| BFD6 | 04D6 | STA zp_ptr_hi ; Store &80 as page counter initial value |
| BFD8 | 04D8 | LDA #&20 ; A=&20: bit 5 mask for ROM type check |
| BFDA | 04DA | AND rom_type ; ROM type bit 5: reloc address in header? |
| BFDD | 04DD | TAY ; Y = 0 or &20 (reloc flag) |
| BFDE | 04DE | STY tube_transfer_addr ; Store as transfer address selector |
| BFE0 | 04E0 | BEQ store_xfer_end_addr ; No reloc addr: use defaults |
| BFE2 | 04E2 | LDX copyright_offset ; Skip past copyright string to find reloc addr |
| BFE5 | 04E5 | .scan_copyright_end←1← 04E9 BNE |
| INX ; Skip past null-terminated copyright string | ||
| BFE6 | 04E6 | LDA rom_header,x ; Load next byte from ROM header |
| BFE9 | 04E9 | BNE scan_copyright_end ; Loop until null terminator found |
| BFEB | 04EB | LDA rom_header_byte1,x ; Read 4-byte reloc address from ROM header |
| BFEE | 04EE | STA tube_transfer_addr ; Store reloc addr byte 1 as transfer addr |
| BFF0 | 04F0 | LDA rom_header_byte2,x ; Load reloc addr byte 2 |
| BFF3 | 04F3 | STA tube_xfer_page ; Store as source page start |
| BFF5 | 04F5 | LDY service_entry,x ; Load reloc addr byte 3 |
| BFF8 | 04F8 | LDA service_handler_lo,x ; Load reloc addr byte 4 (highest) |
| BFFB | 04FB | .store_xfer_end_addr←1← 04E0 BEQ |
| STA tube_xfer_addr_3 ; Store high byte of end address | ||
| BFFD | 04FD | STY tube_xfer_addr_2 ; Store byte 3 of end address |
| BFFF | 04FF | RTS ; Return with pointers initialised |
