Acorn ANFS 4.18
Updated 31 Mar 2026
← All Acorn NFS and Acorn ANFS versions
- Disassembly source on GitHub
- Acorn ANFS 4.18 in The BBC Micro ROM Library
- Discuss this disassembly on the Stardot Forums thread: Acorn ANFS - A new annotated disassembly
- Changes from ANFS 4.08.53
- 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← 04E2 LDA |
| .language_entry←1← 04E2 LDA | |
| .pydis_start←1← 04E2 LDA | |
| EQUB &00, &42, &43 | |
| 8003 | .service_entry←1← 04F1 LDY |
| JMP service_handler ; JMP service_handler | |
| 8006 | .rom_type←1← 04D6 AND |
| EQUB &82 ; ROM type: service + language | |
| 8007 | .copyright_offset←1← 04DE LDX |
| EQUB copyright - rom_header | |
| 8008 | .binary_version |
| EQUB &04 | |
| 8009 | .title |
| EQUS "Acorn ANFS 4.18" | |
| 8018 | .version |
| EQUB &00 | |
| 8019 | .copyright |
| EQUB &00 ; Null terminator before copyright | |
| 801A | .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.
|
||||||||
| 8028 | .svc5_irq_check | |||||||
| LDA #4 ; A=4: SR bit mask for IFR test | ||||||||
| 802A | BIT system_via_ifr ; Test IFR bit 2: SR complete | |||||||
| 802D | BNE save_registers ; SR set: shift register complete | |||||||
| 802F | LDA #5 ; A=5: not our interrupt, pass on | |||||||
| 8031 | RTS ; Return service code 5 to MOS | |||||||
| 8032 | .save_registers←1← 802D BNE | |||||||
| TXA ; Save X on stack | ||||||||
| 8033 | PHA ; Push saved X | |||||||
| 8034 | TYA ; Save Y on stack | |||||||
| 8035 | PHA ; Push saved Y | |||||||
| 8036 | LDA system_via_acr ; Read ACR for shift register restore | |||||||
| 8039 | AND #&e3 ; Clear SR mode bits (2-4) | |||||||
| 803B | ORA ws_0d64 ; Restore saved SR mode from ws_0d64 | |||||||
| 803E | STA system_via_acr ; Write restored ACR to system VIA | |||||||
| 8041 | LDA system_via_sr ; Read SR to clear shift register IRQ | |||||||
| 8044 | LDA #4 ; A=4: SR bit mask | |||||||
| 8046 | STA system_via_ifr ; Clear SR interrupt flag in IFR | |||||||
| 8049 | STA system_via_ier ; Disable SR interrupt in IER | |||||||
| 804C | LDY tx_op_type ; Load TX operation type for dispatch | |||||||
| 804F | TYA ; Copy to A for sign test | |||||||
| 8050 | BMI set_jsr_protection ; Bit 7 set: dispatch via table | |||||||
| 8052 | LDA #&fe ; A=&FE: Econet receive event | |||||||
| 8054 | JSR generate_event ; Call event vector handler | |||||||
| 8057 | JMP tx_done_exit ; Fire event (enable: *FX52,150) | |||||||
Generate event via event vectorDispatches through the event vector (EVNTV) to notify event handlers. Called with the event number in A.
|
|||||||||||
| 805A | .generate_event←1← 8054 JSR | ||||||||||
| JMP (evntv) ; Dispatch through event vector | |||||||||||
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.
|
||||
| 805D | .set_jsr_protection←1← 8050 BMI | |||
| CPY #&86 ; Y >= &86: above dispatch range | ||||
| 805F | BCS dispatch_svc5 ; Out of range: skip protection | |||
| 8061 | LDA ws_0d68 ; Save current JSR protection mask | |||
| 8064 | STA ws_0d69 ; Backup to saved_jsr_mask | |||
| 8067 | ORA #&1c ; Set protection bits 2-4 | |||
| 8069 | STA ws_0d68 ; Apply protection during dispatch | |||
| 806C | .dispatch_svc5←1← 805F BCS | |||
| LDA #&85 ; Push return addr high (&85) | ||||
| 806E | PHA ; High byte on stack for RTS | |||
| 806F | LDA set_rx_buf_len_hi,y ; Load dispatch target low byte | |||
| 8072 | PHA ; Low byte on stack for RTS | |||
| 8073 | .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. |
|
| 8074 | .adlc_init←1← 8F5D JSR |
| BIT station_id_disable_net_nmis ; INTOFF: read station ID, disable NMIs | |
| 8077 | JSR adlc_full_reset ; Full ADLC hardware reset |
| 807A | LDA #&ea ; OSBYTE &EA: check Tube co-processor |
| 807C | LDX #0 ; X=0 for OSBYTE |
| 807E | STX ws_0d62 ; Clear Econet init flag before setup |
| 8081 | JSR osbyte_x0 ; Check Tube presence via OSBYTE &EA |
| 8084 | STX tube_present ; Store Tube presence flag from OSBYTE &EA |
| 8087 | LDA #&8f ; OSBYTE &8F: issue service request |
| 8089 | LDX #&0c ; X=&0C: NMI claim service |
| 808B | JSR osbyte_yff ; Issue NMI claim service request |
| 808E | LDY #5 ; Y=5: NMI claim service number |
| 8090 | .econet_restore |
| CPY #5 ; Check if NMI service was claimed (Y changed) | |
| 8092 | 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). |
|
| 8094 | .init_nmi_workspace |
| LDY #&20 ; Copy 32 bytes of NMI shim from ROM to &0D00 | |
| 8096 | .copy_nmi_shim←1← 809D BNE |
| LDA listen_jmp_hi,y ; Read byte from NMI shim ROM source | |
| 8099 | STA nmi_code_base,y ; Write to NMI shim RAM at &0D00 |
| 809C | DEY ; Next byte (descending) |
| 809D | BNE copy_nmi_shim ; Loop until all 32 bytes copied |
| 809F | LDA romsel_copy ; Patch current ROM bank into NMI shim |
| 80A1 | STA nmi_romsel ; Self-modifying code: ROM bank at &0D07 |
| 80A4 | STY tx_src_net ; Clear source network (Y=0 from copy loop) |
| 80A7 | STY prot_flags ; Clear Tube release flag |
| 80A9 | STY tx_op_type ; Clear TX operation type |
| 80AC | LDY station_id_disable_net_nmis ; Read station ID (and disable NMIs) |
| 80AF | STY tx_src_stn ; Set own station as TX source |
| 80B2 | LDA #&80 ; &80 = Econet initialised |
| 80B4 | STA ws_0d60 ; Mark TX as complete (ready) |
| 80B7 | STA ws_0d62 ; Mark Econet as initialised |
| 80BA | BIT video_ula_control ; INTON: re-enable NMIs (&FE20 read side effect) |
| 80BD | .adlc_init_done←1← 8092 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). |
|
| 80BE | .nmi_rx_scout←1← 89B2 JMP |
| LDA #1 ; A=&01: mask for SR2 bit0 (AP = Address Present) | |
| 80C0 | BIT econet_control23_or_status2 ; BIT SR2: Z = A AND SR2 -- tests if AP is set |
| 80C3 | BEQ scout_error ; AP not set, no incoming data -- check for errors |
| 80C5 | LDA econet_data_continue_frame ; Read first RX byte (destination station address) |
| 80C8 | CMP station_id_disable_net_nmis ; Compare to our station ID (&FE18 read = INTOFF, disables NMIs) |
| 80CB | BEQ accept_frame ; Match -- accept frame |
| 80CD | CMP #&ff ; Check for broadcast address (&FF) |
| 80CF | BNE scout_reject ; Neither our address nor broadcast -- reject frame |
| 80D1 | LDA #&40 ; Flag &40 = broadcast frame |
| 80D3 | STA rx_src_net ; Store broadcast flag in rx_src_net |
| 80D6 | .accept_frame←1← 80CB BEQ |
| LDA #&db ; Install nmi_rx_scout_net NMI handler | |
| 80D8 | 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. |
|
| 80DB | .nmi_rx_scout_net |
| BIT econet_control23_or_status2 ; BIT SR2: test for RDA (bit7 = data available) | |
| 80DE | BPL scout_error ; No RDA -- check errors |
| 80E0 | LDA econet_data_continue_frame ; Read destination network byte |
| 80E3 | BEQ accept_local_net ; Network = 0 -- local network, accept |
| 80E5 | EOR #&ff ; EOR &FF: test if network = &FF (broadcast) |
| 80E7 | BEQ accept_scout_net ; Broadcast network -- accept |
| 80E9 | .scout_reject←1← 80CF BNE |
| LDA #&a2 ; Reject: wrong network. CR1=&A2: RIE|RX_DISCONTINUE | |
| 80EB | STA econet_control1_or_status1 ; Write CR1 to discontinue RX |
| 80EE | JMP set_nmi_rx_scout ; Return to idle scout listening |
| 80F1 | .accept_local_net←1← 80E3 BEQ |
| STA rx_src_net ; Network = 0 (local): clear tx_flags | |
| 80F4 | .accept_scout_net←1← 80E7 BEQ |
| STA port_buf_len ; Store Y offset for scout data buffer | |
| 80F6 | LDA #&0d ; Install scout data handler (&8102) |
| 80F8 | LDY #&81 ; High byte of scout data handler |
| 80FA | 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. |
|
| 80FD | .scout_error←5← 80C3 BEQ← 80DE BPL← 8112 BPL← 8146 BEQ← 8148 BPL |
| LDA econet_control23_or_status2 ; Read SR2 | |
| 8100 | AND #&81 ; Test AP (b0) | RDA (b7) |
| 8102 | BEQ scout_discard ; Neither set -- clean end, discard frame |
| 8104 | JSR adlc_full_reset ; Unexpected data/status: full ADLC reset |
| 8107 | JMP set_nmi_rx_scout ; Discard and return to idle |
| 810A | .scout_discard←1← 8102 BEQ |
| JMP reset_adlc_rx_listen ; Gentle discard: RX_DISCONTINUE | |
| 810D | LDY port_buf_len ; Y = buffer offset |
| 810F | LDA econet_control23_or_status2 ; Read SR2 |
| 8112 | .scout_loop_rda←1← 8132 BNE |
| BPL scout_error ; No RDA -- error handler | |
| 8114 | LDA econet_data_continue_frame ; Read data byte from RX FIFO |
| 8117 | STA scout_buf,y ; Store at &0D3D+Y (scout buffer) |
| 811A | INY ; Advance buffer index |
| 811B | LDA econet_control23_or_status2 ; Read SR2 again (FV detection point) |
| 811E | BMI scout_loop_second ; RDA set -- more data, read second byte |
| 8120 | BNE scout_complete ; SR2 non-zero (FV or other) -- scout completion |
| 8122 | .scout_loop_second←1← 811E BMI |
| LDA econet_data_continue_frame ; Read second byte of pair | |
| 8125 | STA scout_buf,y ; Store at &0D3D+Y |
| 8128 | INY ; Advance and check buffer limit |
| 8129 | CPY #&0c ; Copied all 12 scout bytes? |
| 812B | BEQ scout_complete ; Buffer full (Y=12) -- force completion |
| 812D | STY port_buf_len ; Save final buffer offset |
| 812F | LDA econet_control23_or_status2 ; Read SR2 for next pair |
| 8132 | BNE scout_loop_rda ; SR2 non-zero -- loop back for more bytes |
| 8134 | 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. |
|
| 8137 | .scout_complete←2← 8120 BNE← 812B BEQ |
| LDA #0 ; Save Y for next iteration | |
| 8139 | STA econet_control1_or_status1 ; Write CR1 |
| 813C | LDA #&84 ; CR2=&84: disable PSE, enable RDA_SUPPRESS_FV |
| 813E | STA econet_control23_or_status2 ; Write CR2 |
| 8141 | LDA #2 ; A=&02: FV mask for SR2 bit1 |
| 8143 | BIT econet_control23_or_status2 ; BIT SR2: test FV (Z) and RDA (N) |
| 8146 | BEQ scout_error ; No FV -- not a valid frame end, error |
| 8148 | BPL scout_error ; FV set but no RDA -- missing last byte, error |
| 814A | LDA econet_data_continue_frame ; Read last byte from RX FIFO |
| 814D | STA scout_buf,y ; Store last byte at &0D3D+Y |
| 8150 | LDA #&44 ; CR1=&44: RX_RESET | TIE (switch to TX for ACK) |
| 8152 | STA econet_control1_or_status1 ; Write CR1: switch to TX mode |
| 8155 | SEC ; Set bit7 of need_release_tube flag |
| 8156 | ROR prot_flags ; Rotate C=1 into bit7: mark Tube release needed |
| 8158 | LDA scout_port ; Check port byte: 0 = immediate op, non-zero = data transfer |
| 815B | BNE scout_match_port ; Port non-zero -- look for matching receive block |
| 815D | .scout_no_match |
| JMP immediate_op ; Port = 0 -- immediate operation handler | |
| 8160 | .scout_match_port←1← 815B BNE |
| BIT rx_src_net ; Check if broadcast (bit6 of tx_flags) | |
| 8163 | BVC scan_port_list ; Not broadcast -- skip CR2 setup |
| 8165 | LDA #7 ; CR2=&07: broadcast prep |
| 8167 | STA econet_control23_or_status2 ; Write CR2: broadcast frame prep |
| 816A | .scan_port_list←1← 8163 BVC |
| BIT econet_flags ; Check if RX port list active (bit7) | |
| 816D | BPL try_nfs_port_list ; No active ports -- try NFS workspace |
| 816F | LDA #&c0 ; Start scanning port list at page &C0 |
| 8171 | LDY #0 ; Y=0: start offset within each port slot |
| 8173 | .scan_nfs_port_list←1← 81B8 BNE |
| STA port_ws_offset ; Store page to workspace pointer low | |
| 8175 | STY rx_buf_offset ; Store page high byte for slot scanning |
| 8177 | .check_port_slot←1← 81AA BCC |
| LDY #0 ; Y=0: read control byte from start of slot | |
| 8179 | .scout_ctrl_check |
| LDA (port_ws_offset),y ; Read port control byte from slot | |
| 817B | BEQ discard_no_match ; Zero = end of port list, no match |
| 817D | CMP #&7f ; &7F = any-port wildcard |
| 817F | BNE next_port_slot ; Not wildcard -- check specific port match |
| 8181 | INY ; Y=1: advance to port byte in slot |
| 8182 | LDA (port_ws_offset),y ; Read port number from slot (offset 1) |
| 8184 | BEQ check_station_filter ; Zero port in slot = match any port |
| 8186 | CMP scout_port ; Check if port matches this slot |
| 8189 | BNE next_port_slot ; Port mismatch -- try next slot |
| 818B | .check_station_filter←1← 8184 BEQ |
| INY ; Y=2: advance to station byte | |
| 818C | LDA (port_ws_offset),y ; Read station filter from slot (offset 2) |
| 818E | BEQ port_match_found ; Zero station = match any station, accept |
| 8190 | CMP scout_buf ; Check if source station matches |
| 8193 | BNE next_port_slot ; Station mismatch -- try next slot |
| 8195 | .scout_port_match |
| INY ; Y=3: advance to network byte | |
| 8196 | LDA (port_ws_offset),y ; Read network filter from slot (offset 3) |
| 8198 | BEQ port_match_found ; Zero = accept any network |
| 819A | CMP scout_src_net ; Check if source network matches |
| 819D | BEQ port_match_found ; Network matches or zero = accept |
| 819F | .next_port_slot←3← 817F BNE← 8189 BNE← 8193 BNE |
| LDA rx_buf_offset ; Check if NFS workspace search pending | |
| 81A1 | BEQ try_nfs_port_list ; No NFS workspace -- try fallback path |
| 81A3 | LDA port_ws_offset ; Load current slot base address |
| 81A5 | CLC ; CLC for 12-byte slot advance |
| 81A6 | ADC #&0c ; Advance to next 12-byte port slot |
| 81A8 | STA port_ws_offset ; Update workspace pointer to next slot |
| 81AA | BCC check_port_slot ; Always branches (page &C0 won't overflow) |
| 81AC | .discard_no_match←2← 817B BEQ← 81B2 BVC |
| JMP nmi_error_dispatch ; No match found -- discard frame | |
| 81AF | .try_nfs_port_list←2← 816D BPL← 81A1 BEQ |
| BIT econet_flags ; Try NFS workspace if paged list exhausted | |
| 81B2 | BVC discard_no_match ; No NFS workspace RX (bit6 clear) -- discard |
| 81B4 | LDA #0 ; NFS workspace starts at offset 0 in page |
| 81B6 | LDY nfs_workspace_hi ; NFS workspace high byte for port list |
| 81B8 | BNE scan_nfs_port_list ; Scan NFS workspace port list |
| 81BA | .port_match_found←4← 818E BEQ← 8198 BEQ← 819D BEQ← 84B6 JMP |
| LDA #3 ; Match found: set scout_status = 3 | |
| 81BC | STA rx_port ; Record match for completion handler |
| 81BF | JSR tx_calc_transfer ; Calculate transfer parameters |
| 81C2 | BCC nmi_error_dispatch ; C=0: no Tube claimed -- discard |
| 81C4 | BIT rx_src_net ; Check broadcast flag for ACK path |
| 81C7 | BVC send_data_rx_ack ; Not broadcast -- normal ACK path |
| 81C9 | JMP copy_scout_to_buffer ; Broadcast: different completion path |
| 81CC | .send_data_rx_ack←2← 81C7 BVC← 84AB JMP |
| LDA #&44 ; CR1=&44: RX_RESET | TIE | |
| 81CE | STA econet_control1_or_status1 ; Write CR1: TX mode for ACK |
| 81D1 | LDA #&a7 ; CR2=&A7: RTS | CLR_TX_ST | FC_TDRA | PSE |
| 81D3 | STA econet_control23_or_status2 ; Write CR2: enable TX with PSE |
| 81D6 | LDA #&dd ; Install data_rx_setup at &81DD |
| 81D8 | LDY #&81 ; High byte of data_rx_setup handler |
| 81DA | JMP ack_tx_write_dest ; Send ACK with data_rx_setup as next NMI |
| 81DD | .data_rx_setup |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (switch to RX for data frame) | |
| 81DF | STA econet_control1_or_status1 ; Write CR1: switch to RX for data frame |
| 81E2 | LDA #&e7 ; Install nmi_data_rx at &81E7 |
| 81E4 | 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: &81E7 (AP+addr check) -> &81FB (net=0 check) -> &8211 (skip ctrl+port) -> &8239 (bulk data read) -> &8278 (completion) |
|
| 81E7 | .nmi_data_rx |
| LDA #1 ; A=1: AP mask for SR2 bit test | |
| 81E9 | BIT econet_control23_or_status2 ; BIT SR2: test AP bit |
| 81EC | BEQ nmi_error_dispatch ; No AP: wrong frame or error |
| 81EE | LDA econet_data_continue_frame ; Read first byte (dest station) |
| 81F1 | CMP station_id_disable_net_nmis ; Compare to our station ID (INTOFF) |
| 81F4 | BNE nmi_error_dispatch ; Not for us: error path |
| 81F6 | LDA #&fb ; Install net check handler at &81FB |
| 81F8 | JMP install_nmi_handler ; Set NMI vector via RAM shim |
| 81FB | .nmi_data_rx_net |
| BIT econet_control23_or_status2 ; Validate source network = 0 | |
| 81FE | BPL nmi_error_dispatch ; SR2 bit7 clear: no data ready -- error |
| 8200 | LDA econet_data_continue_frame ; Read dest network byte |
| 8203 | BNE nmi_error_dispatch ; Network != 0: wrong network -- error |
| 8205 | LDA #&11 ; Install skip handler at &8211 |
| 8207 | LDY #&82 ; High byte of &8211 handler |
| 8209 | BIT econet_control1_or_status1 ; SR1 bit7: IRQ, data already waiting |
| 820C | BMI nmi_data_rx_skip ; Data ready: skip directly, no RTI |
| 820E | JMP set_nmi_vector ; Install handler and return via RTI |
| 8211 | .nmi_data_rx_skip←1← 820C BMI |
| BIT econet_control23_or_status2 ; Skip control and port bytes (already known from scout) | |
| 8214 | BPL nmi_error_dispatch ; SR2 bit7 clear: error |
| 8216 | LDA econet_data_continue_frame ; Discard control byte |
| 8219 | 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. |
|
| 821C | .install_data_rx_handler←1← 88C6 JMP |
| LDA #2 ; A=2: Tube transfer flag mask | |
| 821E | BIT rx_src_net ; Check if Tube transfer active |
| 8221 | BNE install_tube_rx ; Tube active: use Tube RX path |
| 8223 | LDA #&44 ; Install bulk read at &8239 |
| 8225 | LDY #&82 ; High byte of &8239 handler |
| 8227 | BIT econet_control1_or_status1 ; SR1 bit7: more data already waiting? |
| 822A | BMI nmi_data_rx_bulk ; Yes: enter bulk read directly |
| 822C | JMP set_nmi_vector ; No: install handler and RTI |
| 822F | .install_tube_rx←1← 8221 BNE |
| LDA #&a1 ; Tube: install Tube RX at &8296 | |
| 8231 | LDY #&82 ; High byte of &8296 handler |
| 8233 | 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). |
|
| 8236 | .nmi_error_dispatch←12← 81AC JMP← 81C2 BCC← 81EC BEQ← 81F4 BNE← 81FE BPL← 8203 BNE← 8214 BPL← 8257 BEQ← 8289 BEQ← 828F BEQ← 834C JMP← 8485 JMP |
| LDA rx_src_net ; Check tx_flags for error path | |
| 8239 | BPL rx_error_reset ; Bit7 clear: RX error path |
| 823B | JMP tx_result_fail ; Bit7 set: TX result = not listening |
| 823E | .rx_error_reset←1← 8239 BPL |
| JSR adlc_full_reset ; Full ADLC reset on RX error | |
| 8241 | 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 &8278. SR2 = 0 -> RTI, wait for next NMI to continue. |
|
| 8244 | .nmi_data_rx_bulk←1← 822A BMI |
| LDY port_buf_len ; Y = buffer offset, resume from last position | |
| 8246 | LDA econet_control23_or_status2 ; Read SR2 for next pair |
| 8249 | .data_rx_loop←1← 8273 BNE |
| BPL data_rx_complete ; SR2 bit7 clear: frame complete (FV) | |
| 824B | LDA econet_data_continue_frame ; Read first byte of pair from RX FIFO |
| 824E | STA (open_port_buf),y ; Store byte to buffer |
| 8250 | INY ; Advance buffer offset |
| 8251 | BNE read_sr2_between_pairs ; Y != 0: no page boundary crossing |
| 8253 | INC open_port_buf_hi ; Crossed page: increment buffer high byte |
| 8255 | DEC port_buf_len_hi ; Decrement remaining page count |
| 8257 | BEQ nmi_error_dispatch ; No pages left: handle as complete |
| 8259 | .read_sr2_between_pairs←1← 8251 BNE |
| LDA econet_control23_or_status2 ; Read SR2 between byte pairs | |
| 825C | BMI read_second_rx_byte ; SR2 bit7 set: more data available |
| 825E | BNE data_rx_complete ; SR2 non-zero, bit7 clear: frame done |
| 8260 | .read_second_rx_byte←1← 825C BMI |
| LDA econet_data_continue_frame ; Read second byte of pair from RX FIFO | |
| 8263 | STA (open_port_buf),y ; Store byte to buffer |
| 8265 | INY ; Advance buffer offset |
| 8266 | STY port_buf_len ; Save updated buffer position |
| 8268 | BNE check_sr2_loop_again ; Y != 0: no page boundary crossing |
| 826A | INC open_port_buf_hi ; Crossed page: increment buffer high byte |
| 826C | DEC port_buf_len_hi ; Decrement remaining page count |
| 826E | BEQ data_rx_complete ; No pages left: frame complete |
| 8270 | .check_sr2_loop_again←1← 8268 BNE |
| LDA econet_control23_or_status2 ; Read SR2 for next iteration | |
| 8273 | BNE data_rx_loop ; SR2 non-zero: more data, loop back |
| 8275 | JMP nmi_rti ; SR2=0: no more data yet, wait for NMI |
Data frame completion |
|
| 8278 | .data_rx_complete←3← 8249 BPL← 825E BNE← 826E BEQ |
| LDA #&84 ; CR1=&00: disable all interrupts | |
| 827A | STA econet_control23_or_status2 ; Write CR2: disable PSE for bit testing |
| 827D | LDA #0 ; CR2=&84: disable PSE for individual bit testing |
| 827F | STA econet_control1_or_status1 ; Write CR1: disable all interrupts |
| 8282 | STY port_buf_len ; Save Y (byte count from data RX loop) |
| 8284 | LDA #2 ; A=&02: FV mask |
| 8286 | BIT econet_control23_or_status2 ; BIT SR2: test FV (Z) and RDA (N) |
| 8289 | BEQ nmi_error_dispatch ; No FV -- error |
| 828B | BPL send_ack ; FV set, no RDA -- proceed to ACK |
| 828D | LDA port_buf_len_hi ; Check if buffer space remains |
| 828F | .read_last_rx_byte←3← 82AC BEQ← 82D3 BEQ← 82DF BEQ |
| BEQ nmi_error_dispatch ; No buffer space: error/discard frame | |
| 8291 | LDA econet_data_continue_frame ; FV+RDA: read and store last data byte |
| 8294 | LDY port_buf_len ; Y = current buffer write offset |
| 8296 | STA (open_port_buf),y ; Store last byte in port receive buffer |
| 8298 | INC port_buf_len ; Advance buffer write offset |
| 829A | BNE send_ack ; No page wrap: proceed to send ACK |
| 829C | INC open_port_buf_hi ; Page boundary: advance buffer page |
| 829E | .send_ack←2← 828B BPL← 829A BNE |
| JMP ack_tx ; Send ACK frame to complete handshake | |
| 82A1 | .nmi_data_rx_tube |
| LDA econet_control23_or_status2 ; Read SR2 for Tube data receive path | |
| 82A4 | .rx_tube_data←1← 82BF BNE |
| BPL data_rx_tube_complete ; RDA clear: no more data, frame complete | |
| 82A6 | LDA econet_data_continue_frame ; Read data byte from ADLC RX FIFO |
| 82A9 | JSR advance_buffer_ptr ; Check buffer limits and transfer size |
| 82AC | BEQ read_last_rx_byte ; Zero: buffer full, handle as error |
| 82AE | STA tube_data_register_3 ; Send byte to Tube data register 3 |
| 82B1 | LDA econet_data_continue_frame ; Read second data byte (paired transfer) |
| 82B4 | STA tube_data_register_3 ; Send second byte to Tube |
| 82B7 | JSR advance_buffer_ptr ; Check limits after byte pair |
| 82BA | BEQ data_rx_tube_complete ; Zero: Tube transfer complete |
| 82BC | LDA econet_control23_or_status2 ; Re-read SR2 for next byte pair |
| 82BF | BNE rx_tube_data ; More data available: continue loop |
| 82C1 | .data_rx_tube_error |
| JMP nmi_rti ; Unexpected end: return from NMI | |
| 82C4 | .data_rx_tube_complete←2← 82A4 BPL← 82BA BEQ |
| LDA #0 ; CR1=&00: disable all interrupts | |
| 82C6 | STA econet_control1_or_status1 ; Write CR1 for individual bit testing |
| 82C9 | LDA #&84 ; CR2=&84: disable PSE |
| 82CB | STA econet_control23_or_status2 ; Write CR2: same pattern as main path |
| 82CE | LDA #2 ; A=&02: FV mask for Tube completion |
| 82D0 | BIT econet_control23_or_status2 ; BIT SR2: test FV (Z) and RDA (N) |
| 82D3 | BEQ read_last_rx_byte ; No FV: incomplete frame, error |
| 82D5 | BPL ack_tx ; FV set, no RDA: proceed to ACK |
| 82D7 | LDA port_buf_len ; Check if any buffer was allocated |
| 82D9 | ORA port_buf_len_hi ; OR all 4 buffer pointer bytes together |
| 82DB | ORA open_port_buf ; Check buffer low byte |
| 82DD | ORA open_port_buf_hi ; Check buffer high byte |
| 82DF | BEQ read_last_rx_byte ; All zero (null buffer): error |
| 82E1 | LDA econet_data_continue_frame ; Read extra trailing byte from FIFO |
| 82E4 | STA rx_extra_byte ; Save extra byte at &0D5D for later use |
| 82E7 | LDA #&20 ; Bit5 = extra data byte available flag |
| 82E9 | ORA rx_src_net ; Set extra byte flag in tx_flags |
| 82EC | 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. |
|
| 82EF | .ack_tx←2← 829E JMP← 82D5 BPL |
| LDA rx_src_net ; Load TX flags to check ACK type | |
| 82F2 | BPL ack_tx_configure ; Bit7 clear: normal scout ACK |
| 82F4 | JSR advance_rx_buffer_ptr ; Final ACK: call completion handler |
| 82F7 | JMP tx_result_ok ; Jump to TX success result |
| 82FA | .ack_tx_configure←1← 82F2 BPL |
| LDA #&44 ; CR1=&44: RX_RESET | TIE (switch to TX mode) | |
| 82FC | STA econet_control1_or_status1 ; Write CR1: switch to TX mode |
| 82FF | LDA #&a7 ; CR2=&A7: RTS|CLR_TX_ST|FC_TDRA|2_1_BYTE|PSE |
| 8301 | STA econet_control23_or_status2 ; Write CR2: enable TX with status clear |
| 8304 | LDA #&96 ; Install saved next handler (&8396 for scout ACK) |
| 8306 | LDY #&83 ; High byte of post-ACK handler |
| 8308 | .ack_tx_write_dest←2← 81DA JMP← 84F3 JMP |
| STA saved_nmi_lo ; Store next handler low byte | |
| 830B | STY saved_nmi_hi ; Store next handler high byte |
| 830E | LDA scout_buf ; Load dest station from RX scout buffer |
| 8311 | BIT econet_control1_or_status1 ; BIT SR1: test TDRA (V=bit6) |
| 8314 | BVC dispatch_nmi_error ; TDRA not ready -- error |
| 8316 | STA econet_data_continue_frame ; Write dest station to TX FIFO |
| 8319 | LDA scout_src_net ; Write dest network to TX FIFO |
| 831C | STA econet_data_continue_frame ; Write dest net byte to FIFO |
| 831F | LDA #&26 ; Install handler at &8326 (write src addr) |
| 8321 | LDY #&83 ; High byte of nmi_ack_tx_src |
| 8323 | 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. |
|
| 8326 | .nmi_ack_tx_src |
| LDA station_id_disable_net_nmis ; Load our station ID (also INTOFF) | |
| 8329 | BIT econet_control1_or_status1 ; BIT SR1: test TDRA |
| 832C | BVC dispatch_nmi_error ; TDRA not ready -- error |
| 832E | STA econet_data_continue_frame ; Write our station to TX FIFO |
| 8331 | LDA #0 ; Write network=0 to TX FIFO |
| 8333 | STA econet_data_continue_frame ; Write network=0 (local) to TX FIFO |
| 8336 | LDA rx_src_net ; Check tx_flags for data phase |
| 8339 | BMI start_data_tx ; bit7 set: start data TX phase |
| 833B | 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. |
|
| 833D | .post_ack_scout |
| STA econet_control23_or_status2 ; Write CR2 to clear status after ACK TX | |
| 8340 | LDA saved_nmi_lo ; Install saved handler from &0D4B/&0D4C |
| 8343 | LDY saved_nmi_hi ; Load saved next handler high byte |
| 8346 | JMP set_nmi_vector ; Install next NMI handler |
| 8349 | .start_data_tx←1← 8339 BMI |
| JMP data_tx_begin ; Jump to start data TX phase | |
| 834C | .dispatch_nmi_error←2← 8314 BVC← 832C 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. |
|
| 834F | .advance_rx_buffer_ptr←2← 82F4 JSR← 83A5 JSR |
| LDA #2 ; A=2: test bit1 of tx_flags | |
| 8351 | BIT rx_src_net ; BIT tx_flags: check data transfer bit |
| 8354 | BEQ return_rx_complete ; Bit1 clear: no transfer -- return |
| 8356 | CLC ; CLC: init carry for 4-byte add |
| 8357 | PHP ; Save carry on stack for loop |
| 8358 | LDY #8 ; Y=8: RXCB high pointer offset |
| 835A | .add_rxcb_ptr←1← 8366 BCC |
| LDA (port_ws_offset),y ; Load RXCB[Y] (buffer pointer byte) | |
| 835C | PLP ; Restore carry from stack |
| 835D | ADC net_tx_ptr,y ; Add transfer count byte |
| 8360 | STA (port_ws_offset),y ; Store updated pointer back to RXCB |
| 8362 | INY ; Next byte |
| 8363 | PHP ; Save carry for next iteration |
| 8364 | CPY #&0c ; Done 4 bytes? (Y reaches &0C) |
| 8366 | BCC add_rxcb_ptr ; No: continue adding |
| 8368 | PLP ; Discard final carry |
| 8369 | LDA #&20 ; A=&20: test bit5 of tx_flags |
| 836B | BIT rx_src_net ; BIT tx_flags: check Tube bit |
| 836E | BEQ skip_tube_update ; No Tube: skip Tube update |
| 8370 | TXA ; Save X on stack |
| 8371 | PHA ; Push X |
| 8372 | LDA #8 ; A=8: offset for Tube address |
| 8374 | CLC ; CLC for address calculation |
| 8375 | ADC port_ws_offset ; Add workspace base offset |
| 8377 | TAX ; X = address low for Tube claim |
| 8378 | LDY rx_buf_offset ; Y = address high for Tube claim |
| 837A | LDA #1 ; A=1: Tube claim type (read) |
| 837C | JSR tube_addr_data_dispatch ; Claim Tube address for transfer |
| 837F | LDA rx_extra_byte ; Load extra RX data byte |
| 8382 | STA tube_data_register_3 ; Send to Tube via R3 |
| 8385 | SEC ; SEC: init carry for increment |
| 8386 | LDY #8 ; Y=8: start at high pointer |
| 8388 | .inc_rxcb_ptr←1← 838F BCS |
| LDA #0 ; A=0: add carry only (increment) | |
| 838A | ADC (port_ws_offset),y ; Add carry to pointer byte |
| 838C | STA (port_ws_offset),y ; Store back to RXCB |
| 838E | INY ; Next byte |
| 838F | BCS inc_rxcb_ptr ; Keep going while carry propagates |
| 8391 | PLA ; Restore X from stack |
| 8392 | TAX ; Transfer to X register |
| 8393 | .skip_tube_update←1← 836E BEQ |
| LDA #&ff ; A=&FF: return value (transfer done) | |
| 8395 | .return_rx_complete←1← 8354 BEQ |
| RTS ; Return | |
| 8396 | LDA scout_port ; Load received port byte |
| 8399 | BNE rx_complete_update_rxcb ; Port != 0: data transfer frame |
| 839B | LDY scout_ctrl ; Port=0: load control byte |
| 839E | CPY #&82 ; Ctrl = &82 (POKE)? |
| 83A0 | BEQ rx_complete_update_rxcb ; Yes: POKE also needs data transfer |
| 83A2 | 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. |
|
| 83A5 | .rx_complete_update_rxcb←3← 8399 BNE← 83A0 BEQ← 8434 JMP |
| JSR advance_rx_buffer_ptr ; Update buffer pointer and check for Tube | |
| 83A8 | BNE skip_buf_ptr_update ; Transfer not done: skip buffer update |
| 83AA | .add_buf_to_base |
| LDA port_buf_len ; Load buffer bytes remaining | |
| 83AC | CLC ; CLC for address add |
| 83AD | ADC open_port_buf ; Add to buffer base address |
| 83AF | BCC store_buf_ptr_lo ; No carry: skip high byte increment |
| 83B1 | .inc_rxcb_buf_hi |
| INC open_port_buf_hi ; Carry: increment buffer high byte | |
| 83B3 | .store_buf_ptr_lo←1← 83AF BCC |
| LDY #8 ; Y=8: store updated buffer position | |
| 83B5 | .store_rxcb_buf_ptr |
| STA (port_ws_offset),y ; Store updated low byte to RXCB | |
| 83B7 | INY ; Y=9: buffer high byte offset |
| 83B8 | LDA open_port_buf_hi ; Load updated buffer high byte |
| 83BA | .store_rxcb_buf_hi |
| STA (port_ws_offset),y ; Store high byte to RXCB | |
| 83BC | .skip_buf_ptr_update←1← 83A8 BNE |
| LDA scout_port ; Check port byte again | |
| 83BF | BEQ discard_reset_rx ; Port=0: immediate op, discard+listen |
| 83C1 | LDA scout_src_net ; Load source network from scout buffer |
| 83C4 | LDY #3 ; Y=3: RXCB source network offset |
| 83C6 | STA (port_ws_offset),y ; Store source network to RXCB |
| 83C8 | DEY ; Y=2: source station offset Y=&02 |
| 83C9 | LDA scout_buf ; Load source station from scout buffer |
| 83CC | STA (port_ws_offset),y ; Store source station to RXCB |
| 83CE | DEY ; Y=1: port byte offset Y=&01 |
| 83CF | LDA scout_port ; Load port byte |
| 83D2 | STA (port_ws_offset),y ; Store port to RXCB |
| 83D4 | DEY ; Y=0: control/flag byte offset Y=&00 |
| 83D5 | LDA scout_ctrl ; Load control byte from scout |
| 83D8 | ORA #&80 ; Set bit7 = reception complete flag |
| 83DA | STA (port_ws_offset),y ; Store to RXCB (marks CB as complete) |
| 83DC | LDA fs_flags ; Load callback event flags |
| 83DF | ROR ; Shift bit 0 into carry |
| 83E0 | BCC discard_reset_rx ; Bit 0 clear: no callback, skip to reset |
| 83E2 | LDA port_ws_offset ; Set carry for subtraction Load RXCB workspace pointer low byte |
| 83E4 | .loop_count_rxcb_slot←1← 83E7 BCS |
| INY ; Count slots | |
| 83E5 | SBC #&0c ; Subtract 12 bytes per RXCB slot |
| 83E7 | BCS loop_count_rxcb_slot ; Loop until pointer exhausted |
| 83E9 | DEY ; Adjust for off-by-one |
| 83EA | CPY #3 ; Check slot index >= 3 |
| 83EC | BCC discard_reset_rx ; Slot < 3: no callback, skip to reset |
| 83EE | JSR discard_reset_listen ; Discard scout and reset listen state |
| 83F1 | TYA ; Pass slot index as callback parameter |
| 83F2 | JMP setup_sr_tx ; Jump to TX completion with slot index |
| 83F5 | .discard_reset_rx←6← 8241 JMP← 83BF BEQ← 83E0 BCC← 83EC BCC← 8875 JMP← 88DF JMP |
| JSR discard_reset_listen ; Discard scout and reset RX listen | |
| 83F8 | .reset_adlc_rx_listen←3← 810A JMP← 8470 BCS← 852C JMP |
| JSR adlc_rx_listen ; Reset ADLC and return to RX listen | |
| 83FB | .set_nmi_rx_scout←2← 80EE JMP← 8107 JMP |
| LDA #&be ; A=&BE: low byte of nmi_rx_scout | |
| 83FD | LDY #&80 ; Y=&80: high byte of nmi_rx_scout |
| 83FF | 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. |
|
| 8402 | .discard_reset_listen←2← 83EE JSR← 83F5 JSR |
| LDA #2 ; Tube flag bit 1 AND tx_flags bit 1 | |
| 8404 | AND tube_present ; Check if Tube transfer active |
| 8407 | .imm_op_jump_table←1← 8478 LDA |
| BIT rx_src_net ; Test tx_flags for Tube transfer | |
| 840A | BEQ return_from_discard_reset ; No Tube transfer active -- skip release |
| 840C | JSR release_tube ; Release Tube claim before discarding |
| 840F | .return_from_discard_reset←1← 840A 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. |
|
| 8410 | .copy_scout_to_buffer←1← 81C9 JMP |
| TXA ; Save X on stack | |
| 8411 | PHA ; Push X |
| 8412 | LDX #4 ; X=4: start at scout byte offset 4 |
| 8414 | LDA #2 ; A=2: Tube transfer check mask |
| 8416 | .copy_scout_select |
| BIT rx_src_net ; BIT tx_flags: check Tube bit | |
| 8419 | BNE copy_scout_via_tube ; Tube active: use R3 write path |
| 841B | LDY port_buf_len ; Y = current buffer position |
| 841D | .copy_scout_bytes←1← 8430 BNE |
| LDA scout_buf,x ; Load scout data byte | |
| 8420 | STA (open_port_buf),y ; Store to port buffer |
| 8422 | INY ; Advance buffer pointer |
| 8423 | BNE next_scout_byte ; No page crossing |
| 8425 | INC open_port_buf_hi ; Page crossing: inc buffer high byte |
| 8427 | DEC port_buf_len_hi ; Decrement remaining page count |
| 8429 | BEQ scout_page_overflow ; No pages left: overflow |
| 842B | .next_scout_byte←1← 8423 BNE |
| INX ; Next scout data byte | |
| 842C | STY port_buf_len ; Save updated buffer position |
| 842E | CPX #&0c ; Done all scout data? (X reaches &0C) |
| 8430 | BNE copy_scout_bytes ; No: continue copying |
| 8432 | .scout_copy_done←2← 8447 BEQ← 8481 BEQ |
| PLA ; Restore X from stack | |
| 8433 | TAX ; Transfer to X register |
| 8434 | JMP rx_complete_update_rxcb ; Jump to completion handler |
| 8437 | .copy_scout_via_tube←2← 8419 BNE← 8445 BNE |
| LDA scout_buf,x ; Tube path: load scout data byte | |
| 843A | STA tube_data_register_3 ; Send byte to Tube via R3 |
| 843D | JSR advance_buffer_ptr ; Increment buffer position counters |
| 8440 | BEQ check_scout_done ; Counter overflow: handle end of buffer |
| 8442 | INX ; Next scout data byte |
| 8443 | CPX #&0c ; Done all scout data? |
| 8445 | BNE copy_scout_via_tube ; No: continue Tube writes |
| 8447 | 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. |
|
| 8449 | .release_tube←2← 840C JSR← 893E JSR |
| BIT prot_flags ; Check if Tube needs releasing | |
| 844B | BMI clear_release_flag ; Bit7 set: already released |
| 844D | LDA #&82 ; A=&82: Tube release claim type |
| 844F | JSR tube_addr_data_dispatch ; Release Tube address claim |
| 8452 | .clear_release_flag←1← 844B BMI |
| LSR prot_flags ; Clear release flag (LSR clears bit7) | |
| 8454 | 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. |
|
| 8455 | .immediate_op←1← 815D JMP |
| LDY scout_ctrl ; Control byte &81-&88 range check | |
| 8458 | CPY #&81 ; Below &81: not an immediate op |
| 845A | BCC imm_op_out_of_range ; Out of range low: jump to discard |
| 845C | CPY #&89 ; Above &88: not an immediate op |
| 845E | BCS imm_op_out_of_range ; Out of range high: jump to discard |
| 8460 | CPY #&87 ; HALT(&87)/CONTINUE(&88) skip protection |
| 8462 | BCS dispatch_imm_op ; Ctrl >= &87: dispatch without mask check |
| 8464 | TYA ; Convert ctrl byte to 0-based index for mask |
| 8465 | SEC ; SEC for subtract |
| 8466 | SBC #&81 ; A = ctrl - &81 (0-based operation index) |
| 8468 | TAY ; Y = index for mask rotation count |
| 8469 | LDA ws_0d68 ; Load protection mask from LSTAT |
| 846C | .rotate_prot_mask←1← 846E BPL |
| ROR ; Rotate mask right by control byte index | |
| 846D | DEY ; Decrement rotation counter |
| 846E | BPL rotate_prot_mask ; Loop until bit aligned |
| 8470 | BCS reset_adlc_rx_listen ; Bit set = operation disabled, discard |
| 8472 | .dispatch_imm_op←1← 8462 BCS |
| LDY scout_ctrl ; Reload ctrl byte for dispatch table | |
| 8475 | LDA #&84 ; Hi byte: all handlers are in page &84 |
| 8477 | PHA ; Push hi byte for PHA/PHA/RTS dispatch |
| 8478 | LDA imm_op_jump_table,y ; Load handler low byte from jump table |
| 847B | PHA ; Push handler low byte |
| 847C | RTS ; RTS dispatches to handler |
| 847D | .scout_page_overflow←1← 8429 BEQ |
| INC port_buf_len ; Increment port buffer length | |
| 847F | .check_scout_done←1← 8440 BEQ |
| CPX #&0b ; Check if scout data index reached 11 | |
| 8481 | BEQ scout_copy_done ; Yes: loop back to continue reading |
| 8483 | PLA ; Restore A from stack |
| 8484 | TAX ; Transfer to X |
| 8485 | .imm_op_out_of_range←2← 845A BCC← 845E BCS |
| JMP nmi_error_dispatch ; Jump to discard handler | |
| 8488 | .imm_op_dispatch_lo |
| EQUB <(rx_imm_peek-1) | |
| 8489 | EQUB <(rx_imm_poke-1) |
| 848A | EQUB <(rx_imm_exec-1) |
| 848B | EQUB <(rx_imm_exec-1) |
| 848C | EQUB <(rx_imm_exec-1) |
| 848D | EQUB <(rx_imm_halt_cont-1) |
| 848E | EQUB <(rx_imm_halt_cont-1) |
| 848F | EQUB <(rx_imm_machine_type-1) |
RX immediate: JSR/UserProc/OSProc setup |
|
| 8490 | .rx_imm_exec |
| LDA #0 ; A=0: port buffer lo at page boundary | |
| 8492 | STA open_port_buf ; Set port buffer lo |
| 8494 | LDA #&82 ; Buffer length lo = &82 |
| 8496 | STA port_buf_len ; Set buffer length lo |
| 8498 | LDA #1 ; Buffer length hi = 1 |
| 849A | STA port_buf_len_hi ; Set buffer length hi |
| 849C | LDA net_rx_ptr_hi ; Load RX page hi for buffer |
| 849E | STA open_port_buf_hi ; Set port buffer hi |
| 84A0 | LDY #1 ; Y=1: copy 2 bytes (1 down to 0) |
| 84A2 | .copy_addr_loop←1← 84A9 BPL |
| LDA scout_data,y ; Load remote address byte | |
| 84A5 | STA exec_addr_lo,y ; Store to exec address workspace |
| 84A8 | DEY ; Next byte (descending) |
| 84A9 | BPL copy_addr_loop ; Loop until all 4 bytes copied |
| 84AB | .jmp_send_data_rx_ack |
| JMP send_data_rx_ack ; Enter common data-receive path | |
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. |
|
| 84AE | .svc5_dispatch_lo |
| .rx_imm_poke | |
| LDA #&2e ; Port workspace offset = &3D | |
| 84B0 | STA port_ws_offset ; Store workspace offset lo |
| 84B2 | LDA #&0d ; RX buffer page = &0D |
| 84B4 | STA rx_buf_offset ; Store workspace offset hi |
| 84B6 | 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. |
|
| 84B9 | .rx_imm_machine_type |
| LDA #1 ; Buffer length hi = 1 | |
| 84BB | .set_rx_buf_len_hi←1← 806F LDA |
| STA port_buf_len_hi ; Set buffer length hi | |
| 84BD | LDA #&fc ; Buffer length lo = &FC |
| 84BF | STA port_buf_len ; Set buffer length lo |
| 84C1 | LDA #&cb ; Buffer start lo = &25 |
| 84C3 | STA open_port_buf ; Set port buffer lo |
| 84C5 | LDA #&88 ; Buffer hi = &7F (below screen) |
| 84C7 | STA open_port_buf_hi ; Set port buffer hi |
| 84C9 | 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. |
|
| 84CB | .rx_imm_peek |
| LDA #&2e ; Port workspace offset = &3D | |
| 84CD | STA port_ws_offset ; Store workspace offset lo |
| 84CF | LDA #&0d ; RX buffer page = &0D |
| 84D1 | STA rx_buf_offset ; Store workspace offset hi |
| 84D3 | LDA #2 ; Scout status = 2 (PEEK response) |
| 84D5 | STA rx_port ; Store scout status |
| 84D8 | JSR tx_calc_transfer ; Calculate transfer size for response |
| 84DB | BCC imm_op_discard ; C=0: transfer not set up, discard |
| 84DD | .set_tx_reply_flag←1← 84C9 BNE |
| LDA rx_src_net ; Mark TX flags bit 7 (reply pending) | |
| 84E0 | ORA #&80 ; Set reply pending flag |
| 84E2 | STA rx_src_net ; Store updated TX flags |
| 84E5 | .rx_imm_halt_cont |
| LDA #&44 ; CR1=&44: TIE | TX_LAST_DATA | |
| 84E7 | STA econet_control1_or_status1 ; Write CR1: enable TX interrupts |
| 84EA | .tx_cr2_setup |
| LDA #&a7 ; CR2=&A7: RTS|CLR_RX_ST|FC_TDRA|PSE | |
| 84EC | STA econet_control23_or_status2 ; Write CR2 for TX setup |
| 84EF | .tx_nmi_setup |
| LDA #&0c ; NMI handler lo byte (self-modifying) | |
| 84F1 | LDY #&85 ; Y=&85: NMI handler high byte |
| 84F3 | 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. |
|
| 84F6 | .imm_op_build_reply←1← 83A2 JMP |
| LDA port_buf_len ; Get buffer position for reply header | |
| 84F8 | CLC ; Clear carry for offset addition |
| 84F9 | ADC #&80 ; Data offset = buf_len + &80 (past header) |
| 84FB | LDY #&7f ; Y=&7F: reply data length slot |
| 84FD | STA (net_rx_ptr),y ; Store reply data length in RX buffer |
| 84FF | LDY #&80 ; Y=&80: source station slot |
| 8501 | LDA scout_buf ; Load requesting station number |
| 8504 | STA (net_rx_ptr),y ; Store source station in reply header |
| 8506 | INY ; Y=&81 |
| 8507 | LDA scout_src_net ; Load requesting network number |
| 850A | STA (net_rx_ptr),y ; Store source network in reply header |
| 850C | LDA scout_ctrl ; Load control byte from received frame |
| 850F | .setup_sr_tx←1← 83F2 JMP |
| STA tx_op_type ; Save TX operation type for SR dispatch | |
| 8512 | LDA #&84 ; IER bit 2: disable SR interrupt |
| 8514 | STA system_via_ier ; Write IER to disable SR |
| 8517 | LDA system_via_acr ; Read ACR for shift register config |
| 851A | AND #&1c ; Isolate shift register mode bits (2-4) |
| 851C | STA ws_0d64 ; Save original SR mode for later restore |
| 851F | LDA system_via_acr ; Reload ACR for modification |
| 8522 | AND #&e3 ; Clear SR mode bits (keep other bits) |
| 8524 | ORA #8 ; SR mode 2: shift in under φ2 |
| 8526 | STA system_via_acr ; Apply new shift register mode |
| 8529 | BIT system_via_sr ; Read SR to clear pending interrupt |
| 852C | .imm_op_discard←1← 84DB 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. |
|
| 852F | .advance_buffer_ptr←3← 82A9 JSR← 82B7 JSR← 843D JSR |
| INC port_buf_len ; Increment buffer length low byte | |
| 8531 | BNE return_from_advance_buf ; No overflow: done |
| 8533 | INC port_buf_len_hi ; Increment buffer length high byte |
| 8535 | BNE return_from_advance_buf ; No overflow: done |
| 8537 | INC open_port_buf ; Increment buffer pointer low byte |
| 8539 | BNE return_from_advance_buf ; No overflow: done |
| 853B | INC open_port_buf_hi ; Increment buffer pointer high byte |
| 853D | .return_from_advance_buf←3← 8531 BNE← 8535 BNE← 8539 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 &84BB | |
| ; + 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. | |
| 853E | .tx_done_dispatch_lo |
| EQUB <(tx_done_jsr-1) | |
| 853F | EQUB <(tx_done_econet_event-1) |
| 8540 | EQUB <(tx_done_os_proc-1) |
| 8541 | EQUB <(tx_done_halt-1) |
| 8542 | EQUB <(tx_done_continue-1) |
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. |
|
| 8543 | .tx_done_jsr |
| LDA #&85 ; Hi byte of tx_done_exit-1 | |
| 8545 | PHA ; Push hi byte on stack |
| 8546 | LDA #&84 ; Push lo of (tx_done_exit-1) |
| 8548 | PHA ; Push lo byte on stack |
| 8549 | 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. |
|
| 854C | .tx_done_econet_event |
| LDX exec_addr_lo ; X = remote address lo from l0d66 | |
| 854F | LDA exec_addr_hi ; A = remote address hi from l0d67 |
| 8552 | LDY #event_network_error ; Y = 8: Econet event number |
| 8554 | .tx_done_fire_event |
| JSR oseven ; Generate event Y='Network error' | |
| 8557 | 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. |
|
| 855A | .tx_done_os_proc |
| LDX exec_addr_lo ; X = remote address lo | |
| 855D | LDY exec_addr_hi ; Y = remote address hi |
| 8560 | JSR dir_op_dispatch ; Call ROM entry point at &8000 |
| 8563 | 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. |
|
| 8566 | .tx_done_halt |
| LDA #4 ; A=&04: bit 2 mask for rx_flags | |
| 8568 | BIT econet_flags ; Test if already halted |
| 856B | BNE tx_done_exit ; Already halted: skip to exit |
| 856D | ORA econet_flags ; Set bit 2 in rx_flags |
| 8570 | STA econet_flags ; Store halt flag |
| 8573 | LDA #4 ; A=4: re-load halt bit mask |
| 8575 | CLI ; Enable interrupts during halt wait |
| 8576 | .halt_spin_loop←1← 8579 BNE |
| BIT econet_flags ; Test halt flag | |
| 8579 | BNE halt_spin_loop ; Still halted: keep spinning |
| 857B | 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. |
|
| 857D | .tx_done_continue |
| LDA econet_flags ; Load current RX flags | |
| 8580 | AND #&fb ; Clear bit 2: release halted station |
| 8582 | STA econet_flags ; Store updated flags |
| 8585 | .tx_done_exit←5← 8057 JMP← 8557 JMP← 8563 JMP← 856B BNE← 857B BEQ |
| PLA ; Restore Y from stack | |
| 8586 | TAY ; Transfer to Y register |
| 8587 | PLA ; Restore X from stack |
| 8588 | TAX ; Transfer to X register |
| 8589 | LDA #0 ; A=0: success status |
| 858B | 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. |
|
| 858C | .tx_begin←3← 98D6 JSR← A5BE JMP← A89A JSR |
| TXA ; Save X on stack | |
| 858D | PHA ; Push X |
| 858E | LDY #2 ; Y=2: TXCB offset for dest station |
| 8590 | LDA (nmi_tx_block),y ; Load dest station from TX control block |
| 8592 | STA tx_dst_stn ; Store to TX scout buffer |
| 8595 | INY ; Y=&03 |
| 8596 | LDA (nmi_tx_block),y ; Load dest network from TX control block |
| 8598 | STA tx_dst_net ; Store to TX scout buffer |
| 859B | LDY #0 ; Y=0: first byte of TX control block |
| 859D | LDA (nmi_tx_block),y ; Load control/flag byte |
| 859F | BMI tx_imm_op_setup ; Bit7 set: immediate operation ctrl byte |
| 85A1 | JMP tx_active_start ; Bit7 clear: normal data transfer |
| 85A4 | .tx_imm_op_setup←1← 859F BMI |
| STA tx_ctrl_byte ; Store control byte to TX scout buffer | |
| 85A7 | TAX ; X = control byte for range checks |
| 85A8 | INY ; Y=1: port byte offset |
| 85A9 | LDA (nmi_tx_block),y ; Load port byte from TX control block |
| 85AB | STA tx_port ; Store port byte to TX scout buffer |
| 85AE | BNE tx_line_idle_check ; Port != 0: skip immediate op setup |
| 85B0 | CPX #&83 ; Ctrl < &83: PEEK/POKE need address calc |
| 85B2 | BCS tx_ctrl_range_check ; Ctrl >= &83: skip to range check |
| 85B4 | SEC ; SEC: init borrow for 4-byte subtract |
| 85B5 | PHP ; Save carry on stack for loop |
| 85B6 | LDY #8 ; Y=8: high pointer offset in TXCB |
| 85B8 | .calc_peek_poke_size←1← 85CC BCC |
| LDA (nmi_tx_block),y ; Load TXCB[Y] (end addr byte) | |
| 85BA | DEY ; Y -= 4: back to start addr offset |
| 85BB | DEY ; (continued) |
| 85BC | DEY ; (continued) |
| 85BD | DEY ; (continued) |
| 85BE | PLP ; Restore borrow from stack |
| 85BF | SBC (nmi_tx_block),y ; end - start = transfer size byte |
| 85C1 | STA tx_data_start,y ; Store result to tx_data_start |
| 85C4 | INY ; Y += 5: advance to next end byte |
| 85C5 | INY ; (continued) |
| 85C6 | INY ; (continued) |
| 85C7 | INY ; (continued) |
| 85C8 | INY ; (continued) |
| 85C9 | PHP ; Save borrow for next byte |
| 85CA | CPY #&0c ; Done all 4 bytes? (Y reaches &0C) |
| 85CC | BCC calc_peek_poke_size ; No: next byte pair |
| 85CE | PLP ; Discard final borrow |
| 85CF | .tx_ctrl_range_check←1← 85B2 BCS |
| CPX #&81 ; Ctrl < &81: not an immediate op | |
| 85D1 | BCC tx_active_start ; Below range: normal data transfer |
| 85D3 | .check_imm_range |
| CPX #&89 ; Ctrl >= &89: out of immediate range | |
| 85D5 | BCS tx_active_start ; Above range: normal data transfer |
| 85D7 | LDY #&0c ; Y=&0C: start of extra data in TXCB |
| 85D9 | .copy_imm_params←1← 85E1 BCC |
| LDA (nmi_tx_block),y ; Load extra parameter byte from TXCB | |
| 85DB | STA imm_param_base,y ; Copy to NMI shim workspace at &0D1A+Y |
| 85DE | INY ; Next byte |
| 85DF | CPY #&10 ; Done 4 bytes? (Y reaches &10) |
| 85E1 | BCC copy_imm_params ; No: continue copying |
| 85E3 | .tx_line_idle_check←1← 85AE BNE |
| LDA #&20 ; A=&20: mask for SR2 INACTIVE bit | |
| 85E5 | BIT econet_control23_or_status2 ; BIT SR2: test if line is idle |
| 85E8 | BNE tx_no_clock_error ; Line not idle: handle as line jammed |
| 85EA | LDA #&fd ; A=&FD: high byte of timeout counter |
| 85EC | PHA ; Push timeout high byte to stack |
| 85ED | LDA #6 ; Scout frame = 6 address+ctrl bytes |
| 85EF | STA rx_ctrl ; Store scout frame length |
| 85F2 | 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. |
|
| 85F4 | .inactive_poll |
| STA rx_remote_addr ; Save TX index | |
| 85F7 | PHA ; Push timeout byte 1 on stack |
| 85F8 | PHA ; Push timeout byte 2 on stack |
| 85F9 | LDY #&e7 ; Y=&E7: CR2 value for TX prep (RTS|CLR_TX_ST|CLR_RX_ST|FC_TDRA|2_1_ BYTE|PSE) |
| 85FB | .reload_inactive_mask←3← 8621 BNE← 8626 BNE← 862B BNE |
| LDA #4 ; A=&04: INACTIVE bit mask for SR2 test | |
| 85FD | .test_inactive_retry |
| PHP ; Save interrupt state | |
| 85FE | 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. |
|
| 85FF | .intoff_test_inactive |
| BIT station_id_disable_net_nmis ; INTOFF -- disable NMIs | |
| 8602 | BIT station_id_disable_net_nmis ; INTOFF again (belt-and-braces) |
| 8605 | .test_line_idle |
| BIT econet_control23_or_status2 ; BIT SR2: Z = &04 AND SR2 -- tests INACTIVE | |
| 8608 | BEQ inactive_retry ; INACTIVE not set -- re-enable NMIs and loop |
| 860A | LDA econet_control1_or_status1 ; Read SR1 (acknowledge pending interrupt) |
| 860D | LDA #&67 ; CR2=&67: CLR_TX_ST|CLR_RX_ST|FC_ TDRA|2_1_BYTE|PSE |
| 860F | STA econet_control23_or_status2 ; Write CR2: clear status, prepare TX |
| 8612 | LDA #&10 ; A=&10: CTS mask for SR1 bit4 |
| 8614 | BIT econet_control1_or_status1 ; BIT SR1: tests CTS present |
| 8617 | BNE tx_prepare ; CTS set -- clock hardware detected, start TX |
| 8619 | .inactive_retry←1← 8608 BEQ |
| BIT video_ula_control ; INTON -- re-enable NMIs (&FE20 read) | |
| 861C | PLP ; Restore interrupt state |
| 861D | TSX ; 3-byte timeout counter on stack |
| 861E | INC error_text,x ; Increment timeout counter byte 1 |
| 8621 | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test |
| 8623 | INC stack_page_2,x ; Increment timeout counter byte 2 |
| 8626 | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test |
| 8628 | INC stack_page_3,x ; Increment timeout counter byte 3 |
| 862B | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test |
| 862D | BEQ tx_line_jammed ; ALWAYS branch |
| 862F | .tx_active_start←3← 85A1 JMP← 85D1 BCC← 85D5 BCS |
| LDA #&44 ; CR1=&44: TIE | TX_LAST_DATA | |
| 8631 | 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. |
|
| 8633 | .tx_line_jammed←1← 862D BEQ |
| LDA #7 ; CR2=&07: FC_TDRA | 2_1_BYTE | PSE (abort TX) | |
| 8635 | STA econet_control23_or_status2 ; Write CR2 to abort TX |
| 8638 | PLA ; Clean 3 bytes of timeout loop state |
| 8639 | PLA ; Pop saved register |
| 863A | PLA ; Pop saved register |
| 863B | LDA #&40 ; Error &40 = 'Line Jammed' |
| 863D | BNE store_tx_error ; ALWAYS branch to shared error handler ALWAYS branch |
| 863F | .tx_no_clock_error←1← 85E8 BNE |
| LDA #&43 ; Error &43 = 'No Clock' | |
| 8641 | .store_tx_error←2← 8631 BNE← 863D BNE |
| LDY #0 ; Offset 0 = error byte in TX control block | |
| 8643 | STA (nmi_tx_block),y ; Store error code in TX CB byte 0 |
| 8645 | LDA #&80 ; &80 = TX complete flag |
| 8647 | STA ws_0d60 ; Signal TX operation complete |
| 864A | PLA ; Restore X saved by caller |
| 864B | TAX ; Move to X register |
| 864C | 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. |
|
| 864D | .tx_prepare←1← 8617 BNE |
| STY econet_control23_or_status2 ; Write CR2 = Y (&E7: RTS|CLR_TX_ST|CLR_RX_ST| FC_TDRA|2_1_BYTE|PSE) | |
| 8650 | LDX #&44 ; CR1=&44: RX_RESET | TIE (TX active, TX interrupts enabled) |
| 8652 | STX econet_control1_or_status1 ; Write to ADLC CR1 |
| 8655 | LDX #&ea ; Install NMI handler at &86E0 (TX data handler) |
| 8657 | LDY #&86 ; High byte of NMI handler address |
| 8659 | STX nmi_jmp_lo ; Write NMI vector low byte directly |
| 865C | STY nmi_jmp_hi ; Write NMI vector high byte directly |
| 865F | SEC ; Set need_release_tube flag (SEC/ROR = bit7) |
| 8660 | ROR prot_flags ; Rotate carry into bit 7 of flag |
| 8662 | BIT video_ula_control ; INTON -- NMIs now fire for TDRA (&FE20 read) |
| 8665 | LDA tx_port ; Load destination port number |
| 8668 | BNE setup_data_xfer ; Port != 0: standard data transfer |
| 866A | LDY tx_ctrl_byte ; Port 0: load control byte for table lookup |
| 866D | LDA tube_tx_sr1_operand,y ; Look up tx_flags from table |
| 8670 | STA rx_src_net ; Store operation flags |
| 8673 | LDA tube_tx_inc_operand,y ; Look up tx_length from table |
| 8676 | STA rx_ctrl ; Store expected transfer length |
| 8679 | LDA #&86 ; Push high byte of return address (&9C) |
| 867B | PHA ; Push high byte for PHA/PHA/RTS dispatch |
| 867C | LDA intoff_disable_nmi_op,y ; Look up handler address low from table |
| 867F | PHA ; Push low byte for PHA/PHA/RTS dispatch |
| 8680 | 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 &867C 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. | |
| 8681 | .tx_ctrl_dispatch_lo |
| EQUB <(tx_ctrl_peek-1) | |
| 8682 | EQUB <(tx_ctrl_poke-1) |
| 8683 | EQUB <(proc_op_status2-1) |
| 8684 | EQUB <(proc_op_status2-1) |
| 8685 | EQUB <(proc_op_status2-1) |
| 8686 | EQUB <(tx_ctrl_exit-1) |
| 8687 | EQUB <(tx_ctrl_exit-1) |
| 8688 | EQUB <(tx_ctrl_machine_type-1) |
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). |
|
| 8689 | .tx_ctrl_machine_type |
| LDA #3 ; scout_status=3 (machine type query) | |
| 868B | 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. |
|
| 868D | .tx_ctrl_peek |
| LDA #3 ; A=3: scout_status for PEEK op | |
| 868F | 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. |
|
| 8691 | .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.
|
||||
| 8693 | .tx_ctrl_store_and_add←1← 868F BNE | |||
| STA rx_port ; Store scout status | ||||
| 8696 | CLC ; Clear carry for 4-byte addition | |||
| 8697 | PHP ; Save carry on stack | |||
| 8698 | LDY #&0c ; Y=&0C: start at offset 12 | |||
| 869A | .add_bytes_loop←1← 86A7 BCC | |||
| LDA tx_addr_base,y ; Load workspace address byte | ||||
| 869D | PLP ; Restore carry from previous byte | |||
| 869E | ADC (nmi_tx_block),y ; Add TXCB address byte | |||
| 86A0 | STA tx_addr_base,y ; Store updated address byte | |||
| 86A3 | INY ; Next byte | |||
| 86A4 | 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. |
|
| 86A5 | .tx_ctrl_proc |
| CPY #&10 ; Compare Y with 16-byte boundary | |
| 86A7 | BCC add_bytes_loop ; Below boundary: continue addition |
| 86A9 | PLP ; Restore processor flags |
| 86AA | BNE skip_buf_setup ; Skip buffer setup if transfer size is zero |
| 86AC | .setup_data_xfer←1← 8668 BNE |
| LDA tx_dst_stn ; Load dest station for broadcast check | |
| 86AF | AND tx_dst_net ; AND with dest network |
| 86B2 | CMP #&ff ; Both &FF = broadcast address? |
| 86B4 | BNE setup_unicast_xfer ; Not broadcast: unicast path |
| 86B6 | LDA #&0e ; Broadcast scout: 14 bytes total |
| 86B8 | STA rx_ctrl ; Store broadcast scout length |
| 86BB | LDA #&40 ; A=&40: broadcast flag |
| 86BD | STA rx_src_net ; Set broadcast flag in tx_flags |
| 86C0 | LDY #4 ; Y=4: start of address data in TXCB |
| 86C2 | .copy_bcast_addr←1← 86CA BCC |
| LDA (nmi_tx_block),y ; Copy TXCB address bytes to scout buffer | |
| 86C4 | STA tx_src_stn,y ; Store to TX source/data area |
| 86C7 | INY ; Next byte |
| 86C8 | CPY #&0c ; Done 8 bytes? (Y reaches &0C) |
| 86CA | BCC copy_bcast_addr ; No: continue copying |
| 86CC | BCS tx_ctrl_exit ; ALWAYS branch |
| 86CE | .setup_unicast_xfer←1← 86B4 BNE |
| LDA #0 ; A=0: clear flags for unicast | |
| 86D0 | STA rx_src_net ; Clear tx_flags |
| 86D3 | .proc_op_status2 |
| LDA #2 ; scout_status=2: data transfer pending | |
| 86D5 | .store_status_copy_ptr←1← 868B BNE |
| STA rx_port ; Store scout status | |
| 86D8 | .skip_buf_setup←1← 86AA BNE |
| LDA nmi_tx_block ; Copy TX block pointer to workspace ptr | |
| 86DA | STA port_ws_offset ; Store low byte |
| 86DC | LDA nmi_tx_block_hi ; Copy TX block pointer high byte |
| 86DE | STA rx_buf_offset ; Store high byte |
| 86E0 | JSR tx_calc_transfer ; Calculate transfer size from RXCB |
| 86E3 | .tx_ctrl_exit←1← 86CC BCS |
| PLP ; Restore processor status from stack | |
| 86E4 | PLA ; Restore stacked registers (4 PLAs) |
| 86E5 | PLA ; Second PLA |
| 86E6 | PLA ; Third PLA |
| 86E7 | PLA ; Fourth PLA |
| 86E8 | TAX ; Restore X from A |
| 86E9 | 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. |
|
| 86EA | .nmi_tx_data |
| LDY rx_remote_addr ; Load TX buffer index | |
| 86ED | BIT econet_control1_or_status1 ; BIT SR1: V=bit6(TDRA), N=bit7(IRQ) |
| 86F0 | .tx_fifo_write←1← 870B BMI |
| BVC tx_fifo_not_ready ; TDRA not set -- TX error | |
| 86F2 | LDA tx_dst_stn,y ; Load byte from TX buffer |
| 86F5 | STA econet_data_continue_frame ; Write to TX_DATA (continue frame) |
| 86F8 | INY ; Next TX buffer byte |
| 86F9 | LDA tx_dst_stn,y ; Load second byte from TX buffer |
| 86FC | INY ; Advance TX index past second byte |
| 86FD | STY rx_remote_addr ; Save updated TX buffer index |
| 8700 | STA econet_data_continue_frame ; Write second byte to TX_DATA |
| 8703 | CPY rx_ctrl ; Compare index to TX length |
| 8706 | BCS tx_last_data ; Frame complete -- go to TX_LAST_DATA |
| 8708 | BIT econet_control1_or_status1 ; Check if we can send another pair |
| 870B | BMI tx_fifo_write ; IRQ set -- send 2 more bytes (tight loop) |
| 870D | JMP nmi_rti ; RTI -- wait for next NMI |
| 8710 | .tx_error←1← 8753 BEQ |
| LDA #&42 ; Error &42 | |
| 8712 | BNE tx_store_error ; ALWAYS branch |
| 8714 | .tx_fifo_not_ready←1← 86F0 BVC |
| LDA #&67 ; CR2=&67: clear status, return to listen | |
| 8716 | STA econet_control23_or_status2 ; Write CR2: clear status, idle listen |
| 8719 | LDA #&41 ; Error &41 (TDRA not ready) |
| 871B | .tx_store_error←1← 8712 BNE |
| LDY station_id_disable_net_nmis ; INTOFF (also loads station ID) | |
| 871E | .delay_nmi_disable←1← 8721 BNE |
| PHA ; PHA/PLA delay loop (256 iterations for NMI disable) | |
| 871F | PLA ; PHA/PLA delay (~7 cycles each) |
| 8720 | INY ; Increment delay counter |
| 8721 | BNE delay_nmi_disable ; Loop 256 times for NMI disable |
| 8723 | 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) |
|
| 8726 | .tx_last_data←1← 8706 BCS |
| LDA #&3f ; CR2=&3F: TX_LAST_DATA | CLR_RX_ST | FLAG_IDLE | FC_TDRA | 2_1_BYTE | PSE | |
| 8728 | STA econet_control23_or_status2 ; Write to ADLC CR2 |
| 872B | LDA #&32 ; Install NMI handler at &8728 (TX completion) |
| 872D | LDY #&87 ; High byte of handler address |
| 872F | 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 &8878 - Otherwise -> install nmi_reply_scout at &8744 |
|
| 8732 | .nmi_tx_complete |
| LDA #&82 ; Jump to error handler | |
| 8734 | STA econet_control1_or_status1 ; Write CR1 to switch from TX to RX |
| 8737 | BIT rx_src_net ; Test workspace flags |
| 873A | BVC check_handshake_bit ; bit6 not set -- check bit0 |
| 873C | JMP tx_result_ok ; bit6 set -- TX completion |
| 873F | .check_handshake_bit←1← 873A BVC |
| LDA #1 ; A=1: mask for bit0 test | |
| 8741 | BIT rx_src_net ; Test tx_flags bit0 (handshake) |
| 8744 | BEQ install_reply_scout ; bit0 clear: install reply handler |
| 8746 | JMP handshake_await_ack ; bit0 set -- four-way handshake data phase |
| 8749 | .install_reply_scout←1← 8744 BEQ |
| LDA #&4e ; Install RX reply handler at &8744 | |
| 874B | 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). |
|
| 874E | .nmi_reply_scout |
| LDA #1 ; A=&01: AP mask for SR2 | |
| 8750 | BIT econet_control23_or_status2 ; BIT SR2: test AP (Address Present) |
| 8753 | BEQ tx_error ; No AP -- error |
| 8755 | LDA econet_data_continue_frame ; Read first RX byte (destination station) |
| 8758 | CMP station_id_disable_net_nmis ; Compare to our station ID (INTOFF side effect) |
| 875B | BNE reject_reply ; Not our station -- error/reject |
| 875D | LDA #&62 ; Install next handler at &8758 (reply continuation) |
| 875F | 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 (&8779) 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 &8779 without an RTI, avoiding NMI re-entry overhead for short frames where all bytes arrive in quick succession. |
|
| 8762 | .nmi_reply_cont |
| BIT econet_control23_or_status2 ; Read RX byte (destination station) | |
| 8765 | BPL reject_reply ; No RDA -- error |
| 8767 | LDA econet_data_continue_frame ; Read destination network byte |
| 876A | BNE reject_reply ; Non-zero -- network mismatch, error |
| 876C | LDA #&79 ; Install next handler at &8779 (reply validation) |
| 876E | BIT econet_control1_or_status1 ; BIT SR1: test IRQ (N=bit7) -- more data ready? |
| 8771 | BMI nmi_reply_validate ; IRQ set -- fall through to &8779 without RTI |
| 8773 | JMP install_nmi_handler ; IRQ not set -- install handler and RTI |
| 8776 | .reject_reply←7← 875B BNE← 8765 BPL← 876A BNE← 877C BPL← 8784 BNE← 878C BNE← 8793 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 &8779 -- must see data available 2. Read source station at &877E, 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). |
|
| 8779 | .nmi_reply_validate←1← 8771 BMI |
| BIT econet_control23_or_status2 ; BIT SR2: test RDA (bit7). Must be set for valid reply. | |
| 877C | BPL reject_reply ; No RDA -- error (FV masking RDA via PSE would cause this) |
| 877E | LDA econet_data_continue_frame ; Read source station |
| 8781 | CMP tx_dst_stn ; Compare to original TX destination station (&0D20) |
| 8784 | BNE reject_reply ; Mismatch -- not the expected reply, error |
| 8786 | LDA econet_data_continue_frame ; Read source network |
| 8789 | CMP tx_dst_net ; Compare to original TX destination network (&0D21) |
| 878C | BNE reject_reply ; Mismatch -- error |
| 878E | LDA #2 ; A=&02: FV mask for SR2 bit1 |
| 8790 | BIT econet_control23_or_status2 ; BIT SR2: test FV -- frame must be complete |
| 8793 | BEQ reject_reply ; No FV -- incomplete frame, error |
| 8795 | LDA #&a7 ; CR2=&A7: RTS|CLR_TX_ST|FC_TDRA|2_ 1_BYTE|PSE (TX in handshake) |
| 8797 | STA econet_control23_or_status2 ; Write CR2: enable RTS for TX handshake |
| 879A | LDA #&44 ; CR1=&44: RX_RESET | TIE (TX active for scout ACK) |
| 879C | STA econet_control1_or_status1 ; Write CR1: reset RX, enable TX interrupt |
| 879F | LDA #&78 ; Install next handler at &8878 (four-way data phase) into &0D43/&0D44 |
| 87A1 | LDY #&88 ; High byte &88 of next handler address |
| 87A3 | STA saved_nmi_lo ; Store low byte to nmi_next_lo |
| 87A6 | STY saved_nmi_hi ; Store high byte to nmi_next_hi |
| 87A9 | LDA tx_dst_stn ; Load dest station for scout ACK TX |
| 87AC | BIT econet_control1_or_status1 ; BIT SR1: test TDRA (V=bit6) |
| 87AF | BVC tx_check_tdra_ready ; TDRA not ready -- error |
| 87B1 | STA econet_data_continue_frame ; Write dest station to TX FIFO |
| 87B4 | LDA tx_dst_net ; Write dest network to TX FIFO |
| 87B7 | STA econet_data_continue_frame ; Write dest network to TX FIFO |
| 87BA | LDA #&c1 ; Install handler at &87B7 (write src addr for scout ACK) |
| 87BC | LDY #&87 ; High byte &87 of handler address |
| 87BE | 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 &87EE. Installs the chosen handler via set_nmi_vector. Shares the tx_check_tdra entry at &87C7 with ack_tx. |
|
| 87C1 | .nmi_scout_ack_src |
| LDA station_id_disable_net_nmis ; Load our station ID (also INTOFF) | |
| 87C4 | BIT econet_control1_or_status1 ; BIT SR1: test TDRA |
| 87C7 | .tx_check_tdra_ready←1← 87AF BVC |
| BVC data_tx_check_fifo ; TDRA not ready -- error | |
| 87C9 | STA econet_data_continue_frame ; Write our station to TX FIFO |
| 87CC | LDA #0 ; Write network=0 to TX FIFO |
| 87CE | STA econet_data_continue_frame ; Write network byte to TX FIFO |
| 87D1 | .data_tx_begin←1← 8349 JMP |
| LDA #2 ; Test bit 1 of tx_flags | |
| 87D3 | BIT rx_src_net ; Check if immediate-op or data-transfer |
| 87D6 | BNE install_imm_data_nmi ; Bit 1 set: immediate op, use alt handler |
| 87D8 | LDA #&ee ; Install nmi_data_tx at &87EE |
| 87DA | LDY #&87 ; High byte of handler address |
| 87DC | JMP set_nmi_vector ; Install and return via set_nmi_vector |
| 87DF | .install_imm_data_nmi←1← 87D6 BNE |
| LDA #&37 ; Install nmi_imm_data at &8837 | |
| 87E1 | LDY #&88 ; High byte of handler address |
| 87E3 | 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. |
|
| 87E6 | .nmi_data_tx←1← 87F0 BEQ |
| LDY port_buf_len_hi ; Y = buffer offset, resume from last position | |
| 87E8 | BEQ data_tx_last ; No pages left: send final partial page |
| 87EA | LDY port_buf_len ; Load remaining byte count |
| 87EC | BEQ check_tdra_status ; Zero bytes left: skip to TDRA check |
| 87EE | LDY port_buf_len ; Load remaining byte count (alt entry) |
| 87F0 | BEQ nmi_data_tx ; Zero: loop back to top of handler |
| 87F2 | .check_tdra_status←1← 87EC BEQ |
| BIT econet_control1_or_status1 ; BIT SR1: test TDRA (V=bit6) | |
| 87F5 | .data_tx_check_fifo←2← 87C7 BVC← 8818 BMI |
| BVC tube_tx_fifo_write ; TDRA not ready -- error | |
| 87F7 | LDA (open_port_buf),y ; Write data byte to TX FIFO |
| 87F9 | STA econet_data_continue_frame ; Write first byte of pair to FIFO |
| 87FC | INY ; Advance buffer offset |
| 87FD | BNE write_second_tx_byte ; No page crossing |
| 87FF | DEC port_buf_len_hi ; Page crossing: decrement page count |
| 8801 | BEQ data_tx_last ; No pages left: send last data |
| 8803 | INC open_port_buf_hi ; Increment buffer high byte |
| 8805 | .write_second_tx_byte←1← 87FD BNE |
| LDA (open_port_buf),y ; Load second byte of pair | |
| 8807 | STA econet_data_continue_frame ; Write second byte to FIFO |
| 880A | INY ; Advance buffer offset |
| 880B | STY port_buf_len ; Save updated buffer position |
| 880D | BNE check_irq_loop ; No page crossing |
| 880F | DEC port_buf_len_hi ; Page crossing: decrement page count |
| 8811 | BEQ data_tx_last ; No pages left: send last data |
| 8813 | INC open_port_buf_hi ; Increment buffer high byte |
| 8815 | .check_irq_loop←1← 880D BNE |
| BIT econet_control1_or_status1 ; BIT SR1: test IRQ (N=bit7) for tight loop | |
| 8818 | BMI data_tx_check_fifo ; IRQ still set: write 2 more bytes |
| 881A | JMP nmi_rti ; No IRQ: return, wait for next NMI |
| 881D | .data_tx_last←5← 87E8 BEQ← 8801 BEQ← 8811 BEQ← 8850 BEQ← 8866 BEQ |
| LDA #&3f ; CR2=&3F: TX_LAST_DATA (close data frame) | |
| 881F | STA econet_control23_or_status2 ; Write CR2 to close frame |
| 8822 | LDA rx_src_net ; Check tx_flags for next action |
| 8825 | BPL install_saved_handler ; Bit7 clear: error, install saved handler |
| 8827 | LDA #&f5 ; Install discard_reset_listen at &83F5 |
| 8829 | LDY #&83 ; High byte of &83F5 handler |
| 882B | JMP set_nmi_vector ; Set NMI vector and return |
| 882E | .install_saved_handler←1← 8825 BPL |
| LDA saved_nmi_lo ; Load saved next handler low byte | |
| 8831 | LDY saved_nmi_hi ; Load saved next handler high byte |
| 8834 | JMP set_nmi_vector ; Install saved handler and return |
| 8837 | .nmi_data_tx_tube |
| BIT econet_control1_or_status1 ; Tube TX: BIT SR1 test TDRA | |
| 883A | .tube_tx_fifo_write←2← 87F5 BVC← 886B BMI |
| BVC tx_tdra_error ; TDRA not ready -- error | |
| 883C | LDA tube_data_register_3 ; Read byte from Tube R3 |
| 883F | STA econet_data_continue_frame ; Write to TX FIFO |
| 8842 | INC port_buf_len ; Increment 4-byte buffer counter |
| 8844 | BNE write_second_tube_byte ; Low byte didn't wrap |
| 8846 | INC port_buf_len_hi ; Carry into second byte |
| 8848 | BNE write_second_tube_byte ; No further carry |
| 884A | INC open_port_buf ; Carry into third byte |
| 884C | BNE write_second_tube_byte ; No further carry |
| 884E | INC open_port_buf_hi ; Carry into fourth byte |
| 8850 | BEQ data_tx_last ; Counter wrapped to zero: last data |
| 8852 | .write_second_tube_byte←3← 8844 BNE← 8848 BNE← 884C BNE |
| LDA tube_data_register_3 ; Read second Tube byte from R3 | |
| 8855 | STA econet_data_continue_frame ; Write second byte to TX FIFO |
| 8858 | INC port_buf_len ; Increment 4-byte counter (second byte) |
| 885A | BNE check_tube_irq_loop ; Low byte didn't wrap |
| 885C | .tube_tx_inc_byte2 |
| INC port_buf_len_hi ; Carry into second byte | |
| 885E | BNE check_tube_irq_loop ; No further carry |
| 8860 | .tube_tx_inc_byte3 |
| INC open_port_buf ; Carry into third byte | |
| 8862 | BNE check_tube_irq_loop ; No further carry |
| 8864 | .tube_tx_inc_byte4 |
| INC open_port_buf_hi ; Carry into fourth byte | |
| 8866 | BEQ data_tx_last ; Counter wrapped to zero: last data |
| 8868 | .check_tube_irq_loop←3← 885A BNE← 885E BNE← 8862 BNE |
| BIT econet_control1_or_status1 ; BIT SR1: test IRQ for tight loop | |
| 886B | BMI tube_tx_fifo_write ; IRQ still set: write 2 more bytes |
| 886D | JMP nmi_rti ; No IRQ: return, wait for next NMI |
| 8870 | .tx_tdra_error←1← 883A BVC |
| LDA rx_src_net ; TX error: check flags for path | |
| 8873 | BPL tx_result_fail ; Bit7 clear: TX result = not listening |
| 8875 | JMP discard_reset_rx ; Bit7 set: discard and return to listen |
Four-way handshake: switch to RX for final ACK |
|
| 8878 | .handshake_await_ack←1← 8746 JMP |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (switch to RX for final ACK) | |
| 887A | STA econet_control1_or_status1 ; Write to ADLC CR1 |
| 887D | LDA #&84 ; Install nmi_final_ack handler |
| 887F | LDY #&88 ; High byte of handler address |
| 8881 | 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 (&874E-&8779): &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. |
|
| 8884 | .nmi_final_ack |
| LDA #1 ; A=&01: AP mask | |
| 8886 | BIT econet_control23_or_status2 ; BIT SR2: test AP |
| 8889 | BEQ tx_result_fail ; No AP -- error |
| 888B | LDA econet_data_continue_frame ; Read dest station |
| 888E | CMP station_id_disable_net_nmis ; Compare to our station (INTOFF side effect) |
| 8891 | BNE tx_result_fail ; Not our station -- error |
| 8893 | LDA #&98 ; Install nmi_final_ack_net handler |
| 8895 | JMP install_nmi_handler ; Install continuation handler |
| 8898 | .nmi_final_ack_net |
| BIT econet_control23_or_status2 ; BIT SR2: test RDA | |
| 889B | BPL tx_result_fail ; No RDA -- error |
| 889D | LDA econet_data_continue_frame ; Read dest network |
| 88A0 | BNE tx_result_fail ; Non-zero -- network mismatch, error |
| 88A2 | LDA #&ac ; Install nmi_final_ack_validate handler |
| 88A4 | BIT econet_control1_or_status1 ; BIT SR1: test IRQ -- more data ready? |
| 88A7 | BMI nmi_final_ack_validate ; IRQ set -- fall through to validate |
| 88A9 | 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. |
|
| 88AC | .nmi_final_ack_validate←1← 88A7 BMI |
| BIT econet_control23_or_status2 ; BIT SR2: test RDA | |
| 88AF | BPL tx_result_fail ; No RDA -- error |
| 88B1 | LDA econet_data_continue_frame ; Read source station |
| 88B4 | CMP tx_dst_stn ; Compare to TX dest station (&0D20) |
| 88B7 | BNE tx_result_fail ; Mismatch -- error |
| 88B9 | LDA econet_data_continue_frame ; Read source network |
| 88BC | CMP tx_dst_net ; Compare to TX dest network (&0D21) |
| 88BF | BNE tx_result_fail ; Mismatch -- error |
| 88C1 | LDA rx_src_net ; Load TX flags for next action |
| 88C4 | BPL check_fv_final_ack ; bit7 clear: no data phase |
| 88C6 | JMP install_data_rx_handler ; Install data RX handler |
| 88C9 | .check_fv_final_ack←1← 88C4 BPL |
| LDA #2 ; A=&02: FV mask for SR2 bit1 | |
| 88CB | BIT econet_control23_or_status2 ; BIT SR2: test FV -- frame must be complete |
| 88CE | 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. |
|
| 88D0 | .tx_result_ok←2← 82F7 JMP← 873C JMP |
| LDA #0 ; A=0: success result code | |
| 88D2 | 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. |
|
| 88D4 | .tx_result_fail←11← 823B JMP← 8776 JMP← 8873 BPL← 8889 BEQ← 8891 BNE← 889B BPL← 88A0 BNE← 88AF BPL← 88B7 BNE← 88BF BNE← 88CE 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.
|
||||
| 88D6 | .tx_store_result←2← 8723 JMP← 88D2 BEQ | |||
| LDY #0 ; Y=0: index into TX control block | ||||
| 88D8 | STA (nmi_tx_block),y ; Store result/error code at (nmi_tx_block),0 | |||
| 88DA | LDA #&80 ; &80: completion flag for &0D3A | |||
| 88DC | STA ws_0d60 ; Signal TX complete | |||
| 88DF | JMP discard_reset_rx ; Full ADLC reset and return to idle listen | |||
| ; Unreferenced dead data (16 bytes) | ||||
| ; 16 bytes between JMP discard_reset_rx (&88DF) and | ||||
| ; tx_calc_transfer (&88F2). Unreachable as code (after | ||||
| ; an unconditional JMP) and unreferenced as data. No | ||||
| ; label, index, or indirect pointer targets any address | ||||
| ; in the &88E2-&88F1 range. Likely unused remnant from | ||||
| ; development. | ||||
| 88E2 | EQUB &0E ; Dead data: &0E | |||
| 88E3 | EQUB &0E ; Dead data: &0E | |||
| 88E4 | EQUB &0A ; Dead data: &0A | |||
| 88E5 | EQUB &0A ; Dead data: &0A | |||
| 88E6 | EQUB &0A ; Dead data: &0A | |||
| 88E7 | EQUB &06 ; Dead data: &06 | |||
| 88E8 | EQUB &06 ; Dead data: &06 | |||
| 88E9 | EQUB &0A ; Dead data: &0A | |||
| 88EA | EQUB &81 ; Dead data: &81 | |||
| 88EB | EQUB &00 ; Dead data: &00 | |||
| 88EC | EQUB &00 ; Dead data: &00 | |||
| 88ED | EQUB &00 ; Dead data: &00 | |||
| 88EE | EQUB &00 ; Dead data: &00 | |||
| 88EF | EQUB &01 ; Dead data: &01 | |||
| 88F0 | EQUB &01 ; Dead data: &01 | |||
| 88F1 | 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. |
|
| 88F2 | .tx_calc_transfer←3← 81BF JSR← 84D8 JSR← 86E0 JSR |
| LDY #7 ; Y=7: offset to RXCB buffer addr byte 3 | |
| 88F4 | LDA (port_ws_offset),y ; Read RXCB[7] (buffer addr high byte) |
| 88F6 | CMP #&ff ; Compare to &FF |
| 88F8 | BNE check_tx_in_progress ; Not &FF: normal buffer, skip Tube check |
| 88FA | DEY ; Y=&06 |
| 88FB | LDA (port_ws_offset),y ; Read RXCB[6] (buffer addr byte 2) |
| 88FD | CMP #&fe ; Check if addr byte 2 >= &FE (Tube range) |
| 88FF | BCS fallback_calc_transfer ; Tube/IO address: use fallback path |
| 8901 | .check_tx_in_progress←1← 88F8 BNE |
| LDA tube_present ; Transmit in progress? | |
| 8904 | BEQ fallback_calc_transfer ; No: fallback path |
| 8906 | LDA rx_src_net ; Load TX flags for transfer setup |
| 8909 | ORA #2 ; Set bit 1 (transfer complete) |
| 890B | STA rx_src_net ; Store with bit 1 set (Tube xfer) |
| 890E | SEC ; Init borrow for 4-byte subtract |
| 890F | PHP ; Save carry on stack |
| 8910 | LDY #4 ; Y=4: start at RXCB offset 4 |
| 8912 | .calc_transfer_size←1← 8924 BCC |
| LDA (port_ws_offset),y ; Load RXCB[Y] (current ptr byte) | |
| 8914 | INY ; Y += 4: advance to RXCB[Y+4] |
| 8915 | INY ; (continued) |
| 8916 | INY ; (continued) |
| 8917 | INY ; (continued) |
| 8918 | PLP ; Restore borrow from previous byte |
| 8919 | SBC (port_ws_offset),y ; Subtract RXCB[Y+4] (start ptr byte) |
| 891B | STA net_tx_ptr,y ; Store result byte |
| 891E | DEY ; Y -= 3: next source byte |
| 891F | DEY ; (continued) |
| 8920 | DEY ; (continued) |
| 8921 | PHP ; Save borrow for next byte |
| 8922 | CPY #8 ; Done all 4 bytes? |
| 8924 | BCC calc_transfer_size ; No: next byte pair |
| 8926 | PLP ; Discard final borrow |
| 8927 | TXA ; Save X |
| 8928 | PHA ; Save X |
| 8929 | LDA #4 ; Compute address of RXCB+4 |
| 892B | CLC ; CLC for base pointer addition |
| 892C | ADC port_ws_offset ; Add RXCB base to get RXCB+4 addr |
| 892E | TAX ; X = low byte of RXCB+4 |
| 892F | LDY rx_buf_offset ; Y = high byte of RXCB ptr |
| 8931 | LDA #&c2 ; Tube claim type &C2 |
| 8933 | JSR tube_addr_data_dispatch ; Claim Tube transfer address |
| 8936 | BCC restore_x_and_return ; No Tube: skip reclaim |
| 8938 | LDA rx_port ; Tube: reclaim with scout status |
| 893B | JSR tube_addr_data_dispatch ; Reclaim with scout status type |
| 893E | JSR release_tube ; Release Tube claim after reclaim |
| 8941 | SEC ; C=1: Tube address claimed |
| 8942 | .restore_x_and_return←1← 8936 BCC |
| PLA ; Restore X | |
| 8943 | TAX ; Restore X from stack |
| 8944 | RTS ; Return with C = transfer status |
| 8945 | .fallback_calc_transfer←2← 88FF BCS← 8904 BEQ |
| LDY #4 ; Y=4: RXCB current pointer offset | |
| 8947 | LDA (port_ws_offset),y ; Load RXCB[4] (current ptr lo) |
| 8949 | LDY #8 ; Y=8: RXCB start address offset |
| 894B | SEC ; Set carry for subtraction |
| 894C | SBC (port_ws_offset),y ; Subtract RXCB[8] (start ptr lo) |
| 894E | STA port_buf_len ; Store transfer size lo |
| 8950 | LDY #5 ; Y=5: current ptr hi offset |
| 8952 | LDA (port_ws_offset),y ; Load RXCB[5] (current ptr hi) |
| 8954 | SBC #0 ; Propagate borrow only |
| 8956 | STA open_port_buf_hi ; Temp store of adjusted hi byte |
| 8958 | LDY #8 ; Y=8: start address lo offset |
| 895A | LDA (port_ws_offset),y ; Copy RXCB[8] to open port buffer lo |
| 895C | STA open_port_buf ; Store to scratch (side effect) |
| 895E | LDY #9 ; Y=9: start address hi offset |
| 8960 | LDA (port_ws_offset),y ; Load RXCB[9] |
| 8962 | SEC ; Set carry for subtraction |
| 8963 | SBC open_port_buf_hi ; Subtract adjusted hi byte |
| 8965 | STA port_buf_len_hi ; Store transfer size hi |
| 8967 | SEC ; Return with C=1 |
| 8968 | .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. |
|
| 8969 | .adlc_full_reset←3← 8077 JSR← 8104 JSR← 823E JSR |
| LDA #&c1 ; CR1=&C1: TX_RESET | RX_RESET | AC (both sections in reset, address control set) | |
| 896B | STA econet_control1_or_status1 ; Write CR1 to ADLC register 0 |
| 896E | LDA #&1e ; CR4=&1E (via AC=1): 8-bit RX word length, abort extend enabled, NRZ encoding |
| 8970 | STA econet_data_terminate_frame ; Write CR4 to ADLC register 3 |
| 8973 | LDA #0 ; CR3=&00 (via AC=1): no loop-back, no AEX, NRZ, no DTR |
| 8975 | 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. |
|
| 8978 | .adlc_rx_listen←2← 83F8 JSR← 89A4 JMP |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (TX in reset, RX interrupts enabled) | |
| 897A | STA econet_control1_or_status1 ; Write to ADLC CR1 |
| 897D | LDA #&67 ; CR2=&67: CLR_TX_ST | CLR_RX_ST | FC_TDRA | 2_1_BYTE | PSE |
| 897F | STA econet_control23_or_status2 ; Write to ADLC CR2 |
| 8982 | 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 (&80BE). When the NMI handler returns to idle, falls through to save_econet_state to clear the initialised flags and re-enter RX listen mode. |
|
| 8983 | .wait_idle_and_reset |
| BIT ws_0d62 ; Check if Econet has been initialised | |
| 8986 | BPL reset_enter_listen ; Not initialised: skip to RX listen |
| 8988 | .poll_nmi_idle←2← 898D BNE← 8994 BNE |
| LDA nmi_jmp_lo ; Read current NMI handler low byte | |
| 898B | CMP #&be ; Expected: &B3 (nmi_rx_scout low) |
| 898D | BNE poll_nmi_idle ; Not idle: spin and wait |
| 898F | LDA nmi_jmp_hi ; Read current NMI handler high byte |
| 8992 | EOR #&80 ; Test if high byte = &80 (page of nmi_rx_scout) |
| 8994 | 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. |
|
| 8996 | .save_econet_state |
| BIT station_id_disable_net_nmis ; INTOFF: disable NMIs | |
| 8999 | BIT station_id_disable_net_nmis ; INTOFF again (belt-and-braces) |
| 899C | STA ws_0d60 ; TX not in progress |
| 899F | STA ws_0d62 ; Econet not initialised |
| 89A2 | LDY #5 ; Y=5: service call workspace page |
| 89A4 | .reset_enter_listen←1← 8986 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 (&80BE). 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 &80BE. |
|
| 89A7 | .nmi_bootstrap_entry |
| BIT station_id_disable_net_nmis ; INTOFF: disable NMIs while switching ROM | |
| 89AA | PHA ; Save A |
| 89AB | TYA ; Transfer Y to A |
| 89AC | PHA ; Save Y (via A) |
| 89AD | LDA #0 ; ROM bank 0 (patched during init for actual bank) |
| 89AF | STA romsel ; Select Econet ROM bank via ROMSEL |
| 89B2 | JMP nmi_rx_scout ; Jump to scout handler in ROM |
ROM copy of set_nmi_vector + nmi_rti |
|
| 89B5 | .rom_set_nmi_vector |
| STY nmi_jmp_hi ; Store handler high byte at &0D0D | |
| 89B8 | STA nmi_jmp_lo ; Store handler low byte at &0D0C |
| 89BB | LDA romsel_copy ; Restore NFS ROM bank |
| 89BD | STA romsel ; Page in via hardware latch |
| 89C0 | PLA ; Restore Y from stack |
| 89C1 | TAY ; Transfer ROM bank to Y |
| 89C2 | PLA ; Restore A from stack |
| 89C3 | BIT video_ula_control ; INTON: re-enable NMIs |
| 89C6 | RTI ; Return from interrupt |
| ; Unreferenced dead data (3 bytes) | |
| ; 3 bytes between the RTI at &89C6 (end of the NMI | |
| ; shim ROM source) and svc_dispatch_lo at &89CA. | |
| ; The init copy loop (Y=1..&20) copies &89A7-&89C6 | |
| ; to &0D00-&0D1F; these bytes are outside that range | |
| ; and unreferenced. Likely unused development remnant. | |
| 89C7 | EQUB &01 ; Dead data: &01 |
| 89C8 | EQUB &00 ; Dead data: &00 |
| 89C9 | EQUB &18 |
| ; 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). | |
| 89CA | .svc_dispatch_lo←1← 8E52 LDA |
| EQUB &04 ; lo - dummy entry (outside ROM range) | |
| 89CB | EQUB <(dispatch_rts-1) ; lo - Svc 0: already claimed (no-op) |
| 89CC | EQUB <(svc_1_abs_workspace-1) ; lo - Svc 1: absolute workspace |
| 89CD | EQUB <(svc_2_private_workspace-1) ; lo - Svc 2: private workspace |
| 89CE | EQUB <(svc_3_autoboot-1) ; lo - Svc 3: auto-boot |
| 89CF | EQUB <(svc_4_star_command-1) ; lo - Svc 4: unrecognised star command |
| 89D0 | EQUB <(svc5_irq_check-1) ; lo - Svc 5: unrecognised interrupt |
| 89D1 | EQUB <(dispatch_rts-1) ; lo - Svc 6: BRK (no-op) |
| 89D2 | EQUB <(svc_7_osbyte-1) ; lo - Svc 7: unrecognised OSBYTE |
| 89D3 | EQUB <(svc_8_osword-1) ; lo - Svc 8: unrecognised OSWORD |
| 89D4 | EQUB <(svc_9_help-1) ; lo - Svc 9: *HELP |
| 89D5 | EQUB <(dispatch_rts-1) ; lo - Svc 10: static workspace (no-op) |
| 89D6 | EQUB <(econet_restore-1) ; lo - Svc 11: NMI release (reclaim NMIs) |
| 89D7 | EQUB <(wait_idle_and_reset-1) ; lo - Svc 12: NMI claim (save NMI state) |
| 89D8 | EQUB <(svc_18_fs_select-1) ; lo - Svc 18: filing system selection |
| 89D9 | EQUB <(lang_0_insert_remote_key-1) ; lo - Lang 0: no language / Tube |
| 89DA | EQUB <(lang_1_remote_boot-1) ; lo - Lang 1: normal startup |
| 89DB | EQUB <(lang_2_save_palette_vdu-1) ; lo - Lang 2: softkey byte (Electron) |
| 89DC | EQUB <(lang_3_execute_at_0100-1) ; lo - Lang 3: softkey length (Electron) |
| 89DD | EQUB <(lang_4_remote_validated-1) ; lo - Lang 4: remote validated |
| 89DE | EQUB <(fscv_0_opt_entry-1) ; lo - FSCV 0: *OPT |
| 89DF | EQUB <(fscv_1_eof-1) ; lo - FSCV 1: EOF check |
| 89E0 | EQUB <(fscv_2_star_run-1) ; lo - FSCV 2: */ (run) |
| 89E1 | EQUB <(fscv_3_star_cmd-1) ; lo - FSCV 3: unrecognised star command |
| 89E2 | EQUB <(fscv_2_star_run-1) ; lo - FSCV 4: *RUN |
| 89E3 | EQUB <(fscv_5_cat-1) ; lo - FSCV 5: *CAT |
| 89E4 | EQUB <(fscv_6_shutdown-1) ; lo - FSCV 6: shutdown |
| 89E5 | EQUB <(fscv_7_read_handles-1) ; lo - FSCV 7: read handle range |
| 89E6 | EQUB <(fsreply_0_print_dir-1) ; lo - FS reply: print directory name |
| 89E7 | EQUB <(fsreply_1_copy_handles_boot-1) ; lo - FS reply: copy handles + boot |
| 89E8 | EQUB <(fsreply_2_copy_handles-1) ; lo - FS reply: copy handles |
| 89E9 | EQUB <(fsreply_3_set_csd-1) ; lo - FS reply: set CSD handle |
| 89EA | EQUB <(fscv_2_star_run-1) ; lo - FS reply: notify + execute |
| 89EB | EQUB <(fsreply_5_set_lib-1) ; lo - FS reply: set library handle |
| 89EC | EQUB <(net_1_read_handle-1) ; lo - *NET1: read handle from packet |
| 89ED | EQUB <(net_2_read_handle_entry-1) ; lo - *NET2: read handle from workspace |
| 89EE | EQUB <(net_3_close_handle-1) ; lo - *NET3: close handle |
| 89EF | .svc_dispatch_hi←1← 8E4E LDA |
| EQUB &D3 ; hi - dummy entry (outside ROM range) | |
| 89F0 | EQUB >(dispatch_rts-1) ; hi - Svc 0: already claimed (no-op) |
| 89F1 | EQUB >(svc_1_abs_workspace-1) ; hi - Svc 1: absolute workspace |
| 89F2 | EQUB >(svc_2_private_workspace-1) ; hi - Svc 2: private workspace |
| 89F3 | EQUB >(svc_3_autoboot-1) ; hi - Svc 3: auto-boot |
| 89F4 | EQUB >(svc_4_star_command-1) ; hi - Svc 4: unrecognised star command |
| 89F5 | EQUB >(svc5_irq_check-1) ; hi - Svc 5: unrecognised interrupt |
| 89F6 | EQUB >(dispatch_rts-1) ; hi - Svc 6: BRK (no-op) |
| 89F7 | EQUB >(svc_7_osbyte-1) ; hi - Svc 7: unrecognised OSBYTE |
| 89F8 | EQUB >(svc_8_osword-1) ; hi - Svc 8: unrecognised OSWORD |
| 89F9 | EQUB >(svc_9_help-1) ; hi - Svc 9: *HELP |
| 89FA | EQUB >(dispatch_rts-1) ; hi - Svc 10: static workspace (no-op) |
| 89FB | EQUB >(econet_restore-1) ; hi - Svc 11: NMI release (reclaim NMIs) |
| 89FC | EQUB >(wait_idle_and_reset-1) ; hi - Svc 12: NMI claim (save NMI state) |
| 89FD | EQUB >(svc_18_fs_select-1) ; hi - Svc 18: filing system selection |
| 89FE | EQUB >(lang_0_insert_remote_key-1) ; hi - Lang 0: no language / Tube |
| 89FF | EQUB >(lang_1_remote_boot-1) ; hi - Lang 1: normal startup |
| 8A00 | EQUB >(lang_2_save_palette_vdu-1) ; hi - Lang 2: softkey byte (Electron) |
| 8A01 | EQUB >(lang_3_execute_at_0100-1) ; hi - Lang 3: softkey length (Electron) |
| 8A02 | EQUB >(lang_4_remote_validated-1) ; hi - Lang 4: remote validated |
| 8A03 | EQUB >(fscv_0_opt_entry-1) ; hi - FSCV 0: *OPT |
| 8A04 | EQUB >(fscv_1_eof-1) ; hi - FSCV 1: EOF check |
| 8A05 | EQUB >(fscv_2_star_run-1) ; hi - FSCV 2: */ (run) |
| 8A06 | EQUB >(fscv_3_star_cmd-1) ; hi - FSCV 3: unrecognised star command |
| 8A07 | EQUB >(fscv_2_star_run-1) ; hi - FSCV 4: *RUN |
| 8A08 | EQUB >(fscv_5_cat-1) ; hi - FSCV 5: *CAT |
| 8A09 | EQUB >(fscv_6_shutdown-1) ; hi - FSCV 6: shutdown |
| 8A0A | EQUB >(fscv_7_read_handles-1) ; hi - FSCV 7: read handle range |
| 8A0B | EQUB >(fsreply_0_print_dir-1) ; hi - FS reply: print directory name |
| 8A0C | EQUB >(fsreply_1_copy_handles_boot-1) ; hi - FS reply: copy handles + boot |
| 8A0D | EQUB >(fsreply_2_copy_handles-1) ; hi - FS reply: copy handles |
| 8A0E | EQUB >(fsreply_3_set_csd-1) ; hi - FS reply: set CSD handle |
| 8A0F | EQUB >(fscv_2_star_run-1) ; hi - FS reply: notify + execute |
| 8A10 | EQUB >(fsreply_5_set_lib-1) ; hi - FS reply: set library handle |
| 8A11 | EQUB >(net_1_read_handle-1) ; hi - *NET1: read handle from packet |
| 8A12 | EQUB >(net_2_read_handle_entry-1) ; hi - *NET2: read handle from workspace |
| 8A13 | EQUB >(net_3_close_handle-1) ; hi - *NET3: close handle |
| 8A14 | 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.
|
||||||||
| 8A15 | .service_handler←1← 8003 JMP | |||||||
| PHA ; Save service call number | ||||||||
| 8A16 | CMP #&0f ; Is it service 15 (vectors claimed)? | |||||||
| 8A18 | BNE dispatch_service ; No: skip vectors-claimed handling | |||||||
| 8A1A | TYA ; Save Y parameter | |||||||
| 8A1B | PHA ; Save Y on stack | |||||||
| 8A1C | LDA #osbyte_read_os_version ; OSBYTE 0: read OS version | |||||||
| 8A1E | LDX #1 ; X=1 to request version number | |||||||
| 8A20 | 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) | ||||||||
| 8A23 | CPX #1 ; OS 1.20? | |||||||
| 8A25 | BEQ restore_rom_slot ; Yes: skip workspace setup | |||||||
| 8A27 | CPX #2 ; OS 2.00 (BBC B+)? | |||||||
| 8A29 | BEQ restore_rom_slot ; Yes: skip workspace setup | |||||||
| 8A2B | TXA ; Transfer OS version to A | |||||||
| 8A2C | PHP ; Save flags (Z set if OS 1.00) | |||||||
| 8A2D | LDX romsel_copy ; Get current ROM slot number | |||||||
| 8A2F | PLP ; Restore flags | |||||||
| 8A30 | BEQ clear_workspace_byte ; OS 1.00: skip INX | |||||||
| 8A32 | INX ; Adjust index for OS 3+ workspace | |||||||
| 8A33 | .clear_workspace_byte←1← 8A30 BEQ | |||||||
| LDA #0 ; A=0 | ||||||||
| 8A35 | STA rom_type_table,x ; Clear workspace byte for this ROM | |||||||
| 8A38 | .restore_rom_slot←2← 8A25 BEQ← 8A29 BEQ | |||||||
| LDX romsel_copy ; Restore ROM slot to X | ||||||||
| 8A3A | PLA ; Restore Y parameter | |||||||
| 8A3B | TAY ; Transfer to Y | |||||||
| 8A3C | .dispatch_service←1← 8A18 BNE | |||||||
| PLA ; Restore service call number | ||||||||
| 8A3D | JSR tube_vdu_dispatch ; Check relocated code service dispatch | |||||||
| 8A40 | PHA ; Save service call number | |||||||
| 8A41 | CMP #1 ; Service 1 (workspace claim)? | |||||||
| 8A43 | BNE check_adlc_flag ; No: skip ADLC check | |||||||
| 8A45 | LDA econet_control1_or_status1 ; Read ADLC status register 1 | |||||||
| 8A48 | AND #&ed ; Mask relevant status bits | |||||||
| 8A4A | BNE set_adlc_absent ; Non-zero: ADLC absent, set flag | |||||||
| 8A4C | LDA econet_control23_or_status2 ; Read ADLC status register 2 | |||||||
| 8A4F | AND #&db ; Mask relevant status bits | |||||||
| 8A51 | BEQ check_adlc_flag ; Zero: ADLC present, skip | |||||||
| 8A53 | .set_adlc_absent←1← 8A4A BNE | |||||||
| ROL rom_ws_pages,x ; Shift bit 7 into carry | ||||||||
| 8A56 | SEC ; Set carry to mark ADLC absent | |||||||
| 8A57 | ROR rom_ws_pages,x ; Rotate carry into bit 7 of slot flag | |||||||
| 8A5A | .check_adlc_flag←2← 8A43 BNE← 8A51 BEQ | |||||||
| LDA rom_ws_pages,x ; Load ROM slot flag byte | ||||||||
| 8A5D | ASL ; Shift bit 7 (ADLC absent) into carry | |||||||
| 8A5E | PLA ; Restore service call number | |||||||
| 8A5F | BCC handle_vectors_claimed ; ADLC present: continue dispatch | |||||||
| 8A61 | RTS ; ADLC absent: decline service, return | |||||||
| 8A62 | .handle_vectors_claimed←1← 8A5F BCC | |||||||
| CMP #&0f ; Service 15 (vectors claimed)? | ||||||||
| 8A64 | BNE dispatch_svc_with_state ; No: handle other services | |||||||
| 8A66 | LDX ws_0d6a ; Already initialised? | |||||||
| 8A69 | BNE start_rom_scan ; Yes: skip first-time init | |||||||
| 8A6B | INX ; X=1 (mark as initialised) | |||||||
| 8A6C | .sub_c8a6c | |||||||
| STX last_break_type ; Set ROM present flag | ||||||||
| 8A6F | .start_rom_scan←1← 8A69 BNE | |||||||
| STA ws_ptr_hi ; A=service call number; use as ROM counter | ||||||||
| 8A71 | .loop_scan_net_roms←1← 8A9A BPL | |||||||
| LDA #&80 ; Point to ROM header copyright offset | ||||||||
| 8A73 | STA osrdsc_ptr_hi ; Set high byte of OSRDSC pointer | |||||||
| 8A75 | LDA #&0c ; Offset &0C: copyright string offset | |||||||
| 8A77 | STA osrdsc_ptr ; Set low byte of OSRDSC pointer | |||||||
| 8A79 | JSR read_paged_rom ; Read next ROM title char | |||||||
| 8A7C | CMP #&4e ; First char 'N'? | |||||||
| 8A7E | BNE next_rom_slot ; No: not a NET ROM, try next | |||||||
| 8A80 | JSR read_paged_rom ; Read next ROM title char | |||||||
| 8A83 | CMP #&45 ; Second char 'E'? | |||||||
| 8A85 | BNE next_rom_slot ; No: not a NET ROM, try next | |||||||
| 8A87 | JSR read_paged_rom ; Read next ROM title char | |||||||
| 8A8A | CMP #&54 ; Third char 'T'? | |||||||
| 8A8C | BNE next_rom_slot ; No: not a NET ROM, try next | |||||||
| 8A8E | LDX ws_ptr_hi ; X=ROM slot for indexed store | |||||||
| 8A90 | LDA rom_ws_pages,x ; Load its slot flag byte | |||||||
| 8A93 | ORA #&80 ; Set bit 7 to mark as NET ROM | |||||||
| 8A95 | STA rom_ws_pages,x ; Store updated flag | |||||||
| 8A98 | .next_rom_slot←3← 8A7E BNE← 8A85 BNE← 8A8C BNE | |||||||
| DEC ws_ptr_hi ; Previous ROM slot | ||||||||
| 8A9A | BPL loop_scan_net_roms ; More ROMs to check: loop | |||||||
| 8A9C | LDA #&0f ; A=&0F: restore service call number | |||||||
| 8A9E | BNE 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.
|
||||
| 8AA0 | .read_paged_rom←3← 8A79 JSR← 8A80 JSR← 8A87 JSR | |||
| INC osrdsc_ptr ; Advance read pointer to next byte | ||||
| 8AA2 | LDY ws_ptr_hi ; Y=ROM number | |||
| 8AA4 | JMP osrdsc ; Read byte from ROM Y or screen | |||
| 8AA7 | .dispatch_svc_with_state←1← 8A64 BNE | |||
| TAX ; Transfer service number to X | ||||
| 8AA8 | LDA svc_state ; Save current service state | |||
| 8AAA | PHA ; Push old state | |||
| 8AAB | TXA ; Restore service number to A | |||
| 8AAC | STA svc_state ; Store as current service state | |||
| 8AAE | CMP #&0d ; Service < 13? | |||
| 8AB0 | BCC dispatch_svc_index ; Yes: use as dispatch index directly | |||
| 8AB2 | SBC #5 ; Subtract 5 (map 13-17 to 8-12) | |||
| 8AB4 | CMP #&0d ; Mapped value = 13? (original was 18) | |||
| 8AB6 | BEQ dispatch_svc_index ; Yes: valid service 18 (FS select) | |||
| 8AB8 | LDA #0 ; Unknown service: set index to 0 | |||
| 8ABA | .dispatch_svc_index←2← 8AB0 BCC← 8AB6 BEQ | |||
| TAX ; Transfer dispatch index to X | ||||
| 8ABB | BEQ restore_svc_state ; Index 0: unhandled service, skip | |||
| 8ABD | LDA ws_page ; Save current workspace page | |||
| 8ABF | PHA ; Push old page | |||
| 8AC0 | STY ws_page ; Set workspace page from Y parameter | |||
| 8AC2 | TYA ; Transfer Y to A | |||
| 8AC3 | LDY #0 ; Y=0 for dispatch offset | |||
| 8AC5 | JSR svc_dispatch ; Dispatch to service handler via table | |||
| 8AC8 | PLA ; Restore old workspace page | |||
| 8AC9 | STA ws_page ; Store it back | |||
| 8ACB | .restore_svc_state←1← 8ABB BEQ | |||
| LDX svc_state ; Get service state (return code) | ||||
| 8ACD | PLA ; Restore old service state | |||
| 8ACE | STA svc_state ; Store it back | |||
| 8AD0 | TXA ; Transfer return code to A | |||
| 8AD1 | .restore_romsel_rts←1← 8A9E BNE | |||
| LDX romsel_copy ; Restore ROM slot to X | ||||
| 8AD3 | RTS ; Return to MOS | |||
*ROFF command handlerDisables remote operation by clearing the flag at offset 0 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. |
|
| 8AD4 | .cmd_roff |
| LDY #0 ; Offset 0 in receive block | |
| 8AD6 | LDA (net_rx_ptr),y ; Load remote operation flag |
| 8AD8 | BEQ clear_svc_and_ws ; Zero: already off, skip to cleanup |
| 8ADA | LDA #0 ; A=0 |
| 8ADC | TAX ; X=&00 |
| 8ADD | STA (net_rx_ptr),y ; Clear remote operation flag |
| 8ADF | TAY ; Y=&00 |
| 8AE0 | LDA #osbyte_read_write_econet_keyboard_disable ; OSBYTE &C9: keyboard disable |
| 8AE2 | JSR osbyte ; Enable keyboard (for Econet) |
| 8AE5 | LDA #&0a ; A=&0A: workspace init parameter |
| 8AE7 | 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. |
|
| 8AEA | .scan_remote_keys←1← 959F JSR |
| STX nfs_workspace ; Save X in workspace | |
| 8AEC | LDA #&ce ; A=&CE: start of key range |
| 8AEE | .loop_scan_key_range←1← 8AF9 BEQ |
| LDX nfs_workspace ; Restore X from workspace | |
| 8AF0 | LDY #&7f ; Y=&7F: OSBYTE scan parameter |
| 8AF2 | JSR osbyte ; OSBYTE: scan keyboard |
| 8AF5 | ADC #1 ; Advance to next key code |
| 8AF7 | CMP #&d0 ; Reached &D0? |
| 8AF9 | BEQ loop_scan_key_range ; No: loop back (scan &CE and &CF) |
| 8AFB | .clear_svc_and_ws←1← 8AD8 BEQ |
| LDA #0 ; A=0 | |
| 8AFD | STA svc_state ; Clear service state |
| 8AFF | STA nfs_workspace ; Clear workspace byte |
| 8B01 | 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.
|
||||
| 8B02 | .save_text_ptr←3← 8C52 JSR← 8C7D JSR← A2BF JSR | |||
| PHA ; Save A | ||||
| 8B03 | LDA os_text_ptr ; Copy OS text pointer low | |||
| 8B05 | STA fs_crc_lo ; to fs_crc_lo | |||
| 8B07 | LDA os_text_ptr_hi ; Copy OS text pointer high | |||
| 8B09 | STA fs_crc_hi ; to fs_crc_hi | |||
| 8B0B | PLA ; Restore A | |||
| 8B0C | .return_from_save_text_ptr←2← 8B0F BNE← 8B18 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.
|
||||
| 8B0D | .svc_18_fs_select | |||
| CPY #5 ; Y=5 (Econet filing system)? | ||||
| 8B0F | BNE return_from_save_text_ptr ; No: not ours, return unclaimed | |||
| 8B11 | LDA #0 ; A=0: clear service state | |||
| 8B13 | STA svc_state ; Reset service processing state | |||
| 8B15 | BIT fs_flags ; Already selected? | |||
| 8B18 | 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. |
|
| 8B1A | .cmd_net_fs←1← 8CE0 JSR |
| JSR get_ws_page ; Get workspace page for this ROM slot | |
| 8B1D | STA fs_load_addr_hi ; Store as high byte of load address |
| 8B1F | LDA #0 ; A=0 |
| 8B21 | STA fs_load_addr ; Clear low byte of load address |
| 8B23 | CLC ; Clear carry for addition |
| 8B24 | LDY #&76 ; Y=&76: checksum range end |
| 8B26 | .loop_sum_rom_bytes←1← 8B29 BPL |
| ADC (fs_load_addr),y ; Add byte to running checksum | |
| 8B28 | DEY ; Decrement index |
| 8B29 | BPL loop_sum_rom_bytes ; Loop until all bytes summed |
| 8B2B | LDY #&77 ; Y=&77: checksum storage offset |
| 8B2D | EOR (fs_load_addr),y ; Compare with stored checksum |
| 8B2F | BEQ done_rom_checksum ; Match: checksum valid |
| 8B31 | JMP error_net_checksum ; Mismatch: raise checksum error |
| 8B34 | .done_rom_checksum←1← 8B2F BEQ |
| JSR notify_new_fs ; Call FSCV with A=6 (new FS) | |
| 8B37 | LDY #9 ; Y=9: end of FS context block |
| 8B39 | .loop_copy_fs_ctx←1← 8B41 BNE |
| LDA (net_rx_ptr),y ; Load byte from receive block | |
| 8B3B | STA osword_ws_base,y ; Store into FS workspace |
| 8B3E | DEY ; Decrement index |
| 8B3F | CPY #1 ; Reached offset 1? |
| 8B41 | BNE loop_copy_fs_ctx ; No: continue copying |
| 8B43 | ROL fs_flags ; Shift bit 7 of FS flags into carry |
| 8B46 | CLC ; Clear carry |
| 8B47 | ROR fs_flags ; Clear bit 7 of FS flags |
| 8B4A | LDY #&0d ; Y=&0D: vector table size - 1 |
| 8B4C | .loop_set_vectors←1← 8B53 BPL |
| LDA fs_vector_table,y ; Load FS vector address | |
| 8B4F | STA filev,y ; Store into FILEV vector table |
| 8B52 | DEY ; Decrement index |
| 8B53 | BPL loop_set_vectors ; Loop until all vectors installed |
| 8B55 | JSR init_adlc_and_vectors ; Initialise ADLC and NMI workspace |
| 8B58 | LDY #&1b ; Y=&1B: extended vector offset |
| 8B5A | LDX #7 ; X=7: two more vectors to set up |
| 8B5C | JSR write_vector_entry ; Set up extended vectors |
| 8B5F | LDA #0 ; A=0 |
| 8B61 | STA fs_eof_flags ; Clear FS state byte |
| 8B64 | STA cur_chan_attr ; Clear workspace byte |
| 8B67 | STA fs_lib_flags ; Clear workspace byte |
| 8B6A | STA svc_state ; Clear service state |
| 8B6C | JSR store_rx_attribute ; Clear receive attribute byte |
| 8B6F | STA l0e08 ; Clear workspace byte |
| 8B72 | JSR setup_ws_ptr ; Set up workspace pointers |
| 8B75 | JSR init_channel_table ; Initialise FS state |
| 8B78 | LDY #&77 ; Y=&77: workspace block size - 1 |
| 8B7A | .loop_copy_ws_page←1← 8B80 BPL |
| LDA (fs_ws_ptr),y ; Load byte from source workspace | |
| 8B7C | STA fcb_count_lo,y ; Store to page &10 shadow copy |
| 8B7F | DEY ; Decrement index |
| 8B80 | BPL loop_copy_ws_page ; Loop until all bytes copied |
| 8B82 | LDA #&80 ; A=&80: FS selected flag |
| 8B84 | ORA fs_flags ; Set bit 7 of FS flags |
| 8B87 | STA fs_flags ; Store updated flags |
| 8B8A | JMP issue_svc_15 ; Issue service 15 (FS initialised) |
| 8B8D | .help_print_nfs_cmds←1← 8C7A JMP |
| LDX #&4a ; X=&4A: NFS command table offset | |
| 8B8F | 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. |
|
| 8B92 | .help_utils |
| LDX #0 ; X=0: utility command table offset | |
| 8B94 | 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. |
|
| 8B96 | .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.
|
||||||
| 8B98 | .print_cmd_table←2← 8B8F JSR← 8B94 BEQ | |||||
| BVC print_table_newline ; V clear: need to print header first | ||||||
| 8B9A | TXA ; Save X (table offset) | |||||
| 8B9B | PHA ; Push it | |||||
| 8B9C | TYA ; Save Y | |||||
| 8B9D | PHA ; Push it | |||||
| 8B9E | JSR print_version_header ; Print version string header | |||||
| 8BA1 | PLA ; Restore Y | |||||
| 8BA2 | TAY ; Transfer to Y | |||||
| 8BA3 | PLA ; Restore X | |||||
| 8BA4 | TAX ; Transfer to X | |||||
| 8BA5 | CLV ; Clear overflow flag | |||||
| 8BA6 | BVC print_cmd_table_loop ; ALWAYS branch | |||||
| 8BA8 | .print_table_newline←1← 8B98 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.
|
||||
| 8BAB | .print_cmd_table_loop←2← 8BA6 BVC← 8C6D JSR | |||
| TYA ; Save Y (command line offset) | ||||
| 8BAC | PHA ; Push it | |||
| 8BAD | PHP ; Save processor status | |||
| 8BAE | .loop_next_entry←1← 8C2C JMP | |||
| LDA cmd_table_fs,x ; Load byte from command table | ||||
| 8BB1 | BPL print_indent ; Bit 7 clear: valid entry, continue | |||
| 8BB3 | JMP done_print_table ; End of table: finish up | |||
| 8BB6 | .print_indent←1← 8BB1 BPL | |||
| JSR print_inline ; Print two-space indent | ||||
| 8BB9 | EQUS " " | |||
| 8BBB | LDY #9 ; Y=9: max command name length | |||
| 8BBD | LDA cmd_table_fs,x ; Load first byte of command name | |||
| 8BC0 | .loop_print_name←1← 8BC8 BPL | |||
| JSR osasci ; Write character | ||||
| 8BC3 | INX ; Advance table pointer | |||
| 8BC4 | DEY ; Decrement padding counter | |||
| 8BC5 | LDA cmd_table_fs,x ; Load next character | |||
| 8BC8 | BPL loop_print_name ; Bit 7 clear: more chars, continue | |||
| 8BCA | .loop_pad_spaces←1← 8BD0 BPL | |||
| LDA #&20 ; Pad with spaces | ||||
| 8BCC | JSR osasci ; Write character 32 | |||
| 8BCF | DEY ; Decrement remaining pad count | |||
| 8BD0 | BPL loop_pad_spaces ; More padding needed: loop | |||
| 8BD2 | LDA cmd_table_fs,x ; Load syntax descriptor byte | |||
| 8BD5 | AND #&1f ; Mask to get syntax string index | |||
| 8BD7 | CMP #&0e ; Index &0E: shared commands? | |||
| 8BD9 | BEQ print_shared_prefix ; Yes: handle shared commands list | |||
| 8BDB | TAY ; Use index as Y | |||
| 8BDC | LDA cmd_syntax_table,y ; Look up syntax string offset | |||
| 8BDF | TAY ; Transfer offset to Y | |||
| 8BE0 | .loop_print_syntax←2← 8BED JMP← 8BF3 JMP | |||
| INY ; Advance to next character | ||||
| 8BE1 | LDA cmd_syntax_strings,y ; Load syntax string character | |||
| 8BE4 | BEQ done_entry_newline ; Zero terminator: end of syntax | |||
| 8BE6 | CMP #&0d ; Carriage return: line continuation | |||
| 8BE8 | BNE print_syntax_char ; No: print the character | |||
| 8BEA | JSR help_wrap_if_serial ; Handle line wrap in syntax output | |||
| 8BED | JMP loop_print_syntax ; Continue with next character | |||
| 8BF0 | .print_syntax_char←1← 8BE8 BNE | |||
| JSR osasci ; Write character | ||||
| 8BF3 | JMP loop_print_syntax ; Continue with next character | |||
| 8BF6 | .print_shared_prefix←1← 8BD9 BEQ | |||
| TXA ; Save table pointer | ||||
| 8BF7 | PHA ; Push it | |||
| 8BF8 | JSR print_inline ; Print opening parenthesis | |||
| 8BFB | EQUS "(" | |||
| 8BFC | LDY #0 ; Y=0: shared command counter | |||
| 8BFE | LDX #&d3 ; X=&D3: shared command table start | |||
| 8C00 | .loop_next_shared←1← 8C22 BNE | |||
| LDA cmd_table_fs,x ; Load byte from shared command table | ||||
| 8C03 | BMI done_shared_cmds ; Bit 7 set: end of shared commands | |||
| 8C05 | DEX ; Back up one position | |||
| 8C06 | .loop_print_shared←1← 8C0F JMP | |||
| INX ; Advance to next character | ||||
| 8C07 | LDA cmd_table_fs,x ; Load command name character | |||
| 8C0A | BMI print_last_char ; Bit 7 set: end of this name | |||
| 8C0C | JSR osasci ; Write character | |||
| 8C0F | JMP loop_print_shared ; Print more characters of name | |||
| 8C12 | .print_last_char←1← 8C0A BMI | |||
| AND #&7f ; Strip bit 7 from final character | ||||
| 8C14 | JSR osasci ; Write character | |||
| 8C17 | INY ; Count this shared command | |||
| 8C18 | CPY #4 ; Printed 4 commands? | |||
| 8C1A | BNE skip_syntax_bytes ; No: continue on same line | |||
| 8C1C | JSR help_wrap_if_serial ; Handle line wrap after 4 commands | |||
| 8C1F | .skip_syntax_bytes←1← 8C1A BNE | |||
| INX ; X += 3: skip syntax descriptor and address | ||||
| 8C20 | INX ; (continued) | |||
| 8C21 | INX ; (continued) | |||
| 8C22 | BNE loop_next_shared ; Loop for more shared commands | |||
| 8C24 | .done_shared_cmds←1← 8C03 BMI | |||
| PLA ; Restore original table pointer | ||||
| 8C25 | TAX ; Transfer to X | |||
| 8C26 | .done_entry_newline←1← 8BE4 BEQ | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| 8C29 | INX ; X += 3: skip syntax descriptor and address | |||
| 8C2A | INX ; (continued) | |||
| 8C2B | INX ; (continued) | |||
| 8C2C | JMP loop_next_entry ; Loop for next command | |||
| 8C2F | .done_print_table←1← 8BB3 JMP | |||
| PLP ; Restore processor status | ||||
| 8C30 | PLA ; Restore Y | |||
| 8C31 | TAY ; Transfer to Y | |||
| 8C32 | 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.
|
||||
| 8C33 | .help_wrap_if_serial←2← 8BEA JSR← 8C1C JSR | |||
| LDA vdu_mode ; Read output stream type | ||||
| 8C36 | BEQ return_from_help_wrap ; Stream 0 (VDU): no wrapping | |||
| 8C38 | CMP #3 ; Stream 3 (printer)? | |||
| 8C3A | BEQ return_from_help_wrap ; Yes: no wrapping | |||
| 8C3C | TYA ; Save Y | |||
| 8C3D | PHA ; Push it | |||
| 8C3E | JSR osnewl ; Write newline (characters 10 and 13) | |||
| 8C41 | LDY #&0b ; Y=&0B: indent width - 1 | |||
| 8C43 | LDA #&20 ; Space character | |||
| 8C45 | .loop_indent_spaces←1← 8C49 BPL | |||
| JSR osasci ; Write character 32 | ||||
| 8C48 | DEY ; Decrement indent counter | |||
| 8C49 | BPL loop_indent_spaces ; More spaces needed: loop | |||
| 8C4B | PLA ; Restore Y | |||
| 8C4C | TAY ; Transfer to Y | |||
| 8C4D | .return_from_help_wrap←2← 8C36 BEQ← 8C3A 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.
|
||||
| 8C4E | .svc_4_star_command | |||
| LDX #0 ; X=0: start of utility command table | ||||
| 8C50 | LDY ws_page ; Get command line offset | |||
| 8C52 | JSR save_text_ptr ; Save text pointer to fs_crc | |||
| 8C55 | JSR match_fs_cmd ; Try to match command in table | |||
| 8C58 | BCS svc_return_unclaimed ; No match: return to caller | |||
| 8C5A | 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). |
|
| 8C5D | .svc_9_help |
| JSR check_credits_easter_egg ; Check for credits Easter egg | |
| 8C60 | LDY ws_page ; Get command line offset |
| 8C62 | LDA (os_text_ptr),y ; Load character at offset |
| 8C64 | CMP #&0d ; Is it CR (bare *HELP)? |
| 8C66 | BNE check_help_topic ; No: check for specific topic |
| 8C68 | JSR print_version_header ; Print version string |
| 8C6B | LDX #&c4 ; X=&C4: start of help command list |
| 8C6D | JSR print_cmd_table_loop ; Print command list from table |
| 8C70 | .svc_return_unclaimed←3← 8C58 BCS← 8C9D BEQ← 8CD8 BNE |
| LDY ws_page ; Restore Y (command line offset) | |
| 8C72 | RTS ; Return unclaimed |
| 8C73 | .check_help_topic←1← 8C66 BNE |
| BIT bit_test_ff ; Test for topic match (sets flags) | |
| 8C76 | CMP #&2e ; Is first char '.' (abbreviation)? |
| 8C78 | BNE match_help_topic ; No: try topic-specific help |
| 8C7A | JMP help_print_nfs_cmds ; '.' found: show full command list |
| 8C7D | .match_help_topic←1← 8C78 BNE |
| JSR save_text_ptr ; Save text pointer to fs_crc | |
| 8C80 | .loop_dispatch_help←1← 8C9B BNE |
| PHP ; Save flags | |
| 8C81 | LDX #&c4 ; X=&C4: help command table start |
| 8C83 | JSR match_fs_cmd ; Try to match help topic in table |
| 8C86 | BCS skip_if_no_match ; No match: try next topic |
| 8C88 | PLP ; Restore flags |
| 8C89 | LDA #&8c ; Push return address high (&8C) |
| 8C8B | PHA ; Push it for RTS dispatch |
| 8C8C | LDA #&7f ; Push return address low (&74) |
| 8C8E | PHA ; Push it for RTS dispatch |
| 8C8F | LDA cmd_table_fs_hi,x ; Load dispatch address high |
| 8C92 | PHA ; Push dispatch high for RTS |
| 8C93 | LDA cmd_table_fs_lo,x ; Load dispatch address low |
| 8C96 | PHA ; Push dispatch low for RTS |
| 8C97 | RTS ; Dispatch via RTS (returns to &8C80) |
| 8C98 | .skip_if_no_match←1← 8C86 BCS |
| PLP ; Restore flags from before match | |
| 8C99 | CMP #&0d ; End of command line? |
| 8C9B | BNE loop_dispatch_help ; No: try matching next topic |
| 8C9D | 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. |
|
| 8C9F | .print_version_header←2← 8B9E JSR← 8C68 JSR |
| JSR print_inline ; Print version string via inline | |
| 8CA2 | .version_string_cr |
| EQUS ".Advanced NFS 4.18." | |
| 8CB5 | NOP ; NOP (string terminator) |
| 8CB6 | JMP print_station_id ; Print station number after version |
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.
|
||||||
| 8CB9 | .get_ws_page←4← 8B1A JSR← 8CC0 JSR← 8F87 JSR← AFE2 JSR | |||||
| LDY romsel_copy ; Get current ROM slot number | ||||||
| 8CBB | LDA rom_ws_pages,y ; Load workspace page for this slot | |||||
| 8CBE | TAY ; Transfer to Y | |||||
| 8CBF | 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.
|
||||||
| 8CC0 | .setup_ws_ptr←2← 8B72 JSR← 8EE7 JSR | |||||
| JSR get_ws_page ; Get workspace page for ROM slot | ||||||
| 8CC3 | STY nfs_temp ; Store page in nfs_temp | |||||
| 8CC5 | LDA #0 ; A=0 | |||||
| 8CC7 | STA fs_ws_ptr ; Clear low byte of pointer | |||||
| 8CC9 | .return_from_setup_ws_ptr←1← 8CEB 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. |
|
| 8CCA | .svc_3_autoboot |
| LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: scan keyboard from key 16 | |
| 8CCC | JSR osbyte ; Keyboard scan starting from key 16 |
| 8CCF | TXA ; X is key number if key is pressed, or &ff otherwise |
| 8CD0 | BMI select_net_fs ; No key pressed: select Net FS |
| 8CD2 | CMP #&19 ; Key &19 (N)? |
| 8CD4 | BEQ write_key_state ; Yes: write key state and boot |
| 8CD6 | EOR #&55 ; EOR with &55: maps to zero if 'N' |
| 8CD8 | BNE svc_return_unclaimed ; Not N key: return unclaimed |
| 8CDA | .write_key_state←1← 8CD4 BEQ |
| TAY ; Y=key | |
| 8CDB | LDA #osbyte_write_keys_pressed ; OSBYTE &78: write keys pressed |
| 8CDD | JSR osbyte ; Write current keys pressed (X and Y) |
| 8CE0 | .select_net_fs←1← 8CD0 BMI |
| JSR cmd_net_fs ; Select NFS as current filing system | |
| 8CE3 | JSR print_station_id ; Print station number |
| 8CE6 | JSR osnewl ; Write newline (characters 10 and 13) |
| 8CE9 | LDX ws_page ; Get workspace page |
| 8CEB | BNE return_from_setup_ws_ptr ; Non-zero: already initialised, return |
| 8CED | LDA fs_lib_flags ; Load boot flags |
| 8CF0 | ORA #4 ; Set bit 2 (auto-boot in progress) |
| 8CF2 | STA fs_lib_flags ; Store updated boot flags |
| 8CF5 | LDX #&0f ; X=&0F: boot filename address low |
| 8CF7 | LDY #&8d ; Y=&8D: boot filename address high |
| 8CF9 | 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. |
|
| 8CFC | .notify_new_fs←1← 8B34 JSR |
| LDA #6 ; A=6: notify new filing system | |
| 8CFE | JSR call_fscv ; Call FSCV |
| 8D01 | LDX #&0a ; X=&0A: service 10 parameter |
| 8D03 | 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.
|
||||
| 8D05 | .call_fscv←1← 8CFE JSR | |||
| JMP (fscv) ; Dispatch via FSCV | ||||
| 8D08 | .issue_svc_15←1← 8B8A JMP | |||
| LDX #&0f ; X=&0F: service 15 parameter | ||||
| 8D0A | .issue_svc_osbyte←1← 8D03 BNE | |||
| LDA #osbyte_issue_service_request ; OSBYTE &8F: issue service request | ||||
| 8D0C | JMP osbyte ; Issue paged ROM service call | |||
| 8D0F | EQUS "i .boot" | |||
| 8D16 | 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. |
|
| 8D17 | .check_credits_easter_egg←1← 8C5D JSR |
| LDY ws_page ; Get command line offset | |
| 8D19 | LDX #5 ; X=5: start of credits keyword |
| 8D1B | .loop_match_credits←1← 8D24 BNE |
| LDA (os_text_ptr),y ; Load character from command line | |
| 8D1D | CMP credits_keyword_start,x ; Compare with credits keyword |
| 8D20 | BNE done_credits_check ; Mismatch: check if keyword complete |
| 8D22 | INY ; Advance command line pointer |
| 8D23 | INX ; Advance keyword pointer |
| 8D24 | BNE loop_match_credits ; Continue matching |
| 8D26 | .done_credits_check←1← 8D20 BNE |
| CPX #&0d ; Reached end of keyword (X=&0D)? | |
| 8D28 | BNE return_from_credits_check ; No: keyword not fully matched, return |
| 8D2A | LDX #0 ; X=0: start of credits text |
| 8D2C | .loop_emit_credits←1← 8D35 BNE |
| LDA credits_keyword_start,x ; Load character from credits string | |
| 8D2F | BEQ return_from_credits_check ; Zero terminator: done printing |
| 8D31 | JSR osasci ; Write character |
| 8D34 | INX ; Advance string pointer |
| 8D35 | BNE loop_emit_credits ; Continue printing |
| 8D37 | .return_from_credits_check←2← 8D28 BNE← 8D2F BEQ |
| RTS ; Return | |
| 8D38 | .credits_keyword_start←2← 8D1D CMP← 8D2C LDA |
| EQUB &0D ; CR | |
| 8D39 | .credits_string |
| EQUS "The authors of ANFS are;" | |
| 8D51 | EQUB &0D ; CR |
| 8D52 | EQUS "B Cockburn" |
| 8D5C | EQUB &0D ; CR |
| 8D5D | EQUS "J Du" |
| 8D61 | .ps_template_base←1← B01B LDA |
| EQUS "nn" | |
| 8D63 | EQUB &0D ; CR |
| 8D64 | EQUS "B Robertson" |
| 8D6F | EQUB &0D ; CR |
| 8D70 | EQUS "J Wills" |
| 8D77 | EQUB &0D ; CR |
| 8D78 | 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. |
|
| 8D79 | .cmd_iam |
| TYA ; Save Y (command line offset) | |
| 8D7A | PHA ; Push it |
| 8D7B | LDA #osbyte_close_spool_exec ; OSBYTE &77: close SPOOL/EXEC |
| 8D7D | STA fs_eof_flags ; Store as pending operation marker |
| 8D80 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 8D83 | LDY #0 ; Y=0 |
| 8D85 | STY fs_work_4 ; Clear password entry flag |
| 8D87 | JSR process_all_fcbs ; Reset FS connection state |
| 8D8A | LDA #0 ; A=0 |
| 8D8C | STA fs_eof_flags ; Clear pending operation marker |
| 8D8F | PLA ; Restore command line offset |
| 8D90 | TAY ; Transfer to Y |
| 8D91 | LDA (fs_options),y ; Load first option byte |
| 8D93 | JSR is_decimal_digit ; Parse station number if present |
| 8D96 | BCC cmd_pass ; Not a digit: skip to password entry |
| 8D98 | JSR parse_addr_arg ; Parse user ID string |
| 8D9B | BCS skip_no_fs_addr ; No user ID: go to password |
| 8D9D | STA fs_server_net ; Store file server station low |
| 8DA0 | JSR clear_if_station_match ; Check and store FS network |
| 8DA3 | INY ; Skip separator |
| 8DA4 | JSR parse_addr_arg ; Parse next argument |
| 8DA7 | .skip_no_fs_addr←1← 8D9B BCS |
| BEQ cmd_pass ; No FS address: skip to password | |
| 8DA9 | STA fs_server_stn ; Store file server station high |
| 8DAC | LDX #&ff ; X=&FF: pre-decrement for loop |
| 8DAE | .loop_copy_logon_cmd←1← 8DB5 BPL |
| INX ; Advance index | |
| 8DAF | LDA cmd_table_nfs_iam,x ; Load logon command template byte |
| 8DB2 | STA fs_cmd_data,x ; Store into transmit buffer |
| 8DB5 | BPL loop_copy_logon_cmd ; Bit 7 clear: more bytes, loop |
| 8DB7 | JSR copy_arg_validated ; Send logon with file server lookup |
| 8DBA | 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. |
|
| 8DBC | .cmd_pass←2← 8D96 BCC← 8DA7 BEQ |
| JSR copy_arg_to_buf_x0 ; Build FS command packet | |
| 8DBF | .scan_pass_prompt←1← 8DBA BEQ |
| LDY #&ff ; Y=&FF: pre-increment for loop | |
| 8DC1 | .loop_scan_colon←1← 8DCB BNE |
| INY ; Advance to next byte | |
| 8DC2 | LDA fs_cmd_data,y ; Load byte from reply buffer |
| 8DC5 | CMP #&0d ; Is it CR (end of prompt)? |
| 8DC7 | BEQ send_pass_to_fs ; Yes: no colon found, skip to send |
| 8DC9 | CMP #&3a ; Is it ':' (password prompt)? |
| 8DCB | BNE loop_scan_colon ; No: keep scanning |
| 8DCD | JSR oswrch ; Write character |
| 8DD0 | STY fs_work_4 ; Save position of colon |
| 8DD2 | .read_pw_char←4← 8DE2 BNE← 8DE6 BEQ← 8DE9 BNE← 8DF5 BNE |
| LDA #&ff ; A=&FF: mark as escapable | |
| 8DD4 | STA need_release_tube ; Set escape flag |
| 8DD6 | JSR check_escape ; Check for escape condition |
| 8DD9 | JSR osrdch ; Read a character from the current input stream |
| 8DDC | CMP #&15 ; A=character read |
| 8DDE | BNE check_pw_special ; Not NAK (&15): check other chars |
| 8DE0 | LDY fs_work_4 ; Restore colon position |
| 8DE2 | BNE read_pw_char ; Non-zero: restart from colon |
| 8DE4 | .loop_erase_pw←1← 8DED BEQ |
| CPY fs_work_4 ; At colon position? | |
| 8DE6 | BEQ read_pw_char ; Yes: restart password input |
| 8DE8 | DEY ; Backspace: move back one character |
| 8DE9 | BNE read_pw_char ; If not at start: restart input |
| 8DEB | .check_pw_special←1← 8DDE BNE |
| CMP #&7f ; Delete key (&7F)? | |
| 8DED | BEQ loop_erase_pw ; Yes: handle backspace |
| 8DEF | STA fs_cmd_data,y ; Store character in password buffer |
| 8DF2 | INY ; Advance buffer pointer |
| 8DF3 | CMP #&0d ; Is it CR (end of password)? |
| 8DF5 | BNE read_pw_char ; No: read another character |
| 8DF7 | JSR osnewl ; Write newline (characters 10 and 13) |
| 8DFA | .send_pass_to_fs←1← 8DC7 BEQ |
| TYA ; Transfer string length to A | |
| 8DFB | PHA ; Save string length |
| 8DFC | JSR init_txcb ; Set up transmit control block |
| 8DFF | JSR init_tx_ptr_for_pass ; Send to file server and get reply |
| 8E02 | PLA ; Restore string length |
| 8E03 | TAX ; Transfer to X (byte count) |
| 8E04 | INX ; Include terminator |
| 8E05 | LDY #0 ; Y=0 |
| 8E07 | 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.
|
||||
| 8E09 | .clear_if_station_match←2← 8DA0 JSR← A687 JSR | |||
| JSR init_bridge_poll ; Parse station number from cmd line | ||||
| 8E0C | EOR fs_server_net ; Compare with expected station | |||
| 8E0F | BNE return_from_station_match ; Different: return without clearing | |||
| 8E11 | STA fs_server_net ; Same: clear station byte | |||
| 8E14 | .return_from_station_match←1← 8E0F BNE | |||
| RTS ; Return | ||||
| 8E15 | .check_urd_prefix←1← 9462 JMP | |||
| LDY #0 ; Y=0: first character offset | ||||
| 8E17 | LDA (fs_crc_lo),y ; Load first character of command text | |||
| 8E19 | CMP #&26 ; Is it '&' (URD prefix)? | |||
| 8E1B | BNE pass_send_cmd ; No: send as normal FS command | |||
| 8E1D | JMP fscv_2_star_run ; Yes: route via *RUN for URD prefix handling | |||
| 8E20 | .pass_send_cmd←1← 8E1B BNE | |||
| JSR copy_arg_to_buf_x0 ; Build FS command packet | ||||
| 8E23 | TAY ; Transfer result to Y | |||
| 8E24 | .send_cmd_and_dispatch←2← 8E07 BEQ← 9324 JMP | |||
| JSR save_net_tx_cb ; Set up command and send to FS | ||||
| 8E27 | LDX fs_cmd_csd ; Load reply function code | |||
| 8E2A | BEQ dispatch_rts ; Zero: no reply, return | |||
| 8E2C | LDA fs_cmd_data ; Load first reply byte | |||
| 8E2F | LDY #&17 ; Y=&17: logon dispatch offset | |||
| 8E31 | BNE svc_dispatch ; ALWAYS branch | |||
| 8E33 | JSR set_xfer_params ; Parse reply as decimal number | |||
| 8E36 | CMP #8 ; Result >= 8? | |||
| 8E38 | BCS dispatch_rts ; Yes: out of range, return | |||
| 8E3A | TAX ; Transfer handle to X | |||
| 8E3B | JSR mask_owner_access ; Look up in open files table | |||
| 8E3E | TYA ; Transfer result to A | |||
| 8E3F | LDY #&13 ; Y=&13: handle dispatch offset | |||
| 8E41 | BNE svc_dispatch ; ALWAYS branch | |||
Dispatch directory operation via PHA/PHA/RTSValidates X < 5 and sets Y=&0E as the directory dispatch offset, then falls through to svc_dispatch for PHA/PHA/RTS table dispatch. Called by tx_done_os_proc to handle directory operations (e.g. FILEV, ARGSV) from the remote JSR service.
|
||||
| 8E43 | .dir_op_dispatch←1← 8560 JSR | |||
| CPX #5 ; Handle >= 5? | ||||
| 8E45 | BCS dispatch_rts ; Yes: out of range, return | |||
| 8E47 | 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.
|
|||||||||
| 8E49 | .svc_dispatch←5← 8AC5 JSR← 8E31 BNE← 8E41 BNE← 8E4B BPL← 8EA2 JMP | ||||||||
| INX ; Advance X to target index | |||||||||
| 8E4A | DEY ; Decrement Y offset counter | ||||||||
| 8E4B | BPL svc_dispatch ; Y still positive: continue counting | ||||||||
| 8E4D | TAY ; Y=&FF: will be ignored by caller | ||||||||
| 8E4E | LDA svc_dispatch_hi,x ; Load dispatch address high byte | ||||||||
| 8E51 | PHA ; Push high byte for RTS dispatch | ||||||||
| 8E52 | .push_dispatch_lo | ||||||||
| LDA svc_dispatch_lo,x ; Load dispatch address low byte | |||||||||
| 8E55 | PHA ; Push low byte for RTS dispatch | ||||||||
| 8E56 | LDX fs_options ; Load FS options pointer | ||||||||
| 8E58 | .dispatch_rts←3← 8E2A BEQ← 8E38 BCS← 8E45 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 = | |||||||||
| ; &8E59). 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. | |||||||||
| 8E59 | .ps_template_data | ||||||||
| EQUS "PRINT " ; PS template: default name "PRINT " | |||||||||
| 8E5F | EQUB &01 | ||||||||
| 8E60 | EQUB &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. |
|
| 8E61 | .fs_vector_table←1← 8B4C LDA |
| EQUW &FF1B ; FILEV dispatch (&FF1B) | |
| 8E63 | EQUW &FF1E ; ARGSV dispatch (&FF1E) |
| 8E65 | EQUW &FF21 ; BGETV dispatch (&FF21) |
| 8E67 | EQUW &FF24 ; BPUTV dispatch (&FF24) |
| 8E69 | EQUW &FF27 ; GBPBV dispatch (&FF27) |
| 8E6B | EQUW &FF2A ; FINDV dispatch (&FF2A) |
| 8E6D | EQUW &FF2D ; FSCV dispatch (&FF2D) |
| 8E6F | EQUW &9935 ; FILEV handler (&9935) |
| 8E71 | EQUB &4A ; (ROM bank — not read) |
| 8E72 | EQUW &9BBE ; ARGSV handler (&9BBE) |
| 8E74 | EQUB &44 ; (ROM bank — not read) |
| 8E75 | EQUW &B7CE ; BGETV handler (&B7CE) |
| 8E77 | EQUB &57 ; (ROM bank — not read) |
| 8E78 | EQUW &B84D ; BPUTV handler (&B84D) |
| 8E7A | EQUB &42 ; (ROM bank — not read) |
| 8E7B | EQUW &9E2F ; GBPBV handler (&9E2F) |
| 8E7D | EQUB &41 ; (ROM bank — not read) |
| 8E7E | EQUW &9D4E ; FINDV handler (&9D4E) |
| 8E80 | EQUB &52 ; (ROM bank — not read) |
| 8E81 | EQUW &8E33 ; FSCV handler (&8E33) |
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.
|
|||||||||
| 8E83 | .osbyte_x0←3← 8081 JSR← 8F62 JSR← 970E 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.
|
|||||||||
| 8E85 | .osbyte_yff←1← 808B JSR | ||||||||
| LDY #&ff ; Y=&FF | |||||||||
| 8E87 | .jmp_osbyte←1← 8E90 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. Points to | |||||||||
| ; netv_handler which dispatches OSWORDs | |||||||||
| ; 0-8 to Econet handlers. Interleaved with the | |||||||||
| ; OSBYTE wrapper code in the data area. | |||||||||
| 8E8A | .netv_handler_addr | ||||||||
| EQUW netv_handler | |||||||||
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 &8E87.
|
|||||||||
| 8E8C | .osbyte_x0_y0←1← 9722 JSR | ||||||||
| LDX #0 ; X=0 | |||||||||
| 8E8E | LDY #0 ; Y=0 | ||||||||
| 8E90 | 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.
|
||||
| 8E92 | .svc_7_osbyte | |||
| LDA osbyte_a_copy ; Get original OSBYTE A parameter | ||||
| 8E94 | SBC #&31 ; Subtract &31 (map &32-&35 to 1-4) | |||
| 8E96 | CMP #4 ; In range 0-3? | |||
| 8E98 | BCS return_from_svc_1_workspace ; No: not ours, return unclaimed | |||
| 8E9A | TAX ; Transfer to X as dispatch index | |||
| 8E9B | LDA #0 ; A=0: claim the service call | |||
| 8E9D | STA svc_state ; Set return value to 0 (claimed) | |||
| 8E9F | TYA ; Transfer Y to A (OSBYTE Y param) | |||
| 8EA0 | LDY #&21 ; Y=&21: OSBYTE dispatch offset | |||
| 8EA2 | 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.
|
|||||||
| 8EA5 | .svc_1_abs_workspace | ||||||
| CPY #&16 ; Need at least &16 pages? | |||||||
| 8EA7 | BCS return_from_svc_1_workspace ; Already enough: return | ||||||
| 8EA9 | LDY #&16 ; Request &16 pages of workspace | ||||||
| 8EAB | .return_from_svc_1_workspace←2← 8E98 BCS← 8EA7 BCS | ||||||
| RTS ; Return | |||||||
Record workspace page count (capped at &21)Stores the workspace allocation from service 1 into offset &0B 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.
|
||||
| 8EAC | .store_ws_page_count←1← 8EDF JSR | |||
| TYA ; Transfer Y to A | ||||
| 8EAD | CMP #&21 ; Y >= &21? | |||
| 8EAF | BCC done_cap_ws_count ; No: use Y as-is | |||
| 8EB1 | LDA #&21 ; Cap at &21 | |||
| 8EB3 | .done_cap_ws_count←1← 8EAF BCC | |||
| LDY #&0b ; Offset &0B in receive block | ||||
| 8EB5 | STA (net_rx_ptr),y ; Store workspace page count | |||
| 8EB7 | 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.
|
||||
| 8EB8 | .svc_2_private_workspace | |||
| STY net_rx_ptr_hi ; Store Y as receive block page | ||||
| 8EBA | INY ; Advance to next page | |||
| 8EBB | STY nfs_workspace_hi ; Store as NFS workspace page | |||
| 8EBD | INY ; Advance to next page | |||
| 8EBE | TYA ; Transfer page to A | |||
| 8EBF | LDY romsel_copy ; Get current ROM slot number | |||
| 8EC1 | STA rom_ws_pages,y ; Store workspace page for this slot | |||
| 8EC4 | LDY fdc_1770_data ; Load break type from hardware register | |||
| 8EC7 | LDA #0 ; A=0 | |||
| 8EC9 | STA net_rx_ptr ; Clear receive block pointer low | |||
| 8ECB | STA nfs_workspace ; Clear NFS workspace pointer low | |||
| 8ECD | STA ws_page ; Clear workspace page counter | |||
| 8ECF | STA ws_0d60 ; Clear workspace byte | |||
| 8ED2 | LDY #0 ; Offset 0 in receive block | |||
| 8ED4 | STA (net_rx_ptr),y ; Clear remote operation flag | |||
| 8ED6 | LDA #osbyte_issue_service_request ; OSBYTE &8F: issue service request | |||
| 8ED8 | LDX #1 ; X=1: workspace claim service | |||
| 8EDA | LDY #&0e ; Y=&0E: requested pages | |||
| 8EDC | JSR osbyte ; Issue paged ROM service call, Reason X=1 - Absolute public workspace claim | |||
| 8EDF | JSR store_ws_page_count ; Record final workspace allocation | |||
| 8EE2 | LDA last_break_type ; Load ROM present flag | |||
| 8EE5 | BEQ read_station_id ; Zero: first ROM init, skip FS setup | |||
| 8EE7 | JSR setup_ws_ptr ; Set up workspace pointers | |||
| 8EEA | STA fs_flags ; Clear FS flags | |||
| 8EED | TAY ; A=0, transfer to Y | |||
| 8EEE | .loop_zero_workspace←1← 8EF3 BNE | |||
| STA (fs_ws_ptr),y ; Clear byte in FS workspace | ||||
| 8EF0 | STA (nfs_workspace),y ; Clear byte in NFS workspace | |||
| 8EF2 | INY ; Advance index | |||
| 8EF3 | BNE loop_zero_workspace ; Loop until full page zeroed | |||
| 8EF5 | LDY #8 ; Offset 8 in receive block | |||
| 8EF7 | STA (net_rx_ptr),y ; Clear protection flags | |||
| 8EF9 | JSR copy_ps_data_y1c ; Initialise station identity block | |||
| 8EFC | LDY #2 ; Offset 2 in receive block | |||
| 8EFE | LDA #&fe ; A=&FE: default station ID marker | |||
| 8F00 | STA fs_server_stn ; Store default station low | |||
| 8F03 | STA (net_rx_ptr),y ; Store into receive block | |||
| 8F05 | LDA #0 ; A=0 | |||
| 8F07 | STA fs_server_net ; Clear station high byte | |||
| 8F0A | INY ; Y=&03 | |||
| 8F0B | STA (net_rx_ptr),y ; Store into receive block | |||
| 8F0D | LDY #3 ; Offset 3 in NFS workspace | |||
| 8F0F | STA (nfs_workspace),y ; Clear NFS workspace byte 3 | |||
| 8F11 | DEY ; Y=&02 | |||
| 8F12 | LDA #&eb ; A=&EB: default listen state | |||
| 8F14 | STA (nfs_workspace),y ; Store at NFS workspace offset 2 | |||
| 8F16 | LDX #3 ; X=3: init data byte count | |||
| 8F18 | .loop_copy_init_data←1← 8F1F BNE | |||
| LDA ws_init_data,x ; Load initialisation data byte | ||||
| 8F1B | STA fs_flags,x ; Store in workspace | |||
| 8F1E | DEX ; Decrement counter | |||
| 8F1F | BNE loop_copy_init_data ; More bytes: loop | |||
| 8F21 | STX ws_0d68 ; Clear workspace flag | |||
| 8F24 | STX fs_boot_option ; Clear workspace byte | |||
| 8F27 | JSR reset_spool_buf_state ; Initialise ADLC protection table | |||
| 8F2A | DEX ; X=&FF (underflow from X=0) | |||
| 8F2B | STX l0d71 ; Initialise workspace flag to &FF | |||
| 8F2E | .loop_alloc_handles←1← 8F3B BNE | |||
| LDA ws_page ; Get current workspace page | ||||
| 8F30 | JSR byte_to_2bit_index ; Allocate FS handle page | |||
| 8F33 | BCS done_alloc_handles ; Allocation failed: finish init | |||
| 8F35 | LDA #&3f ; A=&3F: default handle permissions | |||
| 8F37 | STA (nfs_workspace),y ; Store handle permissions | |||
| 8F39 | INC ws_page ; Advance to next page | |||
| 8F3B | BNE loop_alloc_handles ; Continue allocating: loop | |||
| 8F3D | .done_alloc_handles←1← 8F33 BCS | |||
| JSR restore_fs_context ; Restore FS context from saved state | ||||
| 8F40 | .read_station_id←1← 8EE5 BEQ | |||
| LDY station_id_disable_net_nmis ; Read station ID from hardware | ||||
| 8F43 | TYA ; Transfer to A | |||
| 8F44 | BNE store_station_id ; Non-zero: station ID valid | |||
| 8F46 | .error_bad_station←1← 8F4D 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 at &8F2B overlaps the | ||||
| ; high byte of JMP err_bad_station_num; byte at | ||||
| ; &8F2B itself (&92) is never read (BNE exits | ||||
| ; when X=0). Stores to l0d6e, l0d6f, l0d70. | ||||
| 8F49 | EQUB &FF ; l0d6e: init=&FF (retry count) | |||
| 8F4A | EQUB &28 ; l0d6f: init=&28 (40, receive poll count) | |||
| 8F4B | EQUB &0A ; l0d70: init=&0A (10, machine peek retries) | |||
| 8F4C | .store_station_id←1← 8F44 BNE | |||
| INY ; Increment station ID | ||||
| 8F4D | BEQ error_bad_station ; Overflow to 0: report error | |||
| 8F4F | LDY #1 ; Offset 1: station ID in recv block | |||
| 8F51 | STA (net_rx_ptr),y ; Store station ID | |||
| 8F53 | LDX #&40 ; X=&40: Econet flag byte | |||
| 8F55 | STX econet_flags ; Store Econet control flag | |||
| 8F58 | LDA #3 ; A=3: protection level | |||
| 8F5A | 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. |
|
| 8F5D | .init_adlc_and_vectors←1← 8B55 JSR |
| JSR adlc_init ; Initialise ADLC hardware | |
| 8F60 | LDA #&a8 ; OSBYTE &A8: read ROM pointer table |
| 8F62 | JSR osbyte_x0 ; Read ROM pointer table address |
| 8F65 | STX fs_error_ptr ; Store table pointer low |
| 8F67 | STY fs_crflag ; Store table pointer high |
| 8F69 | LDY #&36 ; Y=&36: NETV vector offset |
| 8F6B | STY netv ; Set NETV address |
| 8F6E | 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.
|
|||||||||
| 8F70 | .write_vector_entry←2← 8B5C JSR← 8F82 BNE | ||||||||
| LDA svc_dispatch_lo_offset,y ; Load vector address low byte | |||||||||
| 8F73 | STA (fs_error_ptr),y ; Store into extended vector table | ||||||||
| 8F75 | INY ; Advance to high byte | ||||||||
| 8F76 | LDA svc_dispatch_lo_offset,y ; Load vector address high byte | ||||||||
| 8F79 | STA (fs_error_ptr),y ; Store into extended vector table | ||||||||
| 8F7B | INY ; Advance to ROM ID byte | ||||||||
| 8F7C | LDA romsel_copy ; Load current ROM slot number | ||||||||
| 8F7E | STA (fs_error_ptr),y ; Store ROM ID in extended vector | ||||||||
| 8F80 | INY ; Advance to next vector entry | ||||||||
| 8F81 | DEX ; Decrement vector counter | ||||||||
| 8F82 | BNE write_vector_entry ; More vectors to set: loop | ||||||||
| 8F84 | JSR fscv_6_shutdown ; X=&FF Restore FS state if previously active | ||||||||
| 8F87 | JSR get_ws_page ; Get workspace page for ROM slot | ||||||||
| 8F8A | INY ; Advance Y past workspace page | ||||||||
| 8F8B | RTS ; Return | ||||||||
Restore FS context from saved workspaceCopies 8 bytes (offsets 2 to 9) 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. |
|
| 8F8C | .restore_fs_context←3← 8F3D JSR← 8FA8 JSR← A38E JMP |
| LDY #9 ; Y=9: end of FS context block | |
| 8F8E | .loop_restore_ctx←1← 8F96 BNE |
| LDA osword_ws_base,y ; Load FS context byte | |
| 8F91 | STA (net_rx_ptr),y ; Store into receive block |
| 8F93 | DEY ; Decrement index |
| 8F94 | CPY #1 ; Reached offset 1? |
| 8F96 | BNE loop_restore_ctx ; No: continue copying |
| 8F98 | 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. |
|
| 8F99 | .fscv_6_shutdown←1← 8F84 JSR |
| BIT fs_flags ; FS currently selected? | |
| 8F9C | BPL return_from_fs_shutdown ; No (bit 7 clear): return |
| 8F9E | LDY #0 ; Y=0 |
| 8FA0 | JSR process_all_fcbs ; Reset FS connection state |
| 8FA3 | LDA #osbyte_close_spool_exec ; OSBYTE &77: close SPOOL/EXEC |
| 8FA5 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 8FA8 | JSR restore_fs_context ; Restore FS context to receive block |
| 8FAB | LDY #&76 ; Y=&76: checksum range end |
| 8FAD | LDA #0 ; A=0: checksum accumulator |
| 8FAF | CLC ; Clear carry for addition |
| 8FB0 | .loop_checksum_byte←1← 8FB4 BPL |
| ADC fcb_count_lo,y ; Add byte from page &10 shadow | |
| 8FB3 | DEY ; Decrement index |
| 8FB4 | BPL loop_checksum_byte ; Loop until all bytes summed |
| 8FB6 | LDY #&77 ; Y=&77: checksum storage offset |
| 8FB8 | BPL store_ws_byte ; ALWAYS branch |
| 8FBA | .loop_copy_to_ws←1← 8FC0 BPL |
| LDA fcb_count_lo,y ; Load byte from page &10 shadow | |
| 8FBD | .store_ws_byte←1← 8FB8 BPL |
| STA (fs_ws_ptr),y ; Copy to FS workspace | |
| 8FBF | DEY ; Decrement index |
| 8FC0 | BPL loop_copy_to_ws ; Loop until all bytes copied |
| 8FC2 | LDA fs_flags ; Load FS flags |
| 8FC5 | AND #&7f ; Clear bit 7 (FS no longer selected) |
| 8FC7 | STA fs_flags ; Store updated flags |
| 8FCA | .return_from_fs_shutdown←1← 8F9C 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 sum' 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.
|
||||||
| 8FCB | .verify_ws_checksum←5← 9BBE JSR← 9D4E JSR← 9DEE JSR← 9E2F JSR← B5FB JSR | |||||
| PHP ; Save processor status | ||||||
| 8FCC | PHA ; Save A | |||||
| 8FCD | TYA ; Transfer Y to A | |||||
| 8FCE | PHA ; Save Y | |||||
| 8FCF | LDY #&76 ; Y=&76: checksum range end | |||||
| 8FD1 | LDA #0 ; A=0: checksum accumulator | |||||
| 8FD3 | CLC ; Clear carry for addition | |||||
| 8FD4 | .loop_sum_ws←1← 8FD7 BPL | |||||
| ADC (fs_ws_ptr),y ; Add byte from FS workspace | ||||||
| 8FD6 | DEY ; Decrement index | |||||
| 8FD7 | BPL loop_sum_ws ; Loop until all bytes summed | |||||
| 8FD9 | LDY #&77 ; Y=&77: checksum storage offset | |||||
| 8FDB | CMP (fs_ws_ptr),y ; Compare with stored checksum | |||||
| 8FDD | BNE error_net_checksum ; Mismatch: raise checksum error | |||||
| 8FDF | PLA ; Restore Y | |||||
| 8FE0 | TAY ; Transfer to Y | |||||
| 8FE1 | PLA ; Restore A | |||||
| 8FE2 | PLP ; Restore processor status | |||||
| 8FE3 | RTS ; Return (checksum valid) | |||||
| 8FE4 | .error_net_checksum←2← 8B31 JMP← 8FDD BNE | |||||
| LDA #&aa ; Error number &AA | ||||||
| 8FE6 | JSR error_bad_inline ; Raise 'net checksum' error | |||||
| 8FE9 | EQUS "net sum." | |||||
| fall through ↓ | ||||||
Print Econet station number and clock statusUses print_inline to output 'Econet Station ', then reads the station ID from offset 1 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. |
|
| 8FF1 | .print_station_id←2← 8CB6 JMP← 8CE3 JSR |
| JSR print_inline ; Print 'Econet Station ' prefix | |
| 8FF4 | EQUS "Econet Station " ; Print 'Econet Station ' via inline |
| 9003 | LDY #1 ; Y=1: station number offset in RX block |
| 9005 | LDA (net_rx_ptr),y ; Load station ID from receive block |
| 9007 | JSR print_num_no_leading ; Print station number as decimal |
| 900A | LDA #&20 ; Space character |
| 900C | BIT econet_control23_or_status2 ; Check ADLC status register 2 |
| 900F | BEQ done_print_newline ; Clock present: skip warning |
| 9011 | JSR print_inline ; Print ' No Clock' via inline |
| 9014 | EQUS " No Clock" |
| 901D | NOP ; NOP (string terminator) |
| 901E | .done_print_newline←1← 900F BEQ |
| JSR osnewl ; Write newline (characters 10 and 13) | |
| 9021 | .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. | |
| 9022 | .cmd_syntax_strings←1← 8BE1 LDA |
| .syn_opt_dir←1← 8BE1 LDA | |
| EQUS "(<dir>)" ; Syn 1: *Dir, *LCat, *LEx, *Wipe | |
| 9029 | EQUB &00 ; Null terminator |
| 902A | .syn_iam |
| EQUS "(<stn. id.>) <user id.> " ; Syn 2: *I Am (login) | |
| 9042 | EQUB &0D ; Line break |
| 9043 | EQUS "((:<CR>)<password>)" ; Syn 2 continued: password clause |
| 9056 | EQUB &00 ; Null terminator |
| 9057 | .syn_object |
| EQUS "<object>" ; Syn 3: *Delete, *FS, *Remove | |
| 905F | EQUB &00 ; Null terminator |
| 9060 | .syn_file_offset |
| EQUS "<filename> (<offset> " ; Syn 4: *Dump | |
| 9075 | EQUB &0D ; Line break |
| 9076 | EQUS "(<address>))" ; Syn 4 continued: address clause |
| 9082 | EQUB &00 ; Null terminator |
| 9083 | .syn_dir |
| EQUS "<dir>" ; Syn 5: *Lib | |
| 9088 | EQUB &00 ; Null terminator |
| 9089 | .syn_dir_num |
| EQUS "<dir> (<number>)" ; Syn 6: *CDir | |
| 9099 | EQUB &00 ; Null terminator |
| 909A | .syn_password |
| EQUS "(:<CR>) <password> " ; Syn 7: *Pass | |
| 90AD | EQUB &0D ; Line break |
| 90AE | EQUS "<new password>" ; Syn 7 continued: new password |
| 90BC | EQUB &00 ; Null terminator |
| 90BD | .syn_ps_type |
| EQUS "(<stn. id.>|<ps type>)" ; Syn 8: *PS, *Pollps | |
| 90D3 | EQUB &00 ; Null terminator |
| 90D4 | .syn_access |
| EQUS "<object> (L)(W)(R)(/(W)(R))" ; Syn 9: *Access | |
| 90EF | EQUB &00 ; Null terminator |
| 90F0 | .syn_rename |
| EQUS "<filename> <new filename>" ; Syn 10: *Rename | |
| 9109 | EQUB &00 ; Null terminator |
| 910A | .syn_opt_stn |
| EQUS "(<stn. id.>)" ; Syn 11: (station id. argument) | |
| 9116 | EQUB &00 ; Null terminator |
| 9117 | .syn_filename |
| EQUS "<filename>" ; Syn 12: *Print, *Type | |
| 9121 | EQUB &00 ; Null terminator |
| ; Command syntax string offset table | |
| ; 13 offsets into cmd_syntax_strings (&9022). | |
| ; 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. | |
| 9122 | .cmd_syntax_table←1← 8BDC LDA |
| EQUB syn_iam - cmd_syntax_strings - 2 ; Idx 0: (no synta x) | |
| 9123 | EQUB (syn_opt_dir - cmd_syntax_strings - 1) AND &FF ; Idx 1: "(< dir >)" (Y wraps via &FF) |
| 9124 | EQUB syn_iam - cmd_syntax_strings - 1 ; Idx 2: "(< stn. id.>) <user id .>... " |
| 9125 | EQUB syn_object - cmd_syntax_strings - 1 ; Idx 3: "< objec t>" |
| 9126 | EQUB syn_file_offset - cmd_syntax_strings - 1 ; Idx 4: "< filen ame> (< offse t >...) " |
| 9127 | EQUB syn_dir - cmd_syntax_strings - 1 ; Idx 5: "< dir>" |
| 9128 | EQUB syn_dir_num - cmd_syntax_strings - 1 ; Idx 6: "< dir> (< numbe r>)" |
| 9129 | EQUB syn_password - cmd_syntax_strings - 1 ; Idx 7: "(:< CR>) < passw ord >..." |
| 912A | EQUB syn_ps_type - cmd_syntax_strings - 1 ; Idx 8: "(< stn. id.>| <ps type >)" |
| 912B | EQUB syn_access - cmd_syntax_strings - 1 ; Idx 9: "< objec t> (L)( W)(R )..." |
| 912C | EQUB syn_rename - cmd_syntax_strings - 1 ; Idx 10: "< filen ame> <new filen ame>" |
| 912D | EQUB syn_opt_stn - cmd_syntax_strings - 1 ; Idx 11: "(< stn. id .>)" |
| 912E | 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.
|
|||||||
| 912F | .print_hex_byte←4← 9A66 JSR← AE11 JSR← BA6A JSR← BB04 JSR | ||||||
| PHA ; Save full byte | |||||||
| 9130 | LSR ; Shift high nybble to low | ||||||
| 9131 | LSR ; Continue shifting | ||||||
| 9132 | LSR ; Continue shifting | ||||||
| 9133 | LSR ; High nybble now in bits 0-3 | ||||||
| 9134 | JSR print_hex_nybble ; Print high nybble as hex digit | ||||||
| 9137 | 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.
|
||||
| 9138 | .print_hex_nybble←1← 9134 JSR | |||
| AND #&0f ; Mask to low nybble | ||||
| 913A | CMP #&0a ; Digit >= &0A? | |||
| 913C | BCC add_ascii_base ; No: skip letter adjustment | |||
| 913E | ADC #6 ; Add 7 to get 'A'-'F' (6 + carry) | |||
| 9140 | .add_ascii_base←1← 913C BCC | |||
| ADC #&30 ; Add &30 for ASCII '0'-'9' or 'A'-'F' | ||||
| 9142 | 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.
|
||||||||
| 9145 | .print_inline←35← 8BB6 JSR← 8BF8 JSR← 8C9F JSR← 8FF1 JSR← 9011 JSR← 953A JSR← ADC0 JSR← ADCA JSR← ADD8 JSR← ADE3 JSR← ADFF JSR← AE14 JSR← AE27 JSR← AE36 JSR← AF53 JSR← B0A2 JSR← B0AE JSR← B0C5 JSR← B0CF JSR← B0DA JSR← B1AA JSR← B24C JSR← B261 JSR← B284 JSR← B291 JSR← B2A0 JSR← B2B0 JSR← B2BF JSR← B3C4 JSR← B3E4 JSR← B431 JSR← BA86 JSR← BA9F JSR← BAAC JSR← BAE0 JSR | |||||||
| PLA ; Pop return address (low) — points to last byte of JSR | ||||||||
| 9146 | STA fs_error_ptr ; Store as string pointer low | |||||||
| 9148 | PLA ; Pop return address (high) | |||||||
| 9149 | STA fs_crflag ; Store as string pointer high | |||||||
| 914B | LDY #0 ; Y=0: index for indirect loads | |||||||
| 914D | .loop_next_char←1← 9168 JMP | |||||||
| INC fs_error_ptr ; Advance pointer to next character | ||||||||
| 914F | BNE load_char ; No page crossing | |||||||
| 9151 | INC fs_crflag ; Carry into high byte | |||||||
| 9153 | .load_char←1← 914F BNE | |||||||
| LDA (fs_error_ptr),y ; Load next byte from inline string | ||||||||
| 9155 | BMI resume_caller ; Bit 7 set? Done — this byte is the next opcode | |||||||
| 9157 | LDA fs_error_ptr ; Save string pointer on stack | |||||||
| 9159 | PHA ; (push low byte) | |||||||
| 915A | LDA fs_crflag ; Save pointer high byte | |||||||
| 915C | PHA ; (push high byte) | |||||||
| 915D | LDA (fs_error_ptr),y ; Reload character (pointer may have been clobbered) | |||||||
| 915F | JSR osasci ; Print character via OSASCI Write character | |||||||
| 9162 | PLA ; Restore string pointer high | |||||||
| 9163 | STA fs_crflag ; Store pointer high | |||||||
| 9165 | PLA ; Restore string pointer low | |||||||
| 9166 | STA fs_error_ptr ; Store pointer low | |||||||
| 9168 | JMP loop_next_char ; Loop for next character | |||||||
| 916B | .resume_caller←1← 9155 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. |
|
| 916E | .parse_addr_arg←5← 8D98 JSR← 8DA4 JSR← A0AD JSR← A0C2 JSR← AD24 JSR |
| LDA #0 ; Clear accumulator | |
| 9170 | STA fs_load_addr_2 ; Initialise result to zero |
| 9172 | LDA (fs_crc_lo),y ; Get first character of argument |
| 9174 | CMP #&26 ; Is it '&' (hex prefix)? |
| 9176 | BNE next_dec_char ; No: try decimal path |
| 9178 | INY ; Skip '&' prefix |
| 9179 | LDA (fs_crc_lo),y ; Get first hex digit |
| 917B | BCS check_digit_range ; C always set from CMP: validate digit |
| 917D | .next_hex_char←1← 91AC BCC |
| INY ; Advance to next character | |
| 917E | LDA (fs_crc_lo),y ; Get next character |
| 9180 | CMP #&2e ; Is it '.' (net.station separator)? |
| 9182 | BEQ handle_dot_sep ; Yes: handle dot separator |
| 9184 | CMP #&21 ; Below '!' (space/control)? |
| 9186 | BCC done_parse_num ; Yes: end of number |
| 9188 | .check_digit_range←1← 917B BCS |
| CMP #&30 ; Below '0'? | |
| 918A | BCC skip_if_not_hex ; Not a digit: bad hex |
| 918C | CMP #&3a ; Above '9'? |
| 918E | BCC extract_digit_value ; Decimal digit: extract value |
| 9190 | AND #&5f ; Force uppercase |
| 9192 | ADC #&b8 ; Map 'A'-'F' to &FA-&FF |
| 9194 | BCS err_bad_hex ; Overflow: not A-F |
| 9196 | CMP #&fa ; Valid hex letter (A-F)? |
| 9198 | .skip_if_not_hex←1← 918A BCC |
| BCC err_bad_hex ; Below A: bad hex | |
| 919A | .extract_digit_value←1← 918E BCC |
| AND #&0f ; Extract digit value (0-15) | |
| 919C | STA fs_load_addr_3 ; Save current digit |
| 919E | LDA fs_load_addr_2 ; Load running result |
| 91A0 | CMP #&10 ; Would shift overflow a byte? |
| 91A2 | BCS error_overflow ; Yes: overflow error |
| 91A4 | ASL ; Shift result left 4 (x16) |
| 91A5 | ASL ; (shift 2) |
| 91A6 | ASL ; (shift 3) |
| 91A7 | ASL ; (shift 4) |
| 91A8 | ADC fs_load_addr_3 ; Add new hex digit |
| 91AA | STA fs_load_addr_2 ; Store updated result |
| 91AC | BCC next_hex_char ; Loop for next hex digit |
| 91AE | .next_dec_char←2← 9176 BNE← 91D8 BNE |
| LDA (fs_crc_lo),y ; Get current character | |
| 91B0 | CMP #&2e ; Is it '.' (net.station separator)? |
| 91B2 | BEQ handle_dot_sep ; Yes: handle dot separator |
| 91B4 | CMP #&21 ; Below '!' (space/control)? |
| 91B6 | BCC done_parse_num ; Yes: end of number |
| 91B8 | JSR is_dec_digit_only ; Is it a decimal digit? |
| 91BB | BCC error_bad_number ; No: 'Bad number' error |
| 91BD | AND #&0f ; Extract digit value (0-9) |
| 91BF | STA fs_load_addr_3 ; Save current digit |
| 91C1 | ASL fs_load_addr_2 ; result * 2 |
| 91C3 | BCS error_overflow ; Overflow |
| 91C5 | LDA fs_load_addr_2 ; Load result * 2 |
| 91C7 | ASL ; result * 4 |
| 91C8 | BCS error_overflow ; Overflow |
| 91CA | ASL ; result * 8 |
| 91CB | BCS error_overflow ; Overflow |
| 91CD | ADC fs_load_addr_2 ; * 8 + * 2 = result * 10 |
| 91CF | BCS error_overflow ; Overflow |
| 91D1 | ADC fs_load_addr_3 ; result * 10 + new digit |
| 91D3 | BCS error_overflow ; Overflow |
| 91D5 | STA fs_load_addr_2 ; Store updated result |
| 91D7 | INY ; Advance to next character |
| 91D8 | BNE next_dec_char ; Loop (always branches) |
| 91DA | .done_parse_num←2← 9186 BCC← 91B6 BCC |
| LDA fs_work_4 ; Check parsing mode | |
| 91DC | BPL validate_station ; Bit 7 clear: net.station mode |
| 91DE | LDA fs_load_addr_2 ; Decimal-only mode: get result |
| 91E0 | BEQ error_bad_param ; Zero: 'Bad parameter' |
| 91E2 | RTS ; Return with result in A |
| 91E3 | .validate_station←1← 91DC BPL |
| LDA fs_load_addr_2 ; Get parsed station number | |
| 91E5 | CMP #&ff ; Station 255 is reserved |
| 91E7 | BEQ err_bad_station_num ; 255: 'Bad station number' |
| 91E9 | LDA fs_load_addr_2 ; Reload result |
| 91EB | BNE return_parsed ; Non-zero: valid station |
| 91ED | LDA fs_work_4 ; Zero result: check if dot was seen |
| 91EF | BEQ err_bad_station_num ; No dot and zero: 'Bad station number' |
| 91F1 | DEY ; Check character before current pos |
| 91F2 | LDA (fs_crc_lo),y ; Load previous character |
| 91F4 | INY ; Restore Y |
| 91F5 | EOR #&2e ; Was previous char '.'? |
| 91F7 | BNE err_bad_station_num ; No: 'Bad station number' |
| 91F9 | .return_parsed←1← 91EB BNE |
| SEC ; C=1: number was parsed | |
| 91FA | RTS ; Return (result in fs_load_addr_2) |
| 91FB | .handle_dot_sep←2← 9182 BEQ← 91B2 BEQ |
| LDA fs_work_4 ; Check if dot already seen | |
| 91FD | BNE error_bad_number ; Already seen: 'Bad number' |
| 91FF | INC fs_work_4 ; Set dot-seen flag |
| 9201 | LDA fs_load_addr_2 ; Get network number (before dot) |
| 9203 | CMP #&ff ; Network 255 is reserved |
| 9205 | BEQ error_bad_net_num ; 255: 'Bad network number' |
| 9207 | RTS ; Return to caller with network part |
| 9208 | .err_bad_hex←3← 9194 BCS← 9198 BCC← BB6B JMP |
| LDA #&f1 ; Error code &F1 | |
| 920A | JSR error_bad_inline ; Generate 'Bad hex' error |
| 920D | EQUS "hex." |
| 9211 | .error_overflow←6← 91A2 BCS← 91C3 BCS← 91C8 BCS← 91CB BCS← 91CF BCS← 91D3 BCS |
| BIT fs_work_4 ; Test parsing mode | |
| 9213 | BMI error_bad_param ; Decimal mode: 'Bad parameter' |
| 9215 | .err_bad_station_num←4← 8F46 JMP← 91E7 BEQ← 91EF BEQ← 91F7 BNE |
| LDA #&d0 ; Error code &D0 | |
| 9217 | JSR error_bad_inline ; Generate 'Bad station number' error |
| 921A | EQUS "station number." |
| 9229 | .error_bad_number←2← 91BB BCC← 91FD BNE |
| LDA #&f0 ; Error code &F0 | |
| 922B | JSR error_bad_inline ; Generate 'Bad number' error |
| 922E | EQUS "number." |
| 9235 | .error_bad_param←2← 91E0 BEQ← 9213 BMI |
| LDA #&94 ; Error code &94 | |
| 9237 | JSR error_bad_inline ; Generate 'Bad parameter' error |
| 923A | EQUS "parameter." |
| 9244 | .error_bad_net_num←1← 9205 BEQ |
| LDA #&d1 ; Error code &D1 | |
| 9246 | JSR error_bad_inline ; Generate 'Bad network number' error |
| 9249 | 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.
|
|||||||
| 9258 | .is_decimal_digit←3← 8D93 JSR← B005 JSR← B1DC JSR | ||||||
| CMP #&26 ; Is it '&' (hex prefix)? | |||||||
| 925A | BEQ return_from_digit_test ; Yes: return C set (not decimal) | ||||||
| 925C | CMP #&2e ; Is it '.' (separator)? | ||||||
| 925E | 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.
|
|||||||
| 9260 | .is_dec_digit_only←1← 91B8 JSR | ||||||
| CMP #&3a ; Above '9'? | |||||||
| 9262 | BCS not_a_digit ; Yes: not a digit | ||||||
| 9264 | CMP #&30 ; Below '0'? C clear if so | ||||||
| 9266 | .return_from_digit_test←2← 925A BEQ← 925E BEQ | ||||||
| RTS ; Return: C set if '0'-'9' | |||||||
| 9267 | .not_a_digit←1← 9262 BCS | ||||||
| CLC ; C=0: not a digit | |||||||
| 9268 | 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 &9286. Called by check_and_setup_txcb for owner and public access.
|
||||
| 9269 | .get_access_bits←2← 9B20 JSR← 9B4C JSR | |||
| LDY #&0e ; Offset &0E in directory entry | ||||
| 926B | LDA (fs_options),y ; Load raw access byte | |||
| 926D | AND #&3f ; Mask to 6 access bits | |||
| 926F | LDX #4 ; X=4: start encoding at bit 4 | |||
| 9271 | 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 (&9286). Called by send_txcb_swap_addrs and check_and_setup_txcb.
|
|||||||
| 9273 | .get_prot_bits←2← 9A2A JSR← 9B69 JSR | ||||||
| AND #&1f ; Mask to 5 protection bits | |||||||
| 9275 | LDX #&ff ; X=&FF: start encoding at bit 0 | ||||||
| 9277 | .begin_prot_encode←1← 9271 BNE | ||||||
| STA fs_error_ptr ; Save remaining bits | |||||||
| 9279 | LDA #0 ; Clear encoded result | ||||||
| 927B | .loop_encode_prot←1← 9283 BNE | ||||||
| INX ; Advance to next table position | |||||||
| 927C | LSR fs_error_ptr ; Shift out lowest source bit | ||||||
| 927E | BCC skip_clear_prot ; Bit clear: skip this position | ||||||
| 9280 | ORA prot_bit_encode_table,x ; Bit set: OR in encoded value | ||||||
| 9283 | .skip_clear_prot←1← 927E BCC | ||||||
| BNE loop_encode_prot ; More bits to process | |||||||
| 9285 | 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). | |||||||
| 9286 | .prot_bit_encode_table←1← 9280 ORA | ||||||
| EQUB &50 ; Bit 0: &50 = %01010000 (bits 4,6) | |||||||
| 9287 | EQUB &20 ; Bit 1: &20 = %00100000 (bit 5) | ||||||
| 9288 | EQUB &05 ; Bit 2: &05 = %00000101 (bits 0,2) | ||||||
| 9289 | EQUB &02 ; Bit 3: &02 = %00000010 (bit 1) | ||||||
| 928A | EQUB &88 ; Bit 4: &88 = %10001000 (bits 3,7) | ||||||
| 928B | EQUB &04 ; Bit 0: &04 = %00000100 (bit 2) | ||||||
| 928C | EQUB &08 ; Bit 1: &08 = %00001000 (bit 3) | ||||||
| 928D | EQUB &80 ; Bit 2: &80 = %10000000 (bit 7) | ||||||
| 928E | EQUB &10 ; Bit 3: &10 = %00010000 (bit 4) | ||||||
| 928F | EQUB &01 ; Bit 4: &01 = %00000001 (bit 0) | ||||||
| 9290 | 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.
|
||||||
| 9291 | .set_text_and_xfer_ptr←1← A114 JSR | |||||
| STX os_text_ptr ; Set text pointer low | ||||||
| 9293 | 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.
|
||||||||
| 9295 | .set_xfer_params←5← 8E33 JSR← 9935 JSR← 9D51 JSR← 9E32 JSR← AD80 JSR | |||||||
| STA fs_last_byte_flag ; Store transfer byte count | ||||||||
| 9297 | STX fs_crc_lo ; Store source pointer low | |||||||
| 9299 | 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.
|
||||||
| 929B | .set_options_ptr←2← 9BC3 JSR← B984 JSR | |||||
| STX fs_options ; Store options pointer low | ||||||
| 929D | STY fs_block_offset ; Store options pointer high | |||||
| 929F | .clear_escapable←1← 9885 JMP | |||||
| PHP ; Save processor flags | ||||||
| 92A0 | LSR need_release_tube ; Clear bit 0 of escape flag | |||||
| 92A2 | PLP ; Restore processor flags | |||||
| 92A3 | 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.
|
||||
| 92A4 | .cmp_5byte_handle←2← 9998 JSR← 9A9B JSR | |||
| LDX #4 ; Compare 5 bytes (indices 4 down to 1) | ||||
| 92A6 | .loop_cmp_handle←1← 92AD BNE | |||
| LDA addr_work,x ; Load byte from handle buffer | ||||
| 92A8 | EOR fs_load_addr_3,x ; Compare with channel handle | |||
| 92AA | BNE return_from_cmp_handle ; Mismatch: return Z=0 | |||
| 92AC | DEX ; Next byte | |||
| 92AD | BNE loop_cmp_handle ; Loop until all compared | |||
| 92AF | .return_from_cmp_handle←1← 92AA BNE | |||
| RTS ; Return: Z=1 if all 5 matched | ||||
| 92B0 | .fscv_7_read_handles | |||
| LDX #&20 ; Unreachable code | ||||
| 92B2 | LDY #&2f ; (dead) | |||
| 92B4 | 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.
|
||||
| 92B5 | .set_conn_active←2← 9C48 JSR← 9E8F JSR | |||
| PHP ; Save processor flags | ||||
| 92B6 | PHA ; Save A | |||
| 92B7 | TXA ; Transfer X to A | |||
| 92B8 | PHA ; Save original X | |||
| 92B9 | TSX ; Get stack pointer | |||
| 92BA | LDA stack_page_2,x ; Read original A from stack | |||
| 92BD | JSR attr_to_chan_index ; Convert to channel index | |||
| 92C0 | BMI done_conn_flag ; No channel found: skip | |||
| 92C2 | LDA #&40 ; Bit 6: connection active flag | |||
| 92C4 | ORA chan_status,x ; Set active flag in channel table | |||
| 92C7 | STA chan_status,x ; Store updated status | |||
| 92CA | 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.
|
||||
| 92CC | .clear_conn_active←2← 9CA9 JSR← 9E8A JSR | |||
| PHP ; Save processor flags | ||||
| 92CD | PHA ; Save A | |||
| 92CE | TXA ; Transfer X to A | |||
| 92CF | PHA ; Save original X | |||
| 92D0 | TSX ; Get stack pointer | |||
| 92D1 | LDA stack_page_2,x ; Read original A from stack | |||
| 92D4 | JSR attr_to_chan_index ; Convert to channel index | |||
| 92D7 | BMI done_conn_flag ; No channel found: skip | |||
| 92D9 | LDA #&bf ; Bit 6 clear mask (&BF = ~&40) | |||
| 92DB | AND chan_status,x ; Clear active flag in channel table | |||
| 92DE | STA chan_status,x ; Store updated status | |||
| 92E1 | .done_conn_flag←3← 92C0 BMI← 92CA BNE← 92D7 BMI | |||
| PLA ; Restore X | ||||
| 92E2 | TAX ; Transfer back to X | |||
| 92E3 | PLA ; Restore A | |||
| 92E4 | PLP ; Restore processor flags | |||
| 92E5 | 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. |
|
| 92E6 | .cmd_fs_operation |
| JSR copy_fs_cmd_name ; Copy command name to TX buffer | |
| 92E9 | TXA ; Save buffer position |
| 92EA | PHA ; Push it |
| 92EB | JSR parse_quoted_arg ; Parse filename (handles quoting) |
| 92EE | JSR parse_access_prefix ; Parse owner/public access prefix |
| 92F1 | PLA ; Restore buffer position |
| 92F2 | TAX ; Transfer to X |
| 92F3 | JSR check_not_ampersand ; Reject '&' character in filename |
| 92F6 | CMP #&0d ; End of line? |
| 92F8 | BNE read_filename_char ; No: copy filename chars to buffer |
| 92FA | .error_bad_filename←3← 930E BEQ← 9409 JMP← AEDF JMP |
| LDA #&cc ; Error number &CC | |
| 92FC | JSR error_bad_inline ; Raise 'Bad file name' error |
| 92FF | 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. |
|
| 9309 | .check_not_ampersand←2← 92F3 JSR← 9311 JSR |
| LDA fs_filename_buf ; Load first parsed character | |
| 930C | CMP #&26 ; Is it '&'? |
| 930E | BEQ error_bad_filename ; Yes: invalid filename |
| 9310 | RTS ; Return |
| 9311 | .read_filename_char←3← 92F8 BNE← 931F JMP← 93DA JMP |
| JSR check_not_ampersand ; Reject '&' in current char | |
| 9314 | STA fs_cmd_data,x ; Store character in TX buffer |
| 9317 | INX ; Advance buffer pointer |
| 9318 | CMP #&0d ; End of line? |
| 931A | BEQ send_fs_request ; Yes: send request to file server |
| 931C | JSR strip_token_prefix ; Strip BASIC token prefix byte |
| 931F | JMP read_filename_char ; Continue reading filename chars |
| 9322 | .send_fs_request←2← 931A BEQ← 9402 JMP |
| LDY #0 ; Y=0: no extra dispatch offset | |
| 9324 | 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.
|
||||||
| 9327 | .copy_fs_cmd_name←2← 92E6 JSR← 938B JSR | |||||
| TYA ; Save command line offset | ||||||
| 9328 | PHA ; Push it | |||||
| 9329 | .loop_scan_flag←1← 932D BPL | |||||
| DEX ; Scan backwards in command table | ||||||
| 932A | LDA cmd_table_fs,x ; Load table byte | |||||
| 932D | BPL loop_scan_flag ; Bit 7 clear: keep scanning | |||||
| 932F | INX ; Point past flag byte to name start | |||||
| 9330 | LDY #0 ; Y=0: TX buffer offset | |||||
| 9332 | .loop_copy_name←1← 933C BNE | |||||
| LDA cmd_table_fs,x ; Load command name character | ||||||
| 9335 | BMI append_space ; Bit 7 set: end of name | |||||
| 9337 | STA fs_cmd_data,y ; Store character in TX buffer | |||||
| 933A | INX ; Advance table pointer | |||||
| 933B | INY ; Advance buffer pointer | |||||
| 933C | BNE loop_copy_name ; Continue copying name | |||||
| 933E | .append_space←1← 9335 BMI | |||||
| LDA #&20 ; Space separator | ||||||
| 9340 | STA fs_cmd_data,y ; Append space after command name | |||||
| 9343 | INY ; Advance buffer pointer | |||||
| 9344 | TYA ; Transfer length to A | |||||
| 9345 | TAX ; And to X (buffer position) | |||||
| 9346 | PLA ; Restore command line offset | |||||
| 9347 | TAY ; Transfer to Y | |||||
| 9348 | .return_from_copy_cmd_name←1← 937D 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. |
|
| 9349 | .parse_quoted_arg←2← 92EB JSR← 9393 JSR |
| LDA #0 ; A=0: no quote mode | |
| 934B | TAX ; X=&00 |
| 934C | STA quote_mode ; Clear quote tracking flag |
| 934F | .loop_skip_spaces←1← 9356 BNE |
| LDA (fs_crc_lo),y ; Load char from command line | |
| 9351 | CMP #&20 ; Space? |
| 9353 | BNE check_open_quote ; No: check for opening quote |
| 9355 | INY ; Skip leading space |
| 9356 | BNE loop_skip_spaces ; Continue skipping spaces |
| 9358 | .check_open_quote←1← 9353 BNE |
| CMP #&22 ; Double-quote character? | |
| 935A | BNE loop_copy_arg_char ; No: start reading filename |
| 935C | INY ; Skip opening quote |
| 935D | EOR quote_mode ; Toggle quote mode flag |
| 9360 | STA quote_mode ; Store updated quote mode |
| 9363 | .loop_copy_arg_char←2← 935A BNE← 9378 BNE |
| LDA (fs_crc_lo),y ; Load char from command line | |
| 9365 | CMP #&22 ; Double-quote? |
| 9367 | BNE store_arg_char ; No: store character as-is |
| 9369 | EOR quote_mode ; Toggle quote mode |
| 936C | STA quote_mode ; Store updated quote mode |
| 936F | LDA #&20 ; Replace closing quote with space |
| 9371 | .store_arg_char←1← 9367 BNE |
| STA fs_filename_buf,x ; Store character in parse buffer | |
| 9374 | INY ; Advance command line pointer |
| 9375 | INX ; Advance buffer pointer |
| 9376 | CMP #&0d ; End of line? |
| 9378 | BNE loop_copy_arg_char ; No: continue parsing |
| 937A | LDA quote_mode ; Check quote balance flag |
| 937D | BEQ return_from_copy_cmd_name ; Balanced: return OK |
| 937F | LDA brk_ptr ; Unbalanced: use BRK ptr for error |
| 9381 | JSR error_bad_inline ; Raise 'Bad string' error |
| 9384 | 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. |
|
| 938B | .cmd_rename |
| JSR copy_fs_cmd_name ; Copy 'Rename ' to TX buffer | |
| 938E | TXA ; Save buffer position |
| 938F | PHA ; Push it |
| 9390 | JSR mask_owner_access ; Set owner-only access mask |
| 9393 | JSR parse_quoted_arg ; Parse first filename (quoted) |
| 9396 | JSR parse_access_prefix ; Parse access prefix |
| 9399 | PLA ; Restore buffer position |
| 939A | TAX ; Transfer to X |
| 939B | .loop_copy_rename←1← 93B9 JMP |
| LDA fs_filename_buf ; Load next parsed character | |
| 939E | CMP #&0d ; End of line? |
| 93A0 | BNE store_rename_char ; No: store character |
| 93A2 | .error_bad_rename←1← 93D8 BNE |
| LDA #&b0 ; Error number &B0 | |
| 93A4 | JSR error_bad_inline ; Raise 'Bad rename' error |
| 93A7 | EQUS "rename." |
| 93AE | .store_rename_char←1← 93A0 BNE |
| STA fs_cmd_data,x ; Store character in TX buffer | |
| 93B1 | INX ; Advance buffer pointer |
| 93B2 | CMP #&20 ; Space (name separator)? |
| 93B4 | BEQ skip_rename_spaces ; Yes: first name complete |
| 93B6 | JSR strip_token_prefix ; Strip BASIC token prefix byte |
| 93B9 | JMP loop_copy_rename ; Continue copying first filename |
| 93BC | .skip_rename_spaces←2← 93B4 BEQ← 93C4 BEQ |
| JSR strip_token_prefix ; Strip token from next char | |
| 93BF | LDA fs_filename_buf ; Load next parsed character |
| 93C2 | CMP #&20 ; Still a space? |
| 93C4 | BEQ skip_rename_spaces ; Yes: skip multiple spaces |
| 93C6 | LDA fs_lib_flags ; Save current FS options |
| 93C9 | PHA ; Push them |
| 93CA | JSR mask_owner_access ; Reset access mask for second name |
| 93CD | TXA ; Save buffer position |
| 93CE | PHA ; Push it |
| 93CF | JSR parse_access_prefix ; Parse access prefix for second name |
| 93D2 | PLA ; Restore buffer position |
| 93D3 | TAX ; Transfer to X |
| 93D4 | PLA ; Restore original FS options |
| 93D5 | CMP fs_lib_flags ; Options changed (cross-FS)? |
| 93D8 | BNE error_bad_rename ; Yes: error (can't rename across FS) |
| 93DA | 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. |
|
| 93DD | .cmd_dir |
| LDA (fs_crc_lo),y ; Get first char of argument | |
| 93DF | CMP #&26 ; Is it '&' (FS selector prefix)? |
| 93E1 | BNE dir_pass_simple ; No: simple dir change |
| 93E3 | INY ; Skip '&' |
| 93E4 | LDA (fs_crc_lo),y ; Get char after '&' |
| 93E6 | CMP #&0d ; End of line? |
| 93E8 | BEQ setup_fs_root ; Yes: '&' alone (root directory) |
| 93EA | CMP #&20 ; Space? |
| 93EC | BNE check_fs_dot ; No: check for '.' separator |
| 93EE | .setup_fs_root←1← 93E8 BEQ |
| LDY #&ff ; Y=&FF: pre-increment for loop | |
| 93F0 | .loop_copy_fs_num←1← 93F8 BNE |
| INY ; Advance index | |
| 93F1 | LDA (fs_crc_lo),y ; Load char from command line |
| 93F3 | STA fs_cmd_data,y ; Copy to TX buffer |
| 93F6 | CMP #&26 ; Is it '&' (end of FS path)? |
| 93F8 | BNE loop_copy_fs_num ; No: keep copying |
| 93FA | LDA #&0d ; Replace '&' with CR terminator |
| 93FC | STA fs_cmd_data,y ; Store CR in buffer |
| 93FF | INY ; Point past CR |
| 9400 | TYA ; Transfer length to A |
| 9401 | TAX ; And to X (byte count) |
| 9402 | JMP send_fs_request ; Send directory request to server |
| 9405 | .check_fs_dot←1← 93EC BNE |
| CMP #&2e ; Is char after '&' a dot? | |
| 9407 | BEQ parse_fs_dot_dir ; Yes: &FS.dir format |
| 9409 | JMP error_bad_filename ; No: invalid syntax |
| 940C | .parse_fs_dot_dir←1← 9407 BEQ |
| INY ; Skip '.' | |
| 940D | STY fs_load_addr ; Save dir path start position |
| 940F | LDA #4 ; FS command 4: examine directory |
| 9411 | STA fs_cmd_data ; Store in TX buffer |
| 9414 | LDA fs_lib_flags ; Load FS flags |
| 9417 | ORA #&40 ; Set bit 6 (FS selection active) |
| 9419 | STA fs_lib_flags ; Store updated flags |
| 941C | LDX #1 ; X=1: buffer offset |
| 941E | JSR copy_arg_validated ; Copy FS number to buffer |
| 9421 | LDY #&12 ; Y=&12: select FS command code |
| 9423 | JSR save_net_tx_cb ; Send FS selection command |
| 9426 | LDA fs_cmd_data ; Load reply status |
| 9429 | CMP #2 ; Status 2 (found)? |
| 942B | BEQ dir_found_send ; Yes: proceed to dir change |
| 942D | LDA #&d6 ; Error number &D6 |
| 942F | JSR error_inline_log ; Raise 'Not found' error |
| 9432 | EQUS "Not found." |
| 943C | .dir_found_send←1← 942B BEQ |
| LDA fs_csd_handle ; Load current FS station byte | |
| 943F | STA fs_cmd_data ; Store in TX buffer |
| 9442 | LDX #1 ; X=1: buffer offset |
| 9444 | LDY #7 ; Y=7: change directory command code |
| 9446 | JSR save_net_tx_cb ; Send directory change request |
| 9449 | LDX #1 ; X=1 |
| 944B | STX fs_cmd_data ; Store start marker in buffer |
| 944E | STX fs_func_code ; Store start marker in buffer+1 |
| 9451 | INX ; X=&02 |
| 9452 | LDY fs_load_addr ; Restore dir path start position |
| 9454 | JSR copy_arg_validated ; Copy directory path to buffer |
| 9457 | LDY #6 ; Y=6: set directory command code |
| 9459 | JSR save_net_tx_cb ; Send set directory command |
| 945C | LDY fs_cmd_data ; Load reply handle |
| 945F | JMP fsreply_3_set_csd ; Select FS and return |
| 9462 | .dir_pass_simple←1← 93E1 BNE |
| JMP check_urd_prefix ; 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. |
|
| 9465 | .init_txcb_bye←1← 94F1 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.
|
||||
| 9467 | .init_txcb_port←1← 9AE0 JSR | |||
| JSR init_txcb ; Initialise TXCB from template | ||||
| 946A | STA txcb_port ; Set transmit port | |||
| 946C | LDA #3 ; A=3: data start offset | |||
| 946E | STA txcb_start ; Set TXCB start offset | |||
| 9470 | DEC txcb_ctrl ; Decrement control byte | |||
| 9472 | RTS ; Return | |||
Initialise TX control block from ROM templateCopies 12 bytes from txcb_init_template (&948B) 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. |
|
| 9473 | .init_txcb←5← 8DFC JSR← 9467 JSR← 94E0 JSR← A8D3 LDA← B93A JSR |
| PHA ; Save A | |
| 9474 | LDY #&0b ; Y=&0B: template size - 1 |
| 9476 | .loop_init_txcb←1← 9487 BPL |
| LDA txcb_init_template,y ; Load byte from TXCB template | |
| 9479 | STA txcb_ctrl,y ; Store to TXCB workspace |
| 947C | CPY #2 ; Index >= 2? |
| 947E | BPL skip_txcb_dest ; Yes: skip dest station copy |
| 9480 | LDA fs_server_stn,y ; Load dest station byte |
| 9483 | STA txcb_dest,y ; Store to TXCB destination |
| 9486 | .skip_txcb_dest←1← 947E BPL |
| DEY ; Decrement index | |
| 9487 | BPL loop_init_txcb ; More bytes: continue |
| 9489 | PLA ; Restore A |
| 948A | 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, &9491) | |
| ; 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. | |
| 948B | .txcb_init_template←1← 9476 LDA |
| EQUB &80 ; Offset 0: txcb_ctrl = &80 (transmit) | |
| 948C | EQUB &99 ; Offset 1: txcb_port = &99 (FS reply) |
| 948D | EQUB &00 ; Offset 2: txcb_dest lo (overwritten) |
| 948E | EQUB &00 ; Offset 3: txcb_dest hi (overwritten) |
| 948F | EQUB &00 ; Offset 4: txcb_start = 0 |
| 9490 | EQUB &0F ; Offset 5: buffer start hi (page &0F) |
| 9491 | .bit_test_ff←22← 8C73 BIT← 964E BIT← 977D BIT← 9B41 BIT← 9D0F BIT← A09E BIT← A19D BIT← A316 BIT← A341 BIT← A378 BIT← AA80 BIT← AF7F BIT← AF85 BIT← B025 BIT← B1A5 BIT← B205 BIT← B246 BIT← B2CE BIT← B55A BIT← B598 BIT← B888 BIT← B99D BIT |
| EQUB &FF ; Offset 6: BIT target / buffer end lo | |
| 9492 | EQUB &FF ; Offset 7: txcb_pos = &FF |
| 9493 | EQUB &FF ; Offset 8: txcb_end = &FF |
| 9494 | EQUB &0F ; Offset 9: buffer end hi (page &0F) |
| 9495 | EQUB &FF ; Offset 10: extended addr fill (&FF) |
| 9496 | 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. |
|
| 9497 | .send_request_nowrite←1← 9F14 JSR |
| PHA ; Save A | |
| 9498 | SEC ; Set carry (read-only mode) |
| 9499 | 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. |
|
| 949B | .send_request_write←2← 9958 JSR← 9A0C JSR |
| CLV ; Clear V | |
| 949C | 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. |
|
| 949E | .cmd_bye |
| LDY #0 ; Y=0: close all files | |
| 94A0 | JSR process_all_fcbs ; Process all file control blocks |
| 94A3 | LDA #osbyte_close_spool_exec ; OSBYTE &77: close spool/exec |
| 94A5 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 94A8 | JSR close_all_net_chans ; Close all network channels |
| 94AB | 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. |
|
| 94AD | .save_net_tx_cb←25← 8E24 JSR← 9423 JSR← 9446 JSR← 9459 JSR← 9B5D JSR← 9C42 JSR← 9C52 JSR← 9CA0 JSR← 9D37 JSR← 9DB0 JSR← 9DE4 JSR← 9E7C JSR← 9E9F JSR← 9F6F JSR← A02A JSR← A1D6 JSR← A1FE JSR← A543 JSR← AD41 JMP← ADB8 JSR← ADF6 JSR← AE65 JSR← B381 JSR← B418 JSR← B917 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. |
|
| 94AE | .save_net_tx_cb_vset←3← 9B44 JSR← 9D15 JSR← AF82 JMP |
| LDA fs_urd_handle ; Copy FS station to TX control block | |
| 94B1 | STA fs_cmd_urd ; Store in TXCB |
| 94B4 | .txcb_copy_carry_clr←1← 949C BVC |
| CLC ; Clear carry | |
| 94B5 | .txcb_copy_carry_set←1← 9499 BCS |
| PHP ; Save flags (carry = mode) | |
| 94B6 | STY fs_cmd_y_param ; Store function code in TXCB |
| 94B9 | LDY #1 ; Copy 2 bytes (indices 0-1) |
| 94BB | .loop_copy_vset_stn←1← 94C2 BPL |
| LDA fs_csd_handle,y ; Load source byte | |
| 94BE | STA fs_cmd_csd,y ; Store to TXCB |
| 94C1 | DEY ; Next byte |
| 94C2 | BPL loop_copy_vset_stn ; Loop until all copied |
| 94C4 | BIT fs_lib_flags ; Test library flag bits 6-7 |
| 94C7 | BVS use_lib_station ; Bit 6 set: use station as port |
| 94C9 | BPL done_vset_station ; Bit 7 clear: skip port override |
| 94CB | LDA fs_lib_handle ; Bit 7 set: load alternative port |
| 94CE | STA fs_cmd_csd ; Override TXCB port byte |
| 94D1 | BVC done_vset_station ; ALWAYS branch |
| 94D3 | .use_lib_station←1← 94C7 BVS |
| LDA fs_urd_handle ; Bit 6: load station byte | |
| 94D6 | STA fs_cmd_csd ; Use station as TXCB port |
| 94D9 | .done_vset_station←2← 94C9 BPL← 94D1 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. |
|
| 94DA | .prep_send_tx_cb←1← 9FC8 JSR |
| PHP ; Save flags | |
| 94DB | LDA #&90 ; Port &90: FS command port |
| 94DD | STA txcb_reply_port ; Set reply port in TXCB |
| 94E0 | JSR init_txcb ; Initialise TXCB workspace |
| 94E3 | TXA ; Get TXCB data end offset |
| 94E4 | ADC #5 ; Add 5 for header size |
| 94E6 | STA txcb_end ; Set TXCB end pointer |
| 94E8 | PLP ; Restore flags |
| 94E9 | BCS handle_disconnect ; C set: send disconnect instead |
| 94EB | PHP ; Save flags |
| 94EC | JSR init_tx_ptr_and_send ; Initialise TX pointer and send |
| 94EF | 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. |
|
| 94F0 | .recv_and_process_reply←2← 9A1F JSR← 9F68 JSR |
| PHP ; Save flags | |
| 94F1 | JSR init_txcb_bye ; Set up receive TXCB |
| 94F4 | JSR wait_net_tx_ack ; Wait for TX acknowledgment |
| 94F7 | PLP ; Restore flags |
| 94F8 | .loop_next_reply←1← 950C BCC |
| INY ; Advance to next reply byte | |
| 94F9 | LDA (txcb_start),y ; Load reply byte |
| 94FB | TAX ; Save in X |
| 94FC | BEQ return_from_recv_reply ; Zero: no more replies, return |
| 94FE | BVC process_reply_code ; V clear: use code directly |
| 9500 | ADC #&2a ; V set: adjust reply code (+&2B) |
| 9502 | .process_reply_code←1← 94FE BVC |
| BNE store_reply_status ; Non-zero: process reply | |
| 9504 | .return_from_recv_reply←2← 94FC BEQ← 9574 BPL |
| RTS ; Return | |
| 9505 | .handle_disconnect←1← 94E9 BCS |
| PLA ; Discard saved flags | |
| 9506 | LDX #&c0 ; X=&C0: disconnect command |
| 9508 | INY ; Advance reply offset |
| 9509 | JSR send_disconnect_reply ; Send disconnect reply |
| 950C | BCC loop_next_reply ; Successful: process next reply |
| 950E | .store_reply_status←1← 9502 BNE |
| STX fs_last_error ; Store reply status code | |
| 9511 | LDA fs_eof_flags ; Load pending operation marker |
| 9514 | PHP ; Save pending operation flag (Z) |
| 9515 | BNE check_data_loss ; Pending: go to data loss check |
| 9517 | CPX #&bf ; Reply &BF (normal bye response)? |
| 9519 | BNE build_error_block ; No: build error from reply |
| 951B | .check_data_loss←1← 9515 BNE |
| LDA #&40 ; A=&40: initial data-loss flag | |
| 951D | PHA ; Push data-loss accumulator |
| 951E | LDX #&f0 ; Scan 16 channel entries (15 to 0) |
| 9520 | .loop_scan_channels←1← 952E BMI |
| PLA ; Pop accumulator | |
| 9521 | ORA l0fc8,x ; OR in channel status bits |
| 9524 | PHA ; Push updated accumulator |
| 9525 | LDA l0fc8,x ; Load channel status |
| 9528 | AND #&c0 ; Keep only bits 6-7 (close flags) |
| 952A | STA l0fc8,x ; Clear data bits, keep state flags |
| 952D | INX ; Advance to next channel slot |
| 952E | BMI loop_scan_channels ; Bit 7 set: more channels to scan |
| 9530 | STX fs_eof_flags ; Store last channel scanned |
| 9533 | JSR close_all_net_chans ; Close all network channels |
| 9536 | PLA ; Pop data-loss accumulator |
| 9537 | ROR ; Bit 0 to carry (data lost?) |
| 9538 | BCC reload_reply_status ; No data lost: skip message |
| 953A | JSR print_inline ; Print 'Data Lost' + CR |
| 953D | EQUS "Data Lost." |
| 9547 | .reload_reply_status←1← 9538 BCC |
| LDX fs_last_error ; Reload reply status code | |
| 954A | PLP ; Restore pending operation flag |
| 954B | BEQ build_error_block ; No pending operation: build error |
| 954D | PLA ; No pending: build error from reply Pending: clean up stack (3 bytes) |
| 954E | PLA ; (second byte) |
| 954F | PLA ; (third byte) |
| 9550 | RTS ; Return to pending operation caller |
| 9551 | .build_error_block←2← 9519 BNE← 954B BEQ |
| LDY #1 ; Y=1: error code offset in reply | |
| 9553 | CPX #&a8 ; Reply code >= &A8? |
| 9555 | BCS setup_error_copy ; Yes: keep server error code |
| 9557 | LDA #&a8 ; No: use minimum error code &A8 |
| 9559 | STA (txcb_start),y ; Overwrite error code in reply |
| 955B | .setup_error_copy←1← 9555 BCS |
| LDY #&ff ; Y=&FF: pre-increment index | |
| 955D | .loop_copy_error←1← 9565 BNE |
| INY ; Advance to next byte | |
| 955E | LDA (txcb_start),y ; Load reply byte |
| 9560 | STA error_block,y ; Copy to error block |
| 9563 | EOR #&0d ; Is it CR (end of message)? |
| 9565 | BNE loop_copy_error ; No: copy next byte |
| 9567 | STA error_block,y ; Store null terminator (A=0 from EOR) |
| 956A | DEY ; Get message length |
| 956B | TYA ; Transfer to A |
| 956C | TAX ; Length in X |
| 956D | 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. |
|
| 9570 | .check_escape←2← 8DD6 JSR← 985B JSR |
| LDA escape_flag ; Load MOS escape flag | |
| 9572 | AND need_release_tube ; Mask with escape-enabled flag |
| 9574 | BPL return_from_recv_reply ; No escape: return |
| 9576 | .raise_escape_error←1← B445 JMP |
| LDA #osbyte_acknowledge_escape ; OSBYTE &7E: acknowledge escape | |
| 9578 | JSR osbyte ; Clear escape condition and perform escape effects |
| 957B | LDA #6 ; Error class 6: Escape |
| 957D | JMP classify_reply_error ; Classify as network error |
| 9580 | .lang_1_remote_boot |
| LDY #0 ; Offset 0: remote state byte | |
| 9582 | LDA (net_rx_ptr),y ; Load remote state |
| 9584 | BEQ init_remote_session ; Zero: initialise remote session |
| 9586 | .done_commit_state←1← 95CC BNE |
| JMP commit_state_byte ; Non-zero: commit state and return | |
| 9589 | .init_remote_session←2← 9584 BEQ← 95C2 BEQ |
| ORA #9 ; Set bits 0,3: remote active flags | |
| 958B | STA (net_rx_ptr),y ; Store updated remote state |
| 958D | LDX #&80 ; X=&80: flag for vector setup |
| 958F | LDY #&80 ; Offset &80 in RX buffer |
| 9591 | LDA (net_rx_ptr),y ; Load remote station low |
| 9593 | PHA ; Save on stack |
| 9594 | INY ; Y=&81 |
| 9595 | LDA (net_rx_ptr),y ; Load remote station high |
| 9597 | LDY #&0f ; Workspace offset &0F |
| 9599 | STA (nfs_workspace),y ; Store remote station high |
| 959B | DEY ; Y=&0E Y=&0e |
| 959C | PLA ; Restore remote station low |
| 959D | STA (nfs_workspace),y ; Store remote station low |
| 959F | JSR scan_remote_keys ; Set up remote keyboard scanning |
| 95A2 | JSR init_ws_copy_narrow ; Initialise workspace copy |
| 95A5 | LDX #1 ; X=1: disable keyboard |
| 95A7 | LDY #0 ; Y=0 |
| 95A9 | LDA #osbyte_read_write_econet_keyboard_disable ; OSBYTE &C9: Econet keyboard disable |
| 95AB | JSR osbyte ; Disable keyboard (for Econet) |
| 95AE | .lang_3_execute_at_0100 |
| JSR commit_state_byte ; Commit state change | |
| 95B1 | LDA #0 ; Error code 0 |
| 95B3 | JSR error_inline_log ; Generate 'Remoted' error |
| 95B6 | EQUS "Remoted." |
| 95BE | .lang_4_remote_validated |
| LDY #0 ; Offset 0: remote state byte | |
| 95C0 | LDA (net_rx_ptr),y ; Load remote state |
| 95C2 | BEQ init_remote_session ; Zero: reinitialise session |
| 95C4 | LDY #&80 ; Offset &80: station low |
| 95C6 | LDA (net_rx_ptr),y ; Load station low from RX |
| 95C8 | LDY #&0e ; Workspace offset &0E |
| 95CA | CMP (nfs_workspace),y ; Compare with stored station |
| 95CC | BNE done_commit_state ; Different station: commit state |
| 95CE | .lang_0_insert_remote_key |
| LDY #&82 ; Offset &82: keypress byte | |
| 95D0 | LDA (net_rx_ptr),y ; Load remote keypress |
| 95D2 | TAY ; Key code to Y |
| 95D3 | LDX #0 ; X=0: keyboard buffer |
| 95D5 | JSR commit_state_byte ; Commit state change |
| 95D8 | LDA #osbyte_insert_input_buffer ; OSBYTE &99: insert into buffer |
| 95DA | 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. |
|
| 95DD | .wait_net_tx_ack←6← 94F4 JSR← 99B2 JSR← 9AEA JSR← A928 JMP← ABD1 JSR← AC73 JSR |
| LDA tx_retry_count ; Save TX timeout counter | |
| 95E0 | PHA ; Push (used as outer loop counter) |
| 95E1 | LDA econet_flags ; Save TX control state |
| 95E4 | PHA ; Push (preserved during wait) |
| 95E5 | LDA net_tx_ptr_hi ; Check if TX in progress |
| 95E7 | BNE init_poll_counters ; Non-zero: skip force-wait |
| 95E9 | ORA #&80 ; Set bit 7 to force wait mode |
| 95EB | STA econet_flags ; Store updated control state |
| 95EE | .init_poll_counters←1← 95E7 BNE |
| LDA #0 ; A=0: initial counter values | |
| 95F0 | PHA ; Push inner loop counter |
| 95F1 | PHA ; Push middle loop counter |
| 95F2 | TAY ; Y=&00 |
| 95F3 | TSX ; X=SP for stack-relative DECs |
| 95F4 | .loop_poll_tx←3← 95FB BNE← 9600 BNE← 9605 BNE |
| LDA (net_tx_ptr),y ; Poll TX completion status | |
| 95F6 | BMI done_poll_tx ; Bit 7 set: TX complete |
| 95F8 | DEC error_text,x ; Decrement inner counter |
| 95FB | BNE loop_poll_tx ; Not zero: keep polling |
| 95FD | DEC stack_page_2,x ; Decrement middle counter |
| 9600 | BNE loop_poll_tx ; Not zero: keep polling |
| 9602 | DEC stack_page_4,x ; Decrement outer counter |
| 9605 | BNE loop_poll_tx ; Not zero: keep polling |
| 9607 | .done_poll_tx←1← 95F6 BMI |
| PLA ; Discard inner counter | |
| 9608 | PLA ; Discard middle counter |
| 9609 | PLA ; Restore l0d61 control state |
| 960A | STA econet_flags ; Write back TX control state |
| 960D | PLA ; Pop outer counter (0 if timed out) |
| 960E | BEQ build_no_reply_error ; Zero: TX timed out |
| 9610 | 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.
|
||||
| 9611 | .cond_save_error_code←6← 9627 JSR← 9660 JSR← 967C JSR← 96A6 JSR← 96B8 JSR← 96D1 JSR | |||
| BIT fs_flags ; Test error logging flag | ||||
| 9614 | BPL return_from_cond_save_err ; Bit 7 clear: skip save | |||
| 9616 | STA fs_last_error ; Save error code to workspace | |||
| 9619 | .return_from_cond_save_err←1← 9614 BPL | |||
| RTS ; Return | ||||
| 961A | .build_no_reply_error←1← 960E BEQ | |||
| LDX #8 ; X=8: 'No reply' error index | ||||
| 961C | LDY net_error_lookup_data,x ; Look up message table offset | |||
| 961F | LDX #0 ; X=0: error text start | |||
| 9621 | STX error_block ; Clear BRK byte in error block | |||
| 9624 | LDA error_msg_table,y ; Load error number from table | |||
| 9627 | JSR cond_save_error_code ; Conditionally save error code | |||
| 962A | .loop_copy_no_reply_msg←1← 9634 BNE | |||
| LDA error_msg_table,y ; Load message byte | ||||
| 962D | STA error_text,x ; Store in error text buffer | |||
| 9630 | BEQ done_no_reply_msg ; Null terminator? | |||
| 9632 | INX ; Advance destination | |||
| 9633 | INY ; Advance source | |||
| 9634 | BNE loop_copy_no_reply_msg ; Loop until end of message | |||
| 9636 | .done_no_reply_msg←1← 9630 BEQ | |||
| JSR append_drv_dot_num ; Append ' net.station' to message | ||||
| 9639 | LDA #0 ; A=0: null terminator | |||
| 963B | STA error_text,x ; Terminate error text | |||
| 963E | JMP check_net_error_code ; Check and raise network error | |||
| 9641 | .fixup_reply_status_a←1← 98C6 JMP | |||
| LDA (net_tx_ptr,x) ; Load first reply byte | ||||
| 9643 | CMP #&41 ; Is it 'A' (status &41)? | |||
| 9645 | BNE skip_if_not_a ; No: keep original | |||
| 9647 | LDA #&42 ; Yes: change to 'B' (&42) | |||
| 9649 | .skip_if_not_a←1← 9645 BNE | |||
| CLV ; Clear V flag | ||||
| 964A | BVC mask_error_class ; ALWAYS branch | |||
| 964C | .load_reply_and_classify←1← 987F JMP | |||
| LDA (net_tx_ptr,x) ; Load first reply byte | ||||
| 964E | .classify_reply_error←2← 957D JMP← 9DDC JMP | |||
| BIT bit_test_ff ; Set V flag (via BIT &FF) | ||||
| 9651 | .mask_error_class←1← 964A BVC | |||
| AND #7 ; Mask to error class (0-7) | ||||
| 9653 | PHA ; Save error class on stack | |||
| 9654 | CMP #2 ; Class 2 (station error)? | |||
| 9656 | BNE build_simple_error ; No: build simple error message | |||
| 9658 | PHP ; Save flags (V state for suffix) | |||
| 9659 | TAX ; Error class to X | |||
| 965A | LDY net_error_lookup_data,x ; Look up message table offset | |||
| 965D | LDA error_msg_table,y ; Load error number from table | |||
| 9660 | JSR cond_save_error_code ; Conditionally save error code | |||
| 9663 | LDX #0 ; X=0: error text start | |||
| 9665 | STX error_block ; Clear BRK byte | |||
| 9668 | .loop_copy_station_msg←1← 9672 BNE | |||
| LDA error_msg_table,y ; Load message byte | ||||
| 966B | STA error_text,x ; Store in error text | |||
| 966E | BEQ done_station_msg ; Null terminator? | |||
| 9670 | INY ; Advance source | |||
| 9671 | INX ; Advance destination | |||
| 9672 | BNE loop_copy_station_msg ; Loop until end of message | |||
| 9674 | .done_station_msg←1← 966E BEQ | |||
| JSR append_drv_dot_num ; Append ' net.station' suffix | ||||
| 9677 | PLP ; Restore flags | |||
| 9678 | BVS suffix_not_listening ; V set: append 'not listening' | |||
| 967A | LDA #&a4 ; Error code &A4 | |||
| 967C | JSR cond_save_error_code ; Conditionally save error code | |||
| 967F | STA error_text ; Replace error number in block | |||
| 9682 | LDY #&0b ; Y=&0B: 'not present' suffix index | |||
| 9684 | BNE load_suffix_offset ; ALWAYS branch | |||
| 9686 | .suffix_not_listening←1← 9678 BVS | |||
| LDY #9 ; Y=9: 'not listening' suffix index | ||||
| 9688 | .load_suffix_offset←1← 9684 BNE | |||
| LDA net_error_lookup_data,y ; Look up suffix table offset | ||||
| 968B | TAY ; Offset to Y for indexing | |||
| 968C | .loop_copy_suffix←1← 9696 BNE | |||
| LDA error_msg_table,y ; Load suffix byte | ||||
| 968F | STA error_text,x ; Append to error text | |||
| 9692 | BEQ done_suffix ; Null terminator? | |||
| 9694 | INY ; Advance source | |||
| 9695 | INX ; Advance destination | |||
| 9696 | BNE loop_copy_suffix ; Loop until end of suffix | |||
| 9698 | .done_suffix←1← 9692 BEQ | |||
| BEQ check_msg_terminator ; ALWAYS branch to error dispatch | ||||
| 969A | .build_simple_error←1← 9656 BNE | |||
| TAX ; Error class to X | ||||
| 969B | LDY net_error_lookup_data,x ; Look up message table offset | |||
| 969E | LDX #0 ; X=0: error text start | |||
| 96A0 | STX error_block ; Clear BRK byte | |||
| 96A3 | LDA error_msg_table,y ; Load error number from table | |||
| 96A6 | JSR cond_save_error_code ; Conditionally save error code | |||
| 96A9 | .loop_copy_error_msg←1← 96B3 BNE | |||
| LDA error_msg_table,y ; Load message byte | ||||
| 96AC | STA error_text,x ; Store in error text | |||
| 96AF | .check_msg_terminator←1← 9698 BEQ | |||
| BEQ check_net_error_code ; Null terminator? Go to error | ||||
| 96B1 | INY ; Advance source | |||
| 96B2 | INX ; Advance destination | |||
| 96B3 | .bad_str_anchor | |||
| BNE loop_copy_error_msg ; Loop until end of message | ||||
| 96B5 | 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.
|
||||
| 96B8 | .error_bad_inline←11← 8FE6 JSR← 920A JSR← 9217 JSR← 922B JSR← 9237 JSR← 9246 JSR← 92FC JSR← 9381 JSR← 93A4 JSR← A25F JSR← BC14 JSR | |||
| JSR cond_save_error_code ; Conditionally log error code to workspace | ||||
| 96BB | TAY ; Save error number in Y | |||
| 96BC | PLA ; Pop return address (low) — points to last byte of JSR | |||
| 96BD | STA fs_load_addr ; Store return address low | |||
| 96BF | PLA ; Pop return address (high) | |||
| 96C0 | STA fs_load_addr_hi ; Store return address high | |||
| 96C2 | LDX #0 ; X=0: start of prefix string | |||
| 96C4 | .loop_copy_bad_prefix←1← 96CD BNE | |||
| INX ; Copy 'Bad ' prefix from lookup table | ||||
| 96C5 | LDA bad_prefix,x ; Get next prefix character | |||
| 96C8 | STA error_text,x ; Store in error text buffer | |||
| 96CB | CMP #&20 ; Is it space (end of 'Bad ')? | |||
| 96CD | BNE loop_copy_bad_prefix ; No: copy next prefix character | |||
| 96CF | 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.
|
||||
| 96D1 | .error_inline_log←11← 942F JSR← 95B3 JSR← A276 JSR← AC00 JSR← AC12 JSR← B485 JSR← B4FB JSR← B547 JSR← B7DF JSR← B819 JSR← B863 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.
|
||||
| 96D4 | .error_inline←4← A129 JSR← BA11 JSR← BBA7 JSR← BC5D JSR | |||
| TAY ; Save error number in Y | ||||
| 96D5 | PLA ; Pop return address (low) — points to last byte of JSR | |||
| 96D6 | STA fs_load_addr ; Store return address low | |||
| 96D8 | PLA ; Pop return address (high) | |||
| 96D9 | STA fs_load_addr_hi ; Store return address high | |||
| 96DB | LDX #0 ; X=0: error text index | |||
| 96DD | .write_error_num_and_str←1← 96CF BEQ | |||
| STY error_text ; Store error number in error block | ||||
| 96E0 | TYA ; Copy error number to A | |||
| 96E1 | PHA ; Push error number on stack | |||
| 96E2 | LDY #0 ; Y=0: inline string index | |||
| 96E4 | STY error_block ; Zero the BRK byte at &0100 | |||
| 96E7 | .loop_copy_inline_str←1← 96EE BNE | |||
| INX ; Copy inline string into error block | ||||
| 96E8 | INY ; Advance string index | |||
| 96E9 | LDA (fs_load_addr),y ; Read next byte from inline string | |||
| 96EB | STA error_text,x ; Store byte in error block | |||
| 96EE | BNE loop_copy_inline_str ; Loop until null terminator | |||
| 96F0 | .check_net_error_code←4← 956D JMP← 963E JMP← 96AF BEQ← B971 JMP | |||
| JSR read_rx_attribute ; Read receive attribute byte | ||||
| 96F3 | BNE handle_net_error ; Non-zero: network returned an error | |||
| 96F5 | PLA ; Pop saved error number | |||
| 96F6 | CMP #&de ; Was it &DE (file server error)? | |||
| 96F8 | BEQ append_error_number ; Yes: append error number and trigger BRK | |||
| 96FA | .trigger_brk←1← 974B BEQ | |||
| JMP error_block ; Jump to BRK via error block | ||||
| 96FD | .handle_net_error←1← 96F3 BNE | |||
| STA l0e08 ; Store error code in workspace | ||||
| 9700 | PHA ; Push error code | |||
| 9701 | TXA ; Save X (error text index) | |||
| 9702 | PHA ; Push X | |||
| 9703 | JSR read_rx_attribute ; Read receive attribute byte | |||
| 9706 | STA fs_load_addr ; Save to fs_load_addr as spool handle | |||
| 9708 | LDA #0 ; A=0: clear error code in RX buffer | |||
| 970A | STA (net_rx_ptr),y ; Zero the error code byte in buffer | |||
| 970C | LDA #&c6 ; A=&C6: OSBYTE read spool handle | |||
| 970E | JSR osbyte_x0 ; Read current spool file handle | |||
| 9711 | CPY fs_load_addr ; Compare Y result with saved handle | |||
| 9713 | BEQ close_exec_via_y ; Match: close the spool file | |||
| 9715 | CPX fs_load_addr ; Compare X result with saved handle | |||
| 9717 | BNE done_close_files ; No match: skip spool close | |||
| 9719 | PHA ; Push A (preserved) | |||
| 971A | LDA #&c6 ; A=&C6: disable spool with OSBYTE | |||
| 971C | BNE close_spool_exec ; ALWAYS branch to close spool ALWAYS branch | |||
| 971E | .close_exec_via_y←1← 9713 BEQ | |||
| TYA ; Transfer Y to A for stack save | ||||
| 971F | .close_exec_file | |||
| PHA ; Push A (preserved) | ||||
| 9720 | LDA #&c7 ; A=&C7: disable exec with OSBYTE | |||
| 9722 | .close_spool_exec←1← 971C BNE | |||
| JSR osbyte_x0_y0 ; OSBYTE with X=0, Y=0 to close | ||||
| 9725 | PLA ; Pull saved handle | |||
| 9726 | TAY ; Transfer to Y for OSFIND | |||
| 9727 | LDA #osfind_close ; A=0: close file | |||
| 9729 | JSR osfind ; Close the spool/exec file Close one or all files | |||
| 972C | .done_close_files←1← 9717 BNE | |||
| PLA ; Pull saved X (error text index) | ||||
| 972D | TAX ; Restore X | |||
| 972E | LDY #&0a ; Y=&0A: lookup index for 'on channel' | |||
| 9730 | LDA net_error_lookup_data,y ; Load message offset from lookup table | |||
| 9733 | TAY ; Transfer offset to Y | |||
| 9734 | .loop_copy_channel_msg←1← 973E BNE | |||
| LDA error_msg_table,y ; Load error message byte | ||||
| 9737 | STA error_text,x ; Append to error text buffer | |||
| 973A | BEQ append_error_number ; Null terminator: done copying | |||
| 973C | INX ; Advance error text index | |||
| 973D | INY ; Advance message index | |||
| 973E | BNE loop_copy_channel_msg ; Loop until full message copied | |||
| 9740 | .append_error_number←2← 96F8 BEQ← 973A BEQ | |||
| STX fs_load_addr_2 ; Save error text end position | ||||
| 9742 | PLA ; Pull saved error number | |||
| 9743 | JSR append_space_and_num ; Append ' nnn' error number suffix | |||
| 9746 | LDA #0 ; A=0: null terminator | |||
| 9748 | STA stack_page_2,x ; Terminate error text string | |||
| 974B | 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.
|
|||||||
| 974D | .append_drv_dot_num←2← 9636 JSR← 9674 JSR | ||||||
| LDA #&20 ; A=' ': space separator | |||||||
| 974F | STA error_text,x ; Append space to error text | ||||||
| 9752 | INX ; Advance error text index | ||||||
| 9753 | STX fs_load_addr_2 ; Save position for number formatting | ||||||
| 9755 | LDY #3 ; Y=3: offset to network number in TX CB | ||||||
| 9757 | LDA (net_tx_ptr),y ; Load network number | ||||||
| 9759 | BEQ append_station_num ; Zero: skip network part (local) | ||||||
| 975B | JSR append_decimal_num ; Append network number as decimal | ||||||
| 975E | LDX fs_load_addr_2 ; Reload error text position | ||||||
| 9760 | LDA #&2e ; A='.': dot separator | ||||||
| 9762 | STA error_text,x ; Append dot to error text | ||||||
| 9765 | INC fs_load_addr_2 ; Advance past dot | ||||||
| 9767 | .append_station_num←1← 9759 BEQ | ||||||
| LDY #2 ; Y=2: offset to station number in TX CB | |||||||
| 9769 | LDA (net_tx_ptr),y ; Load station number | ||||||
| 976B | JSR append_decimal_num ; Append station number as decimal | ||||||
| 976E | LDX fs_load_addr_2 ; Reload error text position | ||||||
| 9770 | 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.
|
||||
| 9771 | .append_space_and_num←2← 9743 JSR← B4DA JSR | |||
| TAY ; Save number in Y | ||||
| 9772 | LDA #&20 ; A=' ': space prefix | |||
| 9774 | LDX fs_load_addr_2 ; Load current error text position | |||
| 9776 | STA error_text,x ; Append space to error text | |||
| 9779 | INC fs_load_addr_2 ; Advance position past space | |||
| 977B | 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.
|
||||
| 977C | .append_decimal_num←2← 975B JSR← 976B JSR | |||
| TAY ; Save number in Y for division | ||||
| 977D | BIT bit_test_ff ; Set V: suppress leading zeros | |||
| 9780 | LDA #&64 ; A=100: hundreds digit divisor | |||
| 9782 | JSR append_decimal_digit ; Extract and append hundreds digit | |||
| 9785 | LDA #&0a ; A=10: tens digit divisor | |||
| 9787 | JSR append_decimal_digit ; Extract and append tens digit | |||
| 978A | LDA #1 ; A=1: units digit (remainder) | |||
| 978C | 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.
|
|||||||||||||
| 978D | .append_decimal_digit←2← 9782 JSR← 9787 JSR | ||||||||||||
| STA fs_load_addr_3 ; Store divisor | |||||||||||||
| 978F | TYA ; Copy number to A for division | ||||||||||||
| 9790 | LDX #&2f ; X='0'-1: digit counter (ASCII offset) | ||||||||||||
| 9792 | PHP ; Save V flag (leading zero suppression) | ||||||||||||
| 9793 | SEC ; Set carry for subtraction | ||||||||||||
| 9794 | .loop_count_digit←1← 9797 BCS | ||||||||||||
| INX ; Increment digit counter | |||||||||||||
| 9795 | SBC fs_load_addr_3 ; Subtract divisor | ||||||||||||
| 9797 | BCS loop_count_digit ; Not negative yet: continue counting | ||||||||||||
| 9799 | ADC fs_load_addr_3 ; Add back divisor (restore remainder) | ||||||||||||
| 979B | PLP ; Restore V flag | ||||||||||||
| 979C | TAY ; Save remainder back to Y | ||||||||||||
| 979D | TXA ; Digit counter to A (ASCII digit) | ||||||||||||
| 979E | CMP #&30 ; Is digit '0'? | ||||||||||||
| 97A0 | BNE store_digit ; Non-zero: always print | ||||||||||||
| 97A2 | BVS return_from_store_digit ; V set (suppress leading zeros): skip | ||||||||||||
| 97A4 | .store_digit←1← 97A0 BNE | ||||||||||||
| CLV ; Clear V: first non-zero digit seen | |||||||||||||
| 97A5 | LDX fs_load_addr_2 ; Load current text position | ||||||||||||
| 97A7 | STA error_text,x ; Store ASCII digit in error text | ||||||||||||
| 97AA | INC fs_load_addr_2 ; Advance text position | ||||||||||||
| 97AC | .return_from_store_digit←1← 97A2 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. | |||||||||||||
| 97AD | .net_error_lookup_data←5← 961C LDY← 965A LDY← 9688 LDA← 969B LDY← 9730 LDA | ||||||||||||
| EQUB error_msg_table - error_msg_table ; Class 0: &A0 "Line jammed" | |||||||||||||
| 97AE | EQUB msg_net_error - error_msg_table ; Class 1: &A1 "Net error" | ||||||||||||
| 97AF | EQUB msg_station - error_msg_table ; Class 2: &A2 "Station" | ||||||||||||
| 97B0 | EQUB msg_no_clock - error_msg_table ; Class 3: &A3 "No clock" | ||||||||||||
| 97B1 | EQUB msg_escape - error_msg_table ; Class 4: &11 "Escape" | ||||||||||||
| 97B2 | EQUB msg_escape - error_msg_table ; Class 5: &11 "Escape" (duplicate) | ||||||||||||
| 97B3 | EQUB msg_escape - error_msg_table ; Class 6: &11 "Escape" (duplicate) | ||||||||||||
| 97B4 | EQUB msg_bad_option - error_msg_table ; Class 7: &CB "Bad option" | ||||||||||||
| 97B5 | EQUB msg_no_reply - error_msg_table ; Index 8: &A5 "No reply from station" | ||||||||||||
| 97B6 | EQUB msg_not_listening - error_msg_table ; Index 9: " not listening" suffix | ||||||||||||
| 97B7 | EQUB msg_on_channel - error_msg_table ; Index 10: " on channel" suffix | ||||||||||||
| 97B8 | 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". | |||||||||||||
| 97B9 | .error_msg_table←8← 9624 LDA← 962A LDA← 965D LDA← 9668 LDA← 968C LDA← 96A3 LDA← 96A9 LDA← 9734 LDA | ||||||||||||
| EQUB &A0 ; Error &A0: Line jammed | |||||||||||||
| 97BA | EQUS "Line jammed" | ||||||||||||
| 97C5 | EQUB &00 ; Null terminator | ||||||||||||
| 97C6 | .msg_net_error | ||||||||||||
| EQUB &A1 ; Error &A1: Net error | |||||||||||||
| 97C7 | EQUS "Net error" | ||||||||||||
| 97D0 | EQUB &00 ; Null terminator | ||||||||||||
| 97D1 | .msg_station | ||||||||||||
| EQUB &A2 ; Error &A2: Station | |||||||||||||
| 97D2 | EQUS "Station" | ||||||||||||
| 97D9 | EQUB &00 ; Null terminator | ||||||||||||
| 97DA | .msg_no_clock | ||||||||||||
| EQUB &A3 ; Error &A3: No clock | |||||||||||||
| 97DB | EQUB &4E | ||||||||||||
| 97DC | EQUS "o clock" | ||||||||||||
| 97E3 | EQUB &00 ; Null terminator | ||||||||||||
| 97E4 | .msg_escape | ||||||||||||
| EQUB &11 ; Error &11: Escape | |||||||||||||
| 97E5 | EQUS "Escape" | ||||||||||||
| 97EB | EQUB &00 ; Null terminator | ||||||||||||
| 97EC | .msg_bad_option | ||||||||||||
| EQUB &CB ; Error &CB: Bad option | |||||||||||||
| 97ED | EQUS "Bad option" | ||||||||||||
| 97F7 | EQUB &00 ; Null terminator | ||||||||||||
| 97F8 | .msg_no_reply | ||||||||||||
| EQUB &A5 ; Error &A5: No reply from station | |||||||||||||
| 97F9 | EQUB &4E | ||||||||||||
| 97FA | EQUS "o reply from station" | ||||||||||||
| 980E | EQUB &00 ; Null terminator | ||||||||||||
| 980F | .msg_not_listening | ||||||||||||
| EQUS " not listening" ; Suffix: " not listening" | |||||||||||||
| 981D | EQUB &00 ; Null terminator | ||||||||||||
| 981E | .msg_on_channel | ||||||||||||
| EQUS " on channel" ; Suffix: " on channel" | |||||||||||||
| 9829 | EQUB &00 ; Null terminator | ||||||||||||
| 982A | .msg_not_present | ||||||||||||
| EQUS " not present" ; Suffix: " not present" | |||||||||||||
| 9836 | 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. |
|
| 9837 | .init_tx_ptr_and_send←2← 94EC JSR← 9ADB JSR |
| LDX #&c0 ; X=&C0: TX control block base (low) | |
| 9839 | STX net_tx_ptr ; Set TX pointer low |
| 983B | LDX #0 ; X=0: TX control block base (high) |
| 983D | 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. |
|
| 983F | .send_net_packet←6← A977 JMP← A9D4 JSR← ABC6 JSR← AC51 JSR← B05B JSR← B23A JSR |
| LDA net_context ; Load retry count from workspace | |
| 9842 | BNE set_timeout ; Non-zero: use configured retry count |
| 9844 | LDA #&ff ; A=&FF: default retry count (255) |
| 9846 | .set_timeout←1← 9842 BNE |
| LDY #&60 ; Y=&60: timeout value | |
| 9848 | PHA ; Push retry count |
| 9849 | TYA ; A=&60: copy timeout to A A=&60 |
| 984A | PHA ; Push timeout |
| 984B | LDX #0 ; X=0: TX pointer index |
| 984D | LDA (net_tx_ptr,x) ; Load first byte of TX control block |
| 984F | .start_tx_attempt←1← 9871 BEQ |
| STA (net_tx_ptr,x) ; Write control byte back to CB | |
| 9851 | PHA ; Push control byte |
| 9852 | JSR poll_adlc_tx_status ; Poll ADLC until line idle |
| 9855 | ASL ; Shift left: check bit 6 (success) |
| 9856 | BPL tx_success ; Bit 6 clear: transmission complete |
| 9858 | ASL ; Shift left: check bit 5 (fatal) |
| 9859 | BEQ tx_send_error ; Zero (bit 5 clear): fatal error |
| 985B | JSR check_escape ; Check for escape condition |
| 985E | PLA ; Pull control byte |
| 985F | TAX ; Restore to X |
| 9860 | PLA ; Pull timeout |
| 9861 | TAY ; Restore to Y |
| 9862 | PLA ; Pull retry count |
| 9863 | BEQ try_alternate_phase ; Zero retries remaining: try alternate |
| 9865 | .loop_retry_tx←1← 987C BNE |
| SBC #1 ; Decrement retry counter | |
| 9867 | PHA ; Push updated retry count |
| 9868 | TYA ; Copy timeout to A |
| 9869 | PHA ; Push timeout for delay loop |
| 986A | TXA ; Copy control byte to A |
| 986B | .loop_tx_delay←2← 986C BNE← 986F BNE |
| DEX ; Inner delay: decrement X | |
| 986C | BNE loop_tx_delay ; Loop until X=0 |
| 986E | DEY ; Decrement outer counter Y |
| 986F | BNE loop_tx_delay ; Loop until Y=0 |
| 9871 | BEQ start_tx_attempt ; ALWAYS branch: retry transmission ALWAYS branch |
| 9873 | .try_alternate_phase←1← 9863 BEQ |
| CMP net_context ; Compare retry count with alternate | |
| 9876 | BNE tx_send_error ; Different: go to error handling |
| 9878 | LDA #&80 ; A=&80: set escapable flag |
| 987A | STA need_release_tube ; Mark as escapable for second phase |
| 987C | BNE loop_retry_tx ; ALWAYS branch: retry with escapable ALWAYS branch |
| 987E | .tx_send_error←2← 9859 BEQ← 9876 BNE |
| TAX ; Result code to X | |
| 987F | JMP load_reply_and_classify ; Jump to classify reply and return |
| 9882 | .tx_success←1← 9856 BPL |
| PLA ; Pull control byte | |
| 9883 | PLA ; Pull timeout |
| 9884 | PLA ; Pull retry count |
| 9885 | 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. | |
| 9888 | .pass_txbuf_init_table←2← 989E LDX← 98F8 LDX |
| EQUB &88 ; Offset 0: ctrl = &88 (immediate TX) | |
| 9889 | EQUB &00 ; Offset 1: port = &00 (immediate op) |
| 988A | EQUB &FD ; Offset 2: &FD skip (preserve dest stn) |
| 988B | EQUB &FD ; Offset 3: &FD skip (preserve dest net) |
| 988C | EQUB &3A ; Offset 4: buf start lo (&3A) |
| 988D | EQUB &0D ; Offset 5: buf start hi (&0D) -> &0D3A |
| 988E | EQUB &FF ; Offset 6: extended addr fill (&FF) |
| 988F | EQUB &FF ; Offset 7: extended addr fill (&FF) |
| 9890 | EQUB &3E ; Offset 8: buf end lo (&3E) |
| 9891 | EQUB &0D ; Offset 9: buf end hi (&0D) -> &0D3E |
| 9892 | EQUB &FF ; Offset 10: extended addr fill (&FF) |
| 9893 | 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. |
|
| 9894 | .init_tx_ptr_for_pass←1← 8DFF JSR |
| LDY #&c0 ; Y=&C0: TX control block base (low) | |
| 9896 | STY net_tx_ptr ; Set TX pointer low byte |
| 9898 | LDY #0 ; Y=0: TX control block base (high) |
| 989A | 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. |
|
| 989C | .setup_pass_txbuf←1← ABC3 JSR |
| LDY #&0b ; Y=&0B: 12 bytes to process (0-11) | |
| 989E | .loop_copy_template←1← 98AC BPL |
| LDX pass_txbuf_init_table,y ; Load template byte for this offset | |
| 98A1 | CPX #&fd ; Is it &FD (skip marker)? |
| 98A3 | BEQ skip_template_byte ; Yes: skip this offset, don't modify |
| 98A5 | LDA (net_tx_ptr),y ; Load existing TX buffer byte |
| 98A7 | PHA ; Save original value on stack |
| 98A8 | TXA ; Copy template value to A |
| 98A9 | STA (net_tx_ptr),y ; Store template value to TX buffer |
| 98AB | .skip_template_byte←1← 98A3 BEQ |
| DEY ; Next offset (descending) | |
| 98AC | BPL loop_copy_template ; Loop until all 12 bytes processed |
| 98AE | LDA rx_poll_count ; Load pass-through control value |
| 98B1 | PHA ; Push control value |
| 98B2 | TYA ; A=&FF (Y is &FF after loop) |
| 98B3 | PHA ; Push &FF as timeout |
| 98B4 | LDX #0 ; X=0: TX pointer index |
| 98B6 | LDA (net_tx_ptr,x) ; Load control byte from TX CB |
| 98B8 | .start_pass_tx←1← 98F1 BEQ |
| STA (net_tx_ptr,x) ; Write control byte to start TX | |
| 98BA | PHA ; Save control byte on stack |
| 98BB | JSR poll_adlc_tx_status ; Poll ADLC until line idle |
| 98BE | ASL ; Shift result: check bit 6 (success) |
| 98BF | BPL pass_tx_success ; Bit 6 clear: transmission complete |
| 98C1 | ASL ; Shift result: check bit 5 (fatal) |
| 98C2 | BNE restore_retry_state ; Non-zero (not fatal): retry |
| 98C4 | .done_pass_retries←1← 98E3 BEQ |
| LDX #0 ; X=0: clear error status | |
| 98C6 | 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. |
|
| 98C9 | .poll_adlc_tx_status←3← 9852 JSR← 98BB JSR← 98CC BCC |
| ASL ws_0d60 ; Shift ws_0d60 left to poll ADLC | |
| 98CC | BCC poll_adlc_tx_status ; Bit not set: keep polling |
| 98CE | LDA net_tx_ptr ; Copy TX pointer low to NMI TX block |
| 98D0 | STA nmi_tx_block ; Store in NMI TX block low |
| 98D2 | LDA net_tx_ptr_hi ; Copy TX pointer high |
| 98D4 | STA nmi_tx_block_hi ; Store in NMI TX block high |
| 98D6 | JSR tx_begin ; Begin Econet frame transmission |
| 98D9 | .loop_poll_pass_tx←1← 98DB BMI |
| LDA (net_tx_ptr,x) ; Read TX status byte | |
| 98DB | BMI loop_poll_pass_tx ; Bit 7 set: still transmitting |
| 98DD | RTS ; Return with result in A |
| 98DE | .restore_retry_state←1← 98C2 BNE |
| PLA ; Pull control byte | |
| 98DF | TAX ; Restore to X |
| 98E0 | PLA ; Pull timeout |
| 98E1 | TAY ; Restore to Y |
| 98E2 | PLA ; Pull retry count |
| 98E3 | BEQ done_pass_retries ; Zero retries: go to error handling |
| 98E5 | SBC #1 ; Decrement retry counter |
| 98E7 | PHA ; Push updated retry count |
| 98E8 | TYA ; Copy timeout to A |
| 98E9 | PHA ; Push timeout |
| 98EA | TXA ; Copy control byte to A |
| 98EB | .loop_pass_tx_delay←2← 98EC BNE← 98EF BNE |
| DEX ; Inner delay loop: decrement X | |
| 98EC | BNE loop_pass_tx_delay ; Loop until X=0 |
| 98EE | DEY ; Decrement outer counter Y |
| 98EF | BNE loop_pass_tx_delay ; Loop until Y=0 |
| 98F1 | BEQ start_pass_tx ; ALWAYS branch: retry transmission ALWAYS branch |
| 98F3 | .pass_tx_success←1← 98BF BPL |
| PLA ; Pull control byte (discard) | |
| 98F4 | PLA ; Pull timeout (discard) |
| 98F5 | PLA ; Pull retry count (discard) |
| 98F6 | LDY #0 ; Y=0: start restoring from offset 0 |
| 98F8 | .loop_restore_txbuf←1← 9905 BNE |
| LDX pass_txbuf_init_table,y ; Load template byte for this offset | |
| 98FB | CPX #&fd ; Is it &FD (skip marker)? |
| 98FD | BEQ skip_restore_byte ; Yes: don't restore this offset |
| 98FF | PLA ; Pull original value from stack |
| 9900 | STA (net_tx_ptr),y ; Restore original TX buffer byte |
| 9902 | .skip_restore_byte←1← 98FD BEQ |
| INY ; Next offset (ascending) | |
| 9903 | CPY #&0c ; Processed all 12 bytes? |
| 9905 | BNE loop_restore_txbuf ; No: continue restoring |
| 9907 | RTS ; Return with TX buffer restored |
| 9908 | .load_text_ptr_and_parse←1← 9938 JSR |
| LDY #1 ; Y=1: start at second byte of pointer | |
| 990A | .loop_copy_text_ptr←1← 9910 BPL |
| LDA (fs_options),y ; Load pointer byte from FS options | |
| 990C | STA os_text_ptr,y ; Store in OS text pointer |
| 990F | DEY ; Decrement index |
| 9910 | BPL loop_copy_text_ptr ; Loop until both bytes copied |
| 9912 | INY ; Y=0: reset index for string reading |
| 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. |
|
| 9913 | .gsread_to_buf←1← AE94 JSR |
| LDX #&ff ; X=&FF: pre-increment for buffer index | |
| 9915 | CLC ; C=0: initialise for string input |
| 9916 | JSR gsinit ; GSINIT: initialise string reading |
| 9919 | BEQ terminate_buf ; Z set (empty string): store terminator |
| 991B | .loop_gsread_char←1← 9924 BCC |
| JSR gsread ; GSREAD: read next character | |
| 991E | BCS terminate_buf ; C set: end of string reached |
| 9920 | INX ; Advance buffer index |
| 9921 | STA fs_filename_buf,x ; Store character in l0e30 buffer |
| 9924 | BCC loop_gsread_char ; ALWAYS branch: read next character ALWAYS branch |
| 9926 | .terminate_buf←2← 9919 BEQ← 991E BCS |
| INX ; Advance past last character | |
| 9927 | LDA #&0d ; A=CR: terminate filename |
| 9929 | STA fs_filename_buf,x ; Store CR terminator in buffer |
| 992C | LDA #&30 ; A=&30: low byte of l0e30 buffer |
| 992E | STA fs_crc_lo ; Set command text pointer low |
| 9930 | LDA #&0e ; A=&0E: high byte of l0e30 buffer |
| 9932 | STA fs_crc_hi ; Set command text pointer high |
| 9934 | RTS ; Return with buffer filled |
| 9935 | JSR set_xfer_params ; Set up transfer parameters |
| 9938 | JSR load_text_ptr_and_parse ; Load text pointer and parse filename |
| 993B | JSR mask_owner_access ; Set owner-only access mask |
| 993E | JSR parse_access_prefix ; Parse access prefix from filename |
| 9941 | LDA fs_last_byte_flag ; Load last byte flag |
| 9943 | BPL check_display_type ; Positive (not last): display file info |
| 9945 | CMP #&ff ; Is it &FF (last entry)? |
| 9947 | BEQ copy_arg_and_enum ; Yes: copy arg and iterate |
| 9949 | JMP return_with_last_flag ; Other value: return with flag |
| 994C | .copy_arg_and_enum←1← 9947 BEQ |
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer at X=0 | |
| 994F | 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. |
|
| 9951 | .do_fs_cmd_iteration←1← A2D1 JSR |
| LDA #&92 ; A=&92: FS port number | |
| 9953 | STA need_release_tube ; Set escapable flag to &92 |
| 9955 | STA fs_cmd_urd ; Store port number in TX buffer |
| 9958 | JSR send_request_write ; Send request to file server |
| 995B | LDY #6 ; Y=6: offset to response cycle flag |
| 995D | LDA (fs_options),y ; Load cycle flag from FS options |
| 995F | BNE copy_ws_then_fsopts ; Non-zero: already initialised |
| 9961 | JSR copy_fsopts_to_zp ; Copy FS options to zero page first |
| 9964 | JSR copy_workspace_to_fsopts ; Then copy workspace to FS options |
| 9967 | BCC setup_txcb_addrs ; Branch to continue (C clear from JSR) |
| 9969 | .copy_ws_then_fsopts←1← 995F BNE |
| JSR copy_workspace_to_fsopts ; Copy workspace to FS options first | |
| 996C | JSR copy_fsopts_to_zp ; Then copy FS options to zero page |
| 996F | .setup_txcb_addrs←1← 9967 BCC |
| LDY #4 ; Y=4: loop counter | |
| 9971 | .loop_copy_addrs←1← 997C BNE |
| LDA fs_load_addr,x ; Load address byte from zero page | |
| 9973 | STA txcb_end,x ; Save to TXCB end pointer |
| 9975 | ADC fs_file_len,x ; Add offset from buffer |
| 9978 | STA fs_work_4,x ; Store sum in fs_work area |
| 997A | INX ; Advance to next byte |
| 997B | DEY ; Decrement counter |
| 997C | BNE loop_copy_addrs ; Loop for all 4 bytes |
| 997E | SEC ; Set carry for subtraction |
| 997F | SBC fs_file_len_3 ; Subtract high offset |
| 9982 | STA fs_work_7 ; Store result in fs_work_7 |
| 9984 | JSR format_filename_field ; Format filename for display |
| 9987 | JSR send_txcb_swap_addrs ; Send TXCB and swap addresses |
| 998A | LDX #2 ; X=2: copy 3 offset bytes |
| 998C | .loop_copy_offsets←1← 9993 BPL |
| LDA fs_file_len_3,x ; Load offset byte from l0f10 | |
| 998F | STA fs_cmd_data,x ; Store in l0f05 for next iteration |
| 9992 | DEX ; Decrement counter |
| 9993 | BPL loop_copy_offsets ; Loop until all bytes copied |
| 9995 | 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. |
|
| 9998 | .send_txcb_swap_addrs←2← 9987 JSR← 9F52 JSR |
| JSR cmp_5byte_handle ; Compare 5-byte handle with current | |
| 999B | BEQ return_from_txcb_swap ; Match: no need to send, return |
| 999D | LDA #&92 ; A=&92: FS reply port number |
| 999F | STA txcb_port ; Set TXCB port |
| 99A1 | .loop_swap_and_send←1← 99BD BNE |
| LDX #3 ; X=3: copy 4 bytes | |
| 99A3 | .loop_copy_start_end←1← 99AC BPL |
| LDA txcb_end,x ; Load TXCB end pointer byte | |
| 99A5 | STA txcb_start,x ; Store in TXCB start pointer |
| 99A7 | LDA fs_work_4,x ; Load new end address from fs_work |
| 99A9 | STA txcb_end,x ; Store in TXCB end pointer |
| 99AB | DEX ; Decrement counter |
| 99AC | BPL loop_copy_start_end ; Loop for all 4 bytes |
| 99AE | LDA #&7f ; A=&7F: control byte for data transfer |
| 99B0 | STA txcb_ctrl ; Set TXCB control byte |
| 99B2 | JSR wait_net_tx_ack ; Wait for network TX acknowledgement |
| 99B5 | LDY #3 ; Y=3: compare 4 bytes |
| 99B7 | .loop_verify_addrs←1← 99C0 BPL |
| LDA txcb_end,y ; Load TXCB end byte | |
| 99BA | EOR fs_work_4,y ; Compare with expected end address |
| 99BD | BNE loop_swap_and_send ; Mismatch: resend from start |
| 99BF | DEY ; Decrement counter |
| 99C0 | BPL loop_verify_addrs ; Loop until all 4 bytes match |
| 99C2 | .return_from_txcb_swap←1← 999B BEQ |
| RTS ; Return (all bytes match) | |
| 99C3 | .check_display_type←1← 9943 BPL |
| BEQ setup_dir_display ; Z set: directory entry display | |
| 99C5 | JMP dispatch_osword_op ; Non-zero: jump to OSWORD dispatch |
| 99C8 | .setup_dir_display←2← 99C3 BEQ← 9AF8 JMP |
| LDX #4 ; X=4: loop counter for 4 iterations | |
| 99CA | LDY #&0e ; Y=&0E: FS options offset for addresses |
| 99CC | SEC ; Set carry for subtraction |
| 99CD | .loop_compute_diffs←1← 99E7 BNE |
| LDA (fs_options),y ; Load address byte from FS options | |
| 99CF | STA port_ws_offset,y ; Save to workspace (port_ws_offset) |
| 99D2 | JSR retreat_y_by_4 ; Y -= 4 to point to paired offset |
| 99D5 | SBC (fs_options),y ; Subtract paired value |
| 99D7 | STA fs_cmd_csd,y ; Store difference in l0f03 buffer |
| 99DA | PHA ; Push difference |
| 99DB | LDA (fs_options),y ; Load paired value from FS options |
| 99DD | STA port_ws_offset,y ; Save to workspace |
| 99E0 | PLA ; Pull difference back |
| 99E1 | STA (fs_options),y ; Store in FS options for display |
| 99E3 | JSR skip_one_and_advance5 ; Advance Y by 5 for next field |
| 99E6 | DEX ; Decrement loop counter |
| 99E7 | BNE loop_compute_diffs ; Loop for all 4 address pairs |
| 99E9 | LDY #9 ; Y=9: copy 9 bytes of options data |
| 99EB | .loop_copy_fs_options←1← 99F1 BNE |
| LDA (fs_options),y ; Load FS options byte | |
| 99ED | STA fs_cmd_csd,y ; Store in l0f03 buffer |
| 99F0 | DEY ; Decrement index |
| 99F1 | BNE loop_copy_fs_options ; Loop until all 9 bytes copied |
| 99F3 | LDA #&91 ; A=&91: FS port for info request |
| 99F5 | STA need_release_tube ; Set escapable flag |
| 99F7 | STA fs_cmd_urd ; Store port in TX buffer |
| 99FA | STA fs_error_ptr ; Store in fs_error_ptr |
| 99FC | LDX #&0b ; X=&0B: copy argument at offset 11 |
| 99FE | JSR copy_arg_to_buf ; Copy argument to TX buffer |
| 9A01 | LDY #1 ; Y=1: info sub-command |
| 9A03 | LDA fs_last_byte_flag ; Load last byte flag |
| 9A05 | CMP #7 ; Is it 7 (catalogue info)? |
| 9A07 | PHP ; Save comparison result |
| 9A08 | BNE send_info_request ; Not 7: keep Y=1 |
| 9A0A | LDY #&1d ; Y=&1D: extended info command |
| 9A0C | .send_info_request←1← 9A08 BNE |
| JSR send_request_write ; Send request to file server | |
| 9A0F | JSR format_filename_field ; Format filename for display |
| 9A12 | PLP ; Restore comparison flags |
| 9A13 | BNE setup_txcb_transfer ; Not catalogue info: show short format |
| 9A15 | LDX #0 ; X=0: start at first byte |
| 9A17 | BEQ store_result ; ALWAYS branch to store and display ALWAYS branch |
| 9A19 | .setup_txcb_transfer←1← 9A13 BNE |
| LDA fs_cmd_data ; Load file handle from l0f05 | |
| 9A1C | JSR check_and_setup_txcb ; Check and set up TXCB for transfer |
| 9A1F | .recv_reply←1← 9995 JMP |
| JSR recv_and_process_reply ; Receive and process reply | |
| 9A22 | .store_result←1← 9A17 BEQ |
| STX fs_reply_cmd ; Store result byte in l0f08 | |
| 9A25 | LDY #&0e ; Y=&0E: protection bits offset |
| 9A27 | LDA fs_cmd_data ; Load access byte from l0f05 |
| 9A2A | JSR get_prot_bits ; Extract protection bit flags |
| 9A2D | BEQ store_prot_byte ; Zero: use reply buffer data |
| 9A2F | .loop_copy_file_info←1← 9A37 BNE |
| LDA fs_reply_data,y ; Load file info byte from l0ef7 | |
| 9A32 | .store_prot_byte←1← 9A2D BEQ |
| STA (fs_options),y ; Store in FS options at offset Y | |
| 9A34 | INY ; Advance to next byte |
| 9A35 | CPY #&12 ; Y=&12: end of protection fields? |
| 9A37 | BNE loop_copy_file_info ; No: copy next byte |
| 9A39 | LDY fs_messages_flag ; Load display flag from l0e06 |
| 9A3C | BEQ return_from_advance_y ; Zero: skip display, return |
| 9A3E | LDY #&f4 ; Y=&F4: index into l0fff for filename |
| 9A40 | .loop_print_filename←1← 9A47 BNE |
| LDA l0fff,y ; Load filename character from l10f3 | |
| 9A43 | JSR osasci ; Print character via OSASCI Write character |
| 9A46 | INY ; Advance to next character |
| 9A47 | BNE loop_print_filename ; Printed all 12 characters? No: print next character |
| 9A49 | LDY #5 ; Y=5: offset for access string |
| 9A4B | JSR print_5_hex_bytes ; Print 5 hex bytes (access info) |
| 9A4E | JSR print_load_exec_addrs ; Print load and exec addresses |
| 9A51 | JSR osnewl ; Print newline Write newline (characters 10 and 13) |
| 9A54 | 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. |
|
| 9A57 | .print_load_exec_addrs←1← 9A4E JSR |
| LDY #9 ; Y=9: offset for exec address | |
| 9A59 | JSR print_5_hex_bytes ; Print 5 hex bytes (exec address) |
| 9A5C | LDY #&0c ; Y=&0C: offset for length (3 bytes) |
| 9A5E | LDX #3 ; X=3: print 3 bytes only |
| 9A60 | 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).
|
||||||
| 9A62 | .print_5_hex_bytes←2← 9A4B JSR← 9A59 JSR | |||||
| LDX #4 ; X=4: print 5 bytes (4 to 0) | ||||||
| 9A64 | .loop_print_hex_byte←2← 9A60 BNE← 9A6B BNE | |||||
| LDA (fs_options),y ; Load byte from FS options at offset Y | ||||||
| 9A66 | JSR print_hex_byte ; Print as 2-digit hex | |||||
| 9A69 | DEY ; Decrement byte offset | |||||
| 9A6A | DEX ; Decrement byte count | |||||
| 9A6B | BNE loop_print_hex_byte ; Loop until all bytes printed | |||||
| 9A6D | LDA #&20 ; A=' ': space separator | |||||
| 9A6F | 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. |
|
| 9A72 | .copy_fsopts_to_zp←2← 9961 JSR← 996C JSR |
| LDY #5 ; Y=5: copy 4 bytes (offsets 2-5) | |
| 9A74 | .loop_copy_fsopts_byte←1← 9A7C BCS |
| LDA (fs_options),y ; Load byte from FS options | |
| 9A76 | STA work_ae,y ; Store in zero page at l00ae+Y |
| 9A79 | DEY ; Decrement index |
| 9A7A | CPY #2 ; Below offset 2? |
| 9A7C | 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. |
|
| 9A7E | .skip_one_and_advance5←1← 99E3 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.
|
|||||||
| 9A7F | .advance_y_by_4←2← 9F2F JSR← B074 JSR | ||||||
| INY ; Y += 4 | |||||||
| 9A80 | INY ; (continued) | ||||||
| 9A81 | INY ; (continued) | ||||||
| 9A82 | INY ; (continued) | ||||||
| 9A83 | .return_from_advance_y←1← 9A3C 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. |
|
| 9A84 | .copy_workspace_to_fsopts←2← 9964 JSR← 9969 JSR |
| LDY #&0d ; Y=&0D: copy bytes from offset &0D down | |
| 9A86 | TXA ; Transfer X to A |
| 9A87 | .loop_copy_ws_byte←1← 9A8F BCS |
| STA (fs_options),y ; Store byte in FS options at offset Y | |
| 9A89 | LDA fs_cmd_urd,y ; Load next workspace byte from l0f02+Y |
| 9A8C | DEY ; Decrement index |
| 9A8D | CPY #2 ; Below offset 2? |
| 9A8F | 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.
|
|||||||
| 9A91 | .retreat_y_by_4←1← 99D2 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.
|
|||||||
| 9A92 | .retreat_y_by_3←2← 9B0E JSR← 9F37 JSR | ||||||
| DEY ; Y -= 3 | |||||||
| 9A93 | DEY ; (continued) | ||||||
| 9A94 | DEY ; (continued) | ||||||
| 9A95 | RTS ; Return | ||||||
| 9A96 | .discard_handle_match←2← 9A9E BEQ← 9AE4 BCS | ||||||
| PLA ; Discard stacked value | |||||||
| 9A97 | LDY fs_block_offset ; Restore Y from fs_block_offset | ||||||
| 9A99 | 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. |
|
| 9A9A | .check_and_setup_txcb←2← 9A1C JSR← 9F4D JSR |
| PHA ; Save port/sub-function on stack | |
| 9A9B | JSR cmp_5byte_handle ; Compare 5-byte handle with current |
| 9A9E | BEQ discard_handle_match ; Match: discard port and return |
| 9AA0 | .init_transfer_addrs←1← 9AED BNE |
| LDX #0 ; X=0: loop start | |
| 9AA2 | LDY #4 ; Y=4: copy 4 bytes |
| 9AA4 | STX fs_reply_cmd ; Clear l0f08 (transfer size low) |
| 9AA7 | STX fs_load_vector ; Clear l0f09 (transfer size high) |
| 9AAA | CLC ; Clear carry for addition |
| 9AAB | .loop_copy_addr_offset←1← 9AB8 BNE |
| LDA fs_load_addr,x ; Load address byte from zero page | |
| 9AAD | STA txcb_start,x ; Store in TXCB start pointer |
| 9AAF | ADC fs_func_code,x ; Add offset from l0f06 |
| 9AB2 | STA txcb_end,x ; Store sum in TXCB end pointer |
| 9AB4 | STA fs_load_addr,x ; Also update load address |
| 9AB6 | INX ; Advance to next byte |
| 9AB7 | DEY ; Decrement counter |
| 9AB8 | BNE loop_copy_addr_offset ; Loop for all 4 bytes |
| 9ABA | BCS clamp_end_to_limit ; Carry set: overflow, use limit |
| 9ABC | SEC ; Set carry for subtraction |
| 9ABD | .loop_check_vs_limit←1← 9AC5 BNE |
| LDA fs_load_addr,y ; Load computed end address | |
| 9AC0 | SBC fs_work_4,y ; Subtract maximum from fs_work_4 |
| 9AC3 | INY ; Advance to next byte |
| 9AC4 | DEX ; Decrement counter |
| 9AC5 | BNE loop_check_vs_limit ; Loop for all bytes |
| 9AC7 | BCC set_port_and_ctrl ; Below limit: keep computed end |
| 9AC9 | .clamp_end_to_limit←1← 9ABA BCS |
| LDX #3 ; X=3: copy 4 bytes of limit | |
| 9ACB | .loop_copy_limit←1← 9AD0 BPL |
| LDA fs_work_4,x ; Load limit from fs_work_4 | |
| 9ACD | STA txcb_end,x ; Store as TXCB end |
| 9ACF | DEX ; Decrement counter |
| 9AD0 | BPL loop_copy_limit ; Loop for all 4 bytes |
| 9AD2 | .set_port_and_ctrl←1← 9AC7 BCC |
| PLA ; Pull port from stack | |
| 9AD3 | PHA ; Push back (keep for later) |
| 9AD4 | PHP ; Save flags (carry = overflow state) |
| 9AD5 | STA txcb_port ; Set TXCB port number |
| 9AD7 | LDA #&80 ; A=&80: control byte for data request |
| 9AD9 | STA txcb_ctrl ; Set TXCB control byte |
| 9ADB | JSR init_tx_ptr_and_send ; Init TX pointer and send packet |
| 9ADE | LDA fs_error_ptr ; Load error pointer |
| 9AE0 | JSR init_txcb_port ; Init TXCB port from error pointer |
| 9AE3 | PLP ; Restore overflow flags |
| 9AE4 | BCS discard_handle_match ; Carry set: discard and return |
| 9AE6 | LDA #&91 ; A=&91: FS reply port |
| 9AE8 | STA txcb_port ; Set TXCB port for reply |
| 9AEA | JSR wait_net_tx_ack ; Wait for TX acknowledgement |
| 9AED | BNE init_transfer_addrs ; Non-zero (not done): retry send |
| 9AEF | .dispatch_osword_op←1← 99C5 JMP |
| STA fs_cmd_data ; Store sub-operation code | |
| 9AF2 | CMP #7 ; Compare with 7 |
| 9AF4 | BCC dispatch_ops_1_to_6 ; Below 7: handle operations 1-6 |
| 9AF6 | BNE skip_if_error ; Above 7: jump to handle via finalise |
| 9AF8 | JMP setup_dir_display ; Equal to 7: jump to directory display |
| 9AFB | .dispatch_ops_1_to_6←1← 9AF4 BCC |
| CMP #6 ; Compare with 6 | |
| 9AFD | BEQ send_delete_request ; 6: delete file operation |
| 9AFF | CMP #5 ; Compare with 5 |
| 9B01 | BEQ read_cat_info ; 5: read catalogue info |
| 9B03 | CMP #4 ; Compare with 4 |
| 9B05 | BEQ setup_write_access ; 4: write file attributes |
| 9B07 | CMP #1 ; Compare with 1 |
| 9B09 | BEQ setup_save_access ; 1: read file info |
| 9B0B | ASL ; Shift left twice: A*4 |
| 9B0C | ASL ; A*4 |
| 9B0D | TAY ; Copy to Y as index |
| 9B0E | JSR retreat_y_by_3 ; Y -= 3 to get FS options offset |
| 9B11 | LDX #3 ; X=3: copy 4 bytes |
| 9B13 | .loop_copy_fsopts_4←1← 9B1A BPL |
| LDA (fs_options),y ; Load byte from FS options at offset Y | |
| 9B15 | STA fs_func_code,x ; Store in l0f06 buffer |
| 9B18 | DEY ; Decrement source offset |
| 9B19 | DEX ; Decrement byte count |
| 9B1A | BPL loop_copy_fsopts_4 ; Loop for all 4 bytes |
| 9B1C | LDX #5 ; X=5: copy arg to buffer at offset 5 |
| 9B1E | BNE send_save_or_access ; ALWAYS branch to copy and send ALWAYS branch |
| 9B20 | .setup_save_access←1← 9B09 BEQ |
| JSR get_access_bits ; Get access bits for file | |
| 9B23 | STA fs_file_attrs ; Store access byte in l0f0e |
| 9B26 | LDY #9 ; Y=9: source offset in FS options |
| 9B28 | LDX #8 ; X=8: copy 8 bytes to buffer |
| 9B2A | .loop_copy_fsopts_8←1← 9B31 BNE |
| LDA (fs_options),y ; Load FS options byte | |
| 9B2C | STA fs_cmd_data,x ; Store in l0f05 buffer |
| 9B2F | DEY ; Decrement source offset |
| 9B30 | DEX ; Decrement byte count |
| 9B31 | BNE loop_copy_fsopts_8 ; Loop for all 8 bytes |
| 9B33 | LDX #&0a ; X=&0A: buffer offset for argument |
| 9B35 | .send_save_or_access←2← 9B1E BNE← 9B54 BNE |
| JSR copy_arg_to_buf ; Copy argument to buffer | |
| 9B38 | LDY #&13 ; Y=&13: OSWORD &13 (NFS operation) |
| 9B3A | BNE send_request_vset ; ALWAYS branch to send request ALWAYS branch |
| 9B3C | .send_delete_request←1← 9AFD BEQ |
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer at X=0 | |
| 9B3F | LDY #&14 ; Y=&14: delete file command |
| 9B41 | .send_request_vset←1← 9B3A BNE |
| BIT bit_test_ff ; Set V flag (no directory check) | |
| 9B44 | JSR save_net_tx_cb_vset ; Send request with V set |
| 9B47 | .skip_if_error←1← 9AF6 BNE |
| BCS done_osword_op ; Carry set: error, jump to finalise | |
| 9B49 | JMP return_with_last_flag ; No error: return with last flag |
| 9B4C | .setup_write_access←1← 9B05 BEQ |
| JSR get_access_bits ; Get access bits for file | |
| 9B4F | STA fs_func_code ; Store in l0f06 |
| 9B52 | LDX #2 ; X=2: buffer offset |
| 9B54 | BNE send_save_or_access ; ALWAYS branch to copy and send ALWAYS branch |
| 9B56 | .read_cat_info←1← 9B01 BEQ |
| LDX #1 ; X=1: buffer offset | |
| 9B58 | JSR copy_arg_to_buf ; Copy argument to buffer |
| 9B5B | LDY #&12 ; Y=&12: open file command |
| 9B5D | JSR save_net_tx_cb ; Send open file request |
| 9B60 | LDA fs_obj_type ; Load reply handle from l0f11 |
| 9B63 | STX fs_obj_type ; Clear l0f11 |
| 9B66 | STX fs_len_clear ; Clear l0f14 |
| 9B69 | JSR get_prot_bits ; Get protection bits |
| 9B6C | LDX fs_cmd_data ; Load file handle from l0f05 |
| 9B6F | BEQ return_with_handle ; Zero: file not found, return |
| 9B71 | LDY #&0e ; Y=&0E: store access bits |
| 9B73 | STA (fs_options),y ; Store access byte in FS options |
| 9B75 | DEY ; Y=&0D Y=&0d |
| 9B76 | LDX #&0c ; X=&0C: copy 12 bytes of file info |
| 9B78 | .loop_copy_cat_info←1← 9B7F BNE |
| LDA fs_cmd_data,x ; Load reply byte from l0f05+X | |
| 9B7B | STA (fs_options),y ; Store in FS options at offset Y |
| 9B7D | DEY ; Decrement destination offset |
| 9B7E | DEX ; Decrement source counter |
| 9B7F | BNE loop_copy_cat_info ; Loop for all 12 bytes |
| 9B81 | INX ; X=1 (INX from 0) |
| 9B82 | INX ; X=2 |
| 9B83 | LDY #&11 ; Y=&11: FS options offset |
| 9B85 | .loop_copy_ext_info←1← 9B8C BPL |
| LDA fs_access_level,x ; Load extended info byte from l0f12 | |
| 9B88 | STA (fs_options),y ; Store in FS options |
| 9B8A | DEY ; Decrement destination offset |
| 9B8B | DEX ; Decrement source counter |
| 9B8C | BPL loop_copy_ext_info ; Loop until all copied |
| 9B8E | LDX fs_cmd_data ; Reload file handle |
| 9B91 | .return_with_handle←1← 9B6F BEQ |
| TXA ; Transfer to A | |
| 9B92 | .done_osword_op←1← 9B47 BCS |
| JMP finalise_and_return ; Jump to 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. |
|
| ; Unreachable dead code (3 bytes) | |
| ; Duplicate of the JMP at &9B92 immediately above. | |
| ; Unreachable after the unconditional JMP and | |
| ; unreferenced. Likely a development remnant. | |
| 9B95 | .format_filename_field←2← 9984 JSR← 9A0F JSR |
| LDY #0 ; Dead: duplicate JMP finalise_and_return Y=0: destination index | |
| 9B97 | LDX fs_cmd_csd ; Load source offset from l0f03 |
| 9B9A | BNE copy_from_buf_entry ; Non-zero: copy from l0f05 buffer |
| 9B9C | .loop_copy_cmdline_char←1← 9BA6 BNE |
| LDA (fs_crc_lo),y ; Load character from command line | |
| 9B9E | CMP #&21 ; Below '!' (control/space)? |
| 9BA0 | BCC pad_with_spaces ; Yes: pad with spaces |
| 9BA2 | STA filename_buf,y ; Store printable character in l10f3 |
| 9BA5 | INY ; Advance to next character |
| 9BA6 | BNE loop_copy_cmdline_char ; Loop for more characters |
| 9BA8 | .pad_with_spaces←2← 9BA0 BCC← 9BB0 BCC |
| LDA #&20 ; A=' ': space for padding | |
| 9BAA | STA filename_buf,y ; Store space in display buffer |
| 9BAD | INY ; Advance index |
| 9BAE | CPY #&0c ; Filled all 12 characters? |
| 9BB0 | BCC pad_with_spaces ; No: pad more spaces |
| 9BB2 | RTS ; Return with field formatted |
| 9BB3 | .loop_copy_buf_char←1← 9BBB BPL |
| INX ; Advance source and destination | |
| 9BB4 | INY ; INY |
| 9BB5 | .copy_from_buf_entry←1← 9B9A BNE |
| LDA fs_cmd_data,x ; Load byte from l0f05 buffer | |
| 9BB8 | STA filename_buf,y ; Store in display buffer l10f3 |
| 9BBB | BPL loop_copy_buf_char ; Bit 7 clear: more characters |
| 9BBD | RTS ; Return (bit 7 set = terminator) |
| 9BBE | JSR verify_ws_checksum ; Verify workspace checksum |
| 9BC1 | STA fs_last_byte_flag ; Store result as last byte flag |
| 9BC3 | JSR set_options_ptr ; Set FS options pointer |
| 9BC6 | ORA #0 ; OR with 0 to set flags |
| 9BC8 | BPL dispatch_osfind_op ; Positive: handle sub-operations |
| 9BCA | ASL ; Shift left to check bit 6 |
| 9BCB | BEQ validate_chan_close ; Zero (was &80): close channel |
| 9BCD | JMP close_all_fcbs ; Other: process all FCBs first |
| 9BD0 | .validate_chan_close←1← 9BCB BEQ |
| TYA ; Transfer Y to A | |
| 9BD1 | CMP #&20 ; Compare with &20 (space) |
| 9BD3 | BCS check_chan_range ; Above &20: check further |
| 9BD5 | .error_invalid_chan←1← 9BDA BCS |
| JMP err_net_chan_invalid ; Below &20: invalid channel char | |
| 9BD8 | .check_chan_range←1← 9BD3 BCS |
| CMP #&30 ; Compare with '0' | |
| 9BDA | BCS error_invalid_chan ; Above '0': invalid channel char |
| 9BDC | JSR process_all_fcbs ; Process all matching FCBs |
| 9BDF | TYA ; Transfer Y to A (FCB index) |
| 9BE0 | PHA ; Push FCB index |
| 9BE1 | TAX ; Copy to X |
| 9BE2 | LDY #0 ; Y=0: clear counter |
| 9BE4 | STY fs_last_byte_flag ; Clear last byte flag |
| 9BE6 | STY fs_block_offset ; Clear block offset |
| 9BE8 | .loop_copy_fcb_fields←1← 9BF3 BNE |
| LDA fcb_attr_or_count_mid,x ; Load channel data from l1010+X | |
| 9BEB | STA (fs_options),y ; Store in FS options at Y |
| 9BED | JSR advance_x_by_8 ; Advance X by 8 (next FCB field) |
| 9BF0 | INY ; Advance destination index |
| 9BF1 | CPY #4 ; Copied all 4 channel fields? |
| 9BF3 | BNE loop_copy_fcb_fields ; No: copy next field |
| 9BF5 | PLA ; Pull saved FCB index |
| 9BF6 | STA fs_block_offset ; Restore to fs_block_offset |
| 9BF8 | .dispatch_osfind_op←1← 9BC8 BPL |
| CMP #5 ; Compare with 5 | |
| 9BFA | BCS done_return_flag ; 5 or above: return with last flag |
| 9BFC | CPY #0 ; Compare Y with 0 |
| 9BFE | BNE osfind_with_channel ; Non-zero: handle OSFIND with channel |
| 9C00 | JMP osfind_close_or_open ; Y=0 (close): jump to OSFIND open |
| 9C03 | .osfind_with_channel←1← 9BFE BNE |
| PHA ; Push sub-function | |
| 9C04 | TXA ; Transfer X to A |
| 9C05 | PHA ; Push X (FCB slot) |
| 9C06 | TYA ; Transfer Y to A |
| 9C07 | PHA ; Push Y (channel char) |
| 9C08 | JSR check_not_dir ; Check file is not a directory |
| 9C0B | PLA ; Pull channel char |
| 9C0C | JSR store_rx_attribute ; Store channel char as receive attribute |
| 9C0F | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 |
| 9C12 | STA fs_cmd_data ; Store in l0f05 |
| 9C15 | PLA ; Pull X (FCB slot) |
| 9C16 | TAX ; Restore X |
| 9C17 | PLA ; Pull sub-function |
| 9C18 | LSR ; Shift right: check bit 0 |
| 9C19 | BEQ osargs_ptr_dispatch ; Zero (OSFIND close): handle close |
| 9C1B | PHP ; Save flags (carry from LSR) |
| 9C1C | PHA ; Push sub-function |
| 9C1D | LDX fs_options ; Load FS options pointer low |
| 9C1F | LDY fs_block_offset ; Load block offset |
| 9C21 | JSR process_all_fcbs ; Process all matching FCBs |
| 9C24 | LDA fcb_attr_or_count_mid,y ; Load updated data from l1010 |
| 9C27 | STA fs_cmd_data ; Store in l0f05 |
| 9C2A | PLA ; Pull sub-function |
| 9C2B | STA fs_func_code ; Store in l0f06 |
| 9C2E | PLP ; Restore flags |
| 9C2F | TYA ; Transfer Y to A |
| 9C30 | PHA ; Push Y (offset) |
| 9C31 | BCC osargs_read_op ; Carry clear: read operation |
| 9C33 | LDY #3 ; Y=3: copy 4 bytes |
| 9C35 | .loop_copy_zp_to_buf←1← 9C3C BPL |
| LDA zp_work_3,x ; Load zero page data | |
| 9C37 | STA fs_data_count,y ; Store in l0f07 buffer |
| 9C3A | DEX ; Decrement source |
| 9C3B | DEY ; Decrement counter |
| 9C3C | BPL loop_copy_zp_to_buf ; Loop for all 4 bytes |
| 9C3E | LDY #&0d ; Y=&0D: TX buffer size |
| 9C40 | LDX #5 ; X=5: argument offset |
| 9C42 | JSR save_net_tx_cb ; Send TX control block to server |
| 9C45 | STX fs_last_byte_flag ; Store X in last byte flag |
| 9C47 | PLA ; Pull saved offset |
| 9C48 | JSR set_conn_active ; Set connection active flag |
| 9C4B | .done_return_flag←1← 9BFA BCS |
| JMP return_with_last_flag ; Return with last flag | |
| 9C4E | .osargs_read_op←1← 9C31 BCC |
| LDY #&0c ; Y=&0C: TX buffer size (smaller) | |
| 9C50 | LDX #2 ; X=2: argument offset |
| 9C52 | JSR save_net_tx_cb ; Send TX control block |
| 9C55 | STA fs_last_byte_flag ; Store A in last byte flag |
| 9C57 | LDX fs_options ; Load FS options pointer low |
| 9C59 | LDY #2 ; Y=2: zero page offset |
| 9C5B | STA zp_work_3,x ; Store A in zero page |
| 9C5D | .loop_copy_reply_to_zp←1← 9C64 BPL |
| LDA fs_cmd_data,y ; Load buffer byte from l0f05+Y | |
| 9C60 | STA zp_work_2,x ; Store in zero page at offset |
| 9C62 | DEX ; Decrement source X |
| 9C63 | DEY ; Decrement counter Y |
| 9C64 | BPL loop_copy_reply_to_zp ; Loop until all bytes copied |
| 9C66 | PLA ; Pull saved offset |
| 9C67 | JMP return_with_last_flag ; Return with last flag |
| 9C6A | .osargs_ptr_dispatch←1← 9C19 BEQ |
| BCS osargs_write_ptr ; Carry set: write file pointer | |
| 9C6C | LDA fs_block_offset ; Load block offset |
| 9C6E | JSR attr_to_chan_index ; Convert attribute to channel index |
| 9C71 | LDY fs_options ; Load FS options pointer |
| 9C73 | LDA fcb_count_lo,x ; Load FCB low byte from l1000 |
| 9C76 | STA zp_ptr_lo,y ; Store in zero page pointer low |
| 9C79 | LDA fcb_attr_or_count_mid,x ; Load FCB high byte from l1010 |
| 9C7C | STA zp_ptr_hi,y ; Store in zero page pointer high |
| 9C7F | LDA fcb_station_or_count_hi,x ; Load FCB extent from l1020 |
| 9C82 | STA zp_work_2,y ; Store in zero page work area |
| 9C85 | LDA #0 ; A=0: clear high byte |
| 9C87 | STA zp_work_3,y ; Store zero in work area high |
| 9C8A | BEQ return_with_last_flag ; ALWAYS branch to return with flag ALWAYS branch |
| 9C8C | .osargs_write_ptr←1← 9C6A BCS |
| STA fs_func_code ; Store write value in l0f06 | |
| 9C8F | TXA ; Transfer X to A |
| 9C90 | PHA ; Push X (zero page offset) |
| 9C91 | LDY #3 ; Y=3: copy 4 bytes |
| 9C93 | .loop_copy_ptr_to_buf←1← 9C9A BPL |
| LDA zp_work_3,x ; Load zero page data at offset | |
| 9C95 | STA fs_data_count,y ; Store in l0f07 buffer |
| 9C98 | DEX ; Decrement source |
| 9C99 | DEY ; Decrement counter |
| 9C9A | BPL loop_copy_ptr_to_buf ; Loop for all 4 bytes |
| 9C9C | LDY #&0d ; Y=&0D: TX buffer size |
| 9C9E | LDX #5 ; X=5: argument offset |
| 9CA0 | JSR save_net_tx_cb ; Send TX control block |
| 9CA3 | STX fs_last_byte_flag ; Store X in last byte flag |
| 9CA5 | PLA ; Pull saved zero page offset |
| 9CA6 | TAY ; Transfer to Y |
| 9CA7 | LDA fs_block_offset ; Load block offset (attribute) |
| 9CA9 | JSR clear_conn_active ; Clear connection active flag |
| 9CAC | JSR attr_to_chan_index ; Convert attribute to channel index |
| 9CAF | LDA zp_ptr_lo,y ; Load zero page pointer low |
| 9CB2 | STA fcb_count_lo,x ; Store back to FCB l1000 |
| 9CB5 | LDA zp_ptr_hi,y ; Load zero page pointer high |
| 9CB8 | STA fcb_attr_or_count_mid,x ; Store back to FCB l1010 |
| 9CBB | LDA zp_work_2,y ; Load zero page work byte |
| 9CBE | STA fcb_station_or_count_hi,x ; Store back to FCB l1020 |
| 9CC1 | JMP return_with_last_flag ; Return with last flag |
| 9CC4 | .close_all_fcbs←1← 9BCD JMP |
| JSR process_all_fcbs ; Process all matching FCBs first | |
| 9CC7 | .return_with_last_flag←12← 9949 JMP← 9A54 JMP← 9B49 JMP← 9C4B JMP← 9C67 JMP← 9C8A BEQ← 9CC1 JMP← 9DBB JMP← 9E42 JMP← A2F7 JMP← A2FD JMP← A3B1 JMP |
| LDA fs_last_byte_flag ; Load last byte flag | |
| 9CC9 | .finalise_and_return←6← 9B92 JMP← 9CDE BNE← 9CF1 BPL← 9D87 JMP← 9ECF JMP← A206 JMP |
| PHA ; Push result on stack | |
| 9CCA | LDA #0 ; A=0: clear error flag |
| 9CCC | JSR store_rx_attribute ; Clear receive attribute (A=0) |
| 9CCF | PLA ; Pull result back |
| 9CD0 | LDX fs_options ; Restore X from FS options pointer |
| 9CD2 | LDY fs_block_offset ; Restore Y from block offset |
| 9CD4 | RTS ; Return to caller |
| 9CD5 | .osfind_close_or_open←1← 9C00 JMP |
| CMP #2 ; Compare with 2 (open for output) | |
| 9CD7 | BCS done_file_open ; 2 or above: handle file open |
| 9CD9 | TAY ; Transfer to Y (Y=0 or 1) |
| 9CDA | BNE loop_copy_reply_data ; Non-zero (1 = read pointer): copy data |
| 9CDC | LDA #5 ; A=5: return code for close-all |
| 9CDE | BNE finalise_and_return ; ALWAYS branch to finalise ALWAYS branch |
| 9CE0 | .loop_copy_reply_data←2← 9CDA BNE← 9CE6 BPL |
| LDA fs_cmd_context,y ; Load reply data byte at Y | |
| 9CE3 | STA (fs_options),y ; Store in FS options |
| 9CE5 | DEY ; Decrement index |
| 9CE6 | BPL loop_copy_reply_data ; Loop until all bytes copied |
| 9CE8 | STY zp_work_2,x ; Clear zero page work low |
| 9CEA | STY zp_work_3,x ; Clear zero page work high |
| 9CEC | .done_file_open←1← 9CD7 BCS |
| BEQ shift_and_finalise ; Z set: jump to clear A and return | |
| 9CEE | .clear_result←2← 9CF5 BNE← A00C JMP |
| LDA #0 ; A=0: clear result | |
| 9CF0 | .shift_and_finalise←1← 9CEC BEQ |
| LSR ; Shift right (always positive) | |
| 9CF1 | BPL finalise_and_return ; Positive: jump to finalise |
| 9CF3 | .alloc_fcb_for_open←1← 9D61 BEQ |
| AND #&3f ; Mask to 6-bit access value | |
| 9CF5 | BNE clear_result ; Non-zero: clear A and finalise |
| 9CF7 | TXA ; Transfer X to A (options pointer) |
| 9CF8 | JSR alloc_fcb_or_error ; Allocate FCB slot or raise error |
| 9CFB | EOR #&80 ; Toggle bit 7 |
| 9CFD | ASL ; Shift left: build open mode |
| 9CFE | STA fs_cmd_data ; Store open mode in l0f05 |
| 9D01 | ROL ; Rotate to complete mode byte |
| 9D02 | STA fs_func_code ; Store in l0f06 |
| 9D05 | JSR parse_cmd_arg_y0 ; Parse command argument (Y=0) |
| 9D08 | LDX #2 ; X=2: buffer offset |
| 9D0A | JSR copy_arg_to_buf ; Copy argument to TX buffer |
| 9D0D | LDY #6 ; Y=6: open file command |
| 9D0F | BIT bit_test_ff ; Set V flag (skip directory check) |
| 9D12 | SEC ; Set carry |
| 9D13 | ROR need_release_tube ; Rotate carry into escapable flag bit 7 |
| 9D15 | JSR save_net_tx_cb_vset ; Send open request with V set |
| 9D18 | BCS done_osfind ; Carry set (error): jump to finalise |
| 9D1A | LDA #&ff ; A=&FF: mark as newly opened |
| 9D1C | JSR store_rx_attribute ; Store &FF as receive attribute |
| 9D1F | LDA fs_cmd_data ; Load handle from l0f05 |
| 9D22 | PHA ; Push handle |
| 9D23 | LDA #4 ; A=4: file info sub-command |
| 9D25 | STA fs_cmd_data ; Store sub-command |
| 9D28 | LDX #1 ; X=1: shift filename |
| 9D2A | .loop_shift_filename←1← 9D33 BNE |
| LDA fs_func_code,x ; Load filename byte from l0f06+X | |
| 9D2D | STA fs_cmd_data,x ; Shift down to l0f05+X |
| 9D30 | INX ; Advance source index |
| 9D31 | CMP #&0d ; Is it CR (end of filename)? |
| 9D33 | BNE loop_shift_filename ; No: continue shifting |
| 9D35 | LDY #&12 ; Y=&12: file info request |
| 9D37 | JSR save_net_tx_cb ; Send file info request |
| 9D3A | LDA fs_last_byte_flag ; Load last byte flag |
| 9D3C | AND #&bf ; Clear bit 6 (read/write bits) |
| 9D3E | ORA fs_cmd_data ; OR with reply access byte |
| 9D41 | ORA #1 ; Set bit 0 (file is open) |
| 9D43 | TAY ; Transfer to Y (access flags) |
| 9D44 | AND #2 ; Check bit 1 (write access) |
| 9D46 | BEQ check_open_mode ; No write access: check read-only |
| 9D48 | PLA ; Pull handle from stack |
| 9D49 | JSR alloc_fcb_slot ; Allocate FCB slot for channel |
| 9D4C | BNE store_fcb_flags ; Non-zero: FCB allocated, store flags |
| 9D4E | JSR verify_ws_checksum ; Verify workspace checksum |
| 9D51 | JSR set_xfer_params ; Set up transfer parameters |
| 9D54 | TAX ; Transfer A to X |
| 9D55 | JSR mask_owner_access ; Set owner-only access mask |
| 9D58 | TXA ; Transfer X back to A |
| 9D59 | BEQ close_all_channels ; Zero: close file, process FCBs |
| 9D5B | JSR save_ptr_to_os_text ; Save text pointer for OS |
| 9D5E | LDY cur_dir_handle ; Load current directory handle |
| 9D61 | BEQ alloc_fcb_for_open ; Zero: allocate new FCB |
| 9D63 | TYA ; Transfer Y to A |
| 9D64 | LDX #0 ; X=0: clear directory handle |
| 9D66 | STX cur_dir_handle ; Store zero (clear handle) |
| 9D69 | BEQ done_osfind ; ALWAYS branch to finalise ALWAYS branch |
| 9D6B | .check_open_mode←1← 9D46 BEQ |
| LDA fs_func_code ; Load access/open mode byte | |
| 9D6E | ROR ; Rotate right: check bit 0 |
| 9D6F | BCS alloc_fcb_with_flags ; Carry set (bit 0): check read permission |
| 9D71 | ROR ; Rotate right: check bit 1 |
| 9D72 | BCC alloc_fcb_with_flags ; Carry clear (no write): skip |
| 9D74 | BIT fs_data_count ; Test bit 7 of l0f07 (lock flag) |
| 9D77 | BPL alloc_fcb_with_flags ; Not locked: skip |
| 9D79 | TYA ; Transfer Y to A (flags) |
| 9D7A | ORA #&20 ; Set bit 5 (locked file flag) |
| 9D7C | TAY ; Transfer back to Y |
| 9D7D | .alloc_fcb_with_flags←3← 9D6F BCS← 9D72 BCC← 9D77 BPL |
| PLA ; Pull handle from stack | |
| 9D7E | JSR alloc_fcb_slot ; Allocate FCB slot for channel |
| 9D81 | .store_fcb_flags←1← 9D4C BNE |
| TAX ; Transfer to X | |
| 9D82 | TYA ; Transfer Y to A (flags) |
| 9D83 | STA fcb_flags,x ; Store flags in FCB table l1040 |
| 9D86 | TXA ; Transfer X back to A (handle) |
| 9D87 | .done_osfind←2← 9D18 BCS← 9D69 BEQ |
| JMP finalise_and_return ; Jump to finalise and return | |
| 9D8A | .close_all_channels←1← 9D59 BEQ |
| JSR process_all_fcbs ; Process all matching FCBs | |
| 9D8D | TYA ; Transfer Y to A |
| 9D8E | BNE close_specific_chan ; Non-zero channel: close specific |
| 9D90 | LDA fs_options ; Load FS options pointer low |
| 9D92 | PHA ; Push (save for restore) |
| 9D93 | LDA #osbyte_close_spool_exec ; A=&77: OSBYTE close spool/exec files |
| 9D95 | JSR osbyte ; Close any *SPOOL and *EXEC files |
| 9D98 | PLA ; Pull saved options pointer |
| 9D99 | STA fs_options ; Restore FS options pointer |
| 9D9B | LDA #0 ; A=0: clear flags |
| 9D9D | STA fs_last_byte_flag ; Clear last byte flag |
| 9D9F | STA fs_block_offset ; Clear block offset |
| 9DA1 | BEQ send_close_request ; ALWAYS branch to send close request ALWAYS branch |
| 9DA3 | .close_specific_chan←1← 9D8E BNE |
| JSR check_chan_char ; Validate channel character | |
| 9DA6 | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 |
| 9DA9 | .send_close_request←1← 9DA1 BEQ |
| STA fs_cmd_data ; Store as l0f05 (file handle) | |
| 9DAC | LDX #1 ; X=1: argument size |
| 9DAE | LDY #7 ; Y=7: close file command |
| 9DB0 | JSR save_net_tx_cb ; Send close file request |
| 9DB3 | LDY fs_block_offset ; Load block offset |
| 9DB5 | BNE clear_single_fcb ; Non-zero: clear single FCB |
| 9DB7 | CLV ; Clear V flag |
| 9DB8 | JSR scan_fcb_flags ; Scan and clear all FCB flags |
| 9DBB | .done_close←3← 9DC6 BEQ← 9DD8 BCC← 9DEC BPL |
| JMP return_with_last_flag ; Return with last flag | |
| 9DBE | .clear_single_fcb←1← 9DB5 BNE |
| LDA #0 ; A=0: clear FCB entry | |
| 9DC0 | STA fcb_attr_or_count_mid,y ; Clear l1010 (FCB high byte) |
| 9DC3 | STA fcb_flags,y ; Clear l1040 (FCB flags) |
| 9DC6 | BEQ done_close ; ALWAYS branch to return ALWAYS branch |
| 9DC8 | .fscv_0_opt_entry |
| BEQ store_display_flag ; Z set: handle OSARGS 0 | |
| 9DCA | CPX #4 ; Compare X with 4 (number of args) |
| 9DCC | BNE osargs_dispatch ; Not 4: check for error |
| 9DCE | CPY #4 ; Compare Y with 4 |
| 9DD0 | BCC send_osargs_request ; Below 4: handle special OSARGS |
| 9DD2 | .osargs_dispatch←1← 9DCC BNE |
| DEX ; Decrement X | |
| 9DD3 | BNE error_osargs ; X was 1: store display flag |
| 9DD5 | .store_display_flag←1← 9DC8 BEQ |
| STY fs_messages_flag ; Store Y in display control flag l0e06 | |
| 9DD8 | BCC done_close ; Carry clear: return with flag |
| 9DDA | .error_osargs←1← 9DD3 BNE |
| LDA #7 ; A=7: error code | |
| 9DDC | JMP classify_reply_error ; Jump to classify reply error |
| 9DDF | .send_osargs_request←1← 9DD0 BCC |
| STY fs_cmd_data ; Store Y in l0f05 | |
| 9DE2 | LDY #&16 ; Y=&16: OSARGS save command |
| 9DE4 | JSR save_net_tx_cb ; Send OSARGS request |
| 9DE7 | LDY fs_block_offset ; Reload block offset |
| 9DE9 | STY fs_boot_option ; Store in l0e05 |
| 9DEC | BPL done_close ; Positive: return with flag |
| 9DEE | .fscv_1_eof |
| JSR verify_ws_checksum ; Verify workspace checksum | |
| 9DF1 | PHA ; Push result on stack |
| 9DF2 | LDA fs_block_offset ; Load block offset |
| 9DF4 | PHA ; Push block offset |
| 9DF5 | STX cur_chan_attr ; Store X in l10c9 |
| 9DF8 | JSR find_matching_fcb ; Find matching FCB entry |
| 9DFB | BEQ mark_not_found ; Zero: no match found |
| 9DFD | LDA fcb_count_lo,y ; Load FCB low byte from l1000 |
| 9E00 | CMP fcb_buf_offset,x ; Compare with stored offset l1098 |
| 9E03 | BCC mark_not_found ; Below stored: no match |
| 9E05 | LDX #&ff ; X=&FF: mark as found (all bits set) |
| 9E07 | BMI restore_and_return ; ALWAYS branch (negative) ALWAYS branch |
| 9E09 | .mark_not_found←2← 9DFB BEQ← 9E03 BCC |
| LDX #0 ; X=0: mark as not found | |
| 9E0B | .restore_and_return←1← 9E07 BMI |
| PLA ; Restore block offset from stack | |
| 9E0C | TAY ; Transfer to Y |
| 9E0D | PLA ; Restore result from stack |
| 9E0E | 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). |
|
| 9E0F | .update_addr_from_offset9←1← 9F5A JSR |
| LDY #9 ; Y=9: FS options offset for high address | |
| 9E11 | 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.
|
||||
| 9E14 | .update_addr_from_offset1←1← A053 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.
|
||||
| 9E16 | .add_workspace_to_fsopts←1← 9E11 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.
|
||||||
| 9E17 | .adjust_fsopts_4bytes←2← 9F60 JSR← A05F JSR | |||||
| LDX #&fc ; X=&FC: loop counter (-4 to -1) | ||||||
| 9E19 | .loop_adjust_byte←1← 9E2C BNE | |||||
| LDA (fs_options),y ; Load FS options byte at offset Y | ||||||
| 9E1B | BIT fs_load_addr_2 ; Test fs_load_addr_2 bit 7 (add/subtract) | |||||
| 9E1D | BMI subtract_ws_byte ; Bit 7 set: subtract instead | |||||
| 9E1F | ADC fs_cmd_context,x ; Add workspace byte to FS options | |||||
| 9E22 | JMP store_adjusted_byte ; Jump to store result | |||||
| 9E25 | .subtract_ws_byte←1← 9E1D BMI | |||||
| SBC fs_cmd_context,x ; Subtract workspace byte from FS options | ||||||
| 9E28 | .store_adjusted_byte←1← 9E22 JMP | |||||
| STA (fs_options),y ; Store result back to FS options | ||||||
| 9E2A | INY ; Advance to next byte | |||||
| 9E2B | INX ; Advance counter | |||||
| 9E2C | BNE loop_adjust_byte ; Loop until 4 bytes processed | |||||
| 9E2E | RTS ; Return | |||||
| 9E2F | JSR verify_ws_checksum ; Verify workspace checksum | |||||
| 9E32 | JSR set_xfer_params ; Set up transfer parameters | |||||
| 9E35 | PHA ; Push transfer type on stack | |||||
| 9E36 | JSR mask_owner_access ; Set owner-only access mask | |||||
| 9E39 | PLA ; Pull transfer type | |||||
| 9E3A | TAX ; Transfer to X | |||||
| 9E3B | BEQ skip_if_out_of_range ; Zero: no valid operation, return | |||||
| 9E3D | DEX ; Decrement (convert 1-based to 0-based) | |||||
| 9E3E | CPX #8 ; Compare with 8 (max operation) | |||||
| 9E40 | BCC valid_osgbpb_op ; Below 8: valid operation | |||||
| 9E42 | .skip_if_out_of_range←1← 9E3B BEQ | |||||
| JMP return_with_last_flag ; Out of range: return with flag | ||||||
| 9E45 | .valid_osgbpb_op←1← 9E40 BCC | |||||
| TXA ; Transfer operation code to A | ||||||
| 9E46 | LDY #0 ; Y=0: buffer offset | |||||
| 9E48 | PHA ; Push operation code | |||||
| 9E49 | CMP #4 ; Compare with 4 (write operations) | |||||
| 9E4B | BCC load_chan_handle ; Below 4: read operation | |||||
| 9E4D | JMP write_block_entry ; 4 or above: write data block | |||||
| 9E50 | .load_chan_handle←1← 9E4B BCC | |||||
| LDA (fs_options),y ; Load channel handle from FS options | ||||||
| 9E52 | PHA ; Push handle | |||||
| 9E53 | JSR check_not_dir ; Check file is not a directory | |||||
| 9E56 | PLA ; Pull handle | |||||
| 9E57 | TAY ; Transfer to Y | |||||
| 9E58 | JSR process_all_fcbs ; Process all matching FCBs | |||||
| 9E5B | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 | |||||
| 9E5E | STA fs_cmd_data ; Store file handle in l0f05 | |||||
| 9E61 | LDA #0 ; A=0: clear direction flag | |||||
| 9E63 | STA fs_func_code ; Store in l0f06 | |||||
| 9E66 | LDA fcb_count_lo,x ; Load FCB low byte (position) | |||||
| 9E69 | STA fs_data_count ; Store in l0f07 | |||||
| 9E6C | LDA fcb_attr_or_count_mid,x ; Load FCB high byte | |||||
| 9E6F | STA fs_reply_cmd ; Store in l0f08 | |||||
| 9E72 | LDA fcb_station_or_count_hi,x ; Load FCB extent byte | |||||
| 9E75 | STA fs_load_vector ; Store in l0f09 | |||||
| 9E78 | LDY #&0d ; Y=&0D: TX buffer size | |||||
| 9E7A | LDX #5 ; X=5: argument count | |||||
| 9E7C | JSR save_net_tx_cb ; Send TX control block to server | |||||
| 9E7F | PLA ; Pull operation code | |||||
| 9E80 | JSR setup_transfer_workspace ; Set up transfer workspace | |||||
| 9E83 | PHP ; Save flags (carry from setup) | |||||
| 9E84 | LDY #0 ; Y=0: index for channel handle | |||||
| 9E86 | LDA (fs_options),y ; Load channel handle from FS options | |||||
| 9E88 | BCS set_write_active ; Carry set (write): set active | |||||
| 9E8A | JSR clear_conn_active ; Read: clear connection active | |||||
| 9E8D | BPL setup_gbpb_request ; Branch to continue (always positive) | |||||
| 9E8F | .set_write_active←1← 9E88 BCS | |||||
| JSR set_conn_active ; Write: set connection active | ||||||
| 9E92 | .setup_gbpb_request←1← 9E8D BPL | |||||
| STY fs_func_code ; Clear l0f06 (Y=0) | ||||||
| 9E95 | JSR lookup_cat_slot_data ; Look up channel slot data | |||||
| 9E98 | STA fs_cmd_data ; Store flag byte in l0f05 | |||||
| 9E9B | LDY #&0c ; Y=&0C: TX buffer size (short) | |||||
| 9E9D | LDX #2 ; X=2: argument count | |||||
| 9E9F | JSR save_net_tx_cb ; Send TX control block | |||||
| 9EA2 | JSR lookup_cat_entry_0 ; Look up channel entry at Y=0 | |||||
| 9EA5 | LDY #9 ; Y=9: FS options offset for position | |||||
| 9EA7 | LDA fs_cmd_data ; Load new position low from l0f05 | |||||
| 9EAA | STA fcb_count_lo,x ; Update FCB low byte in l1000 | |||||
| 9EAD | STA (fs_options),y ; Store in FS options at Y=9 | |||||
| 9EAF | INY ; Y=&0A Y=&0a | |||||
| 9EB0 | LDA fs_func_code ; Load new position high from l0f06 | |||||
| 9EB3 | STA fcb_attr_or_count_mid,x ; Update FCB high byte in l1010 | |||||
| 9EB6 | STA (fs_options),y ; Store in FS options at Y=&0A | |||||
| 9EB8 | INY ; Y=&0B Y=&0b | |||||
| 9EB9 | LDA fs_data_count ; Load new extent from l0f07 | |||||
| 9EBC | STA fcb_station_or_count_hi,x ; Update FCB extent in l1020 | |||||
| 9EBF | STA (fs_options),y ; Store in FS options at Y=&0B | |||||
| 9EC1 | LDA #0 ; A=0: clear high byte of extent | |||||
| 9EC3 | INY ; Y=&0C Y=&0c | |||||
| 9EC4 | STA (fs_options),y ; Store zero in FS options at Y=&0C | |||||
| 9EC6 | PLP ; Restore flags | |||||
| 9EC7 | BCC return_success ; Carry clear: skip last-byte check | |||||
| 9EC9 | LDA fs_last_byte_flag ; Load last-byte-of-transfer flag | |||||
| 9ECB | CMP #3 ; Is transfer still pending (flag=3)? | |||||
| 9ECD | .return_success←1← 9EC7 BCC | |||||
| LDA #0 ; A=0: success | ||||||
| 9ECF | 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.
|
||||||
| 9ED2 | .lookup_cat_entry_0←2← 9EA2 JSR← 9EDE JSR | |||||
| LDY #0 ; Y=0: offset for channel handle | ||||||
| 9ED4 | 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.
|
|||||||||
| 9ED6 | .lookup_cat_slot_data←1← 9E95 JSR | ||||||||
| JSR lookup_chan_by_char ; Look up channel by character | |||||||||
| 9ED9 | LDA fcb_net_or_port,x ; Load FCB flag byte from l1030 | ||||||||
| 9EDC | 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. |
|
| 9EDD | .setup_transfer_workspace←2← 9E80 JSR← B987 JMP |
| PHA ; Push operation code on stack | |
| 9EDE | JSR lookup_cat_entry_0 ; Look up channel entry at Y=0 |
| 9EE1 | STA fs_cmd_data ; Store flag byte in l0f05 |
| 9EE4 | LDY #&0b ; Y=&0B: source offset in FS options |
| 9EE6 | LDX #6 ; X=6: copy 6 bytes |
| 9EE8 | .loop_copy_opts_to_buf←1← 9EF4 BNE |
| LDA (fs_options),y ; Load FS options byte | |
| 9EEA | STA fs_func_code,x ; Store in l0f06 buffer |
| 9EED | DEY ; Decrement source index |
| 9EEE | CPY #8 ; Skip offset 8? |
| 9EF0 | BNE skip_struct_hole ; No: continue copy |
| 9EF2 | DEY ; Skip offset 8 (hole in structure) |
| 9EF3 | .skip_struct_hole←1← 9EF0 BNE |
| DEX ; Decrement destination counter | |
| 9EF4 | BNE loop_copy_opts_to_buf ; Loop until all 6 bytes copied |
| 9EF6 | PLA ; Pull operation code |
| 9EF7 | LSR ; Shift right: check bit 0 (direction) |
| 9EF8 | PHA ; Push updated code |
| 9EF9 | BCC store_direction_flag ; Carry clear: OSBGET (read) |
| 9EFB | INX ; Carry set: OSBPUT (write), X=1 |
| 9EFC | .store_direction_flag←1← 9EF9 BCC |
| STX fs_func_code ; Store direction flag in l0f06 | |
| 9EFF | LDY #&0b ; Y=&0B: TX buffer size |
| 9F01 | LDX #&91 ; X=&91: port for OSBGET |
| 9F03 | PLA ; Pull operation code |
| 9F04 | PHA ; Push back (keep on stack) |
| 9F05 | BEQ store_port_and_send ; Zero (OSBGET): keep port &91 |
| 9F07 | LDX #&92 ; X=&92: port for OSBPUT |
| 9F09 | DEY ; Y=&0A: adjusted buffer size Y=&0a |
| 9F0A | .store_port_and_send←1← 9F05 BEQ |
| STX fs_cmd_urd ; Store port in l0f02 | |
| 9F0D | STX fs_error_ptr ; Store port in fs_error_ptr |
| 9F0F | LDX #8 ; X=8: argument count |
| 9F11 | LDA fs_cmd_data ; Load file handle from l0f05 |
| 9F14 | JSR send_request_nowrite ; Send request (no write data) |
| 9F17 | LDX #0 ; X=0: index |
| 9F19 | LDA (fs_options,x) ; Load channel handle from FS options |
| 9F1B | TAX ; Transfer to X as index |
| 9F1C | LDA fcb_flags,x ; Load FCB flags from l1040 |
| 9F1F | EOR #1 ; Toggle bit 0 (transfer direction) |
| 9F21 | STA fcb_flags,x ; Store updated flags |
| 9F24 | CLC ; Clear carry for addition |
| 9F25 | LDX #4 ; X=4: process 4 address bytes |
| 9F27 | .loop_setup_addr_bytes←1← 9F3B BNE |
| LDA (fs_options),y ; Load FS options address byte | |
| 9F29 | STA addr_work,y ; Store in zero page address area |
| 9F2C | STA txcb_pos,y ; Store in TXCB position |
| 9F2F | JSR advance_y_by_4 ; Advance Y by 4 |
| 9F32 | ADC (fs_options),y ; Add offset from FS options |
| 9F34 | STA addr_work,y ; Store computed end address |
| 9F37 | JSR retreat_y_by_3 ; Retreat Y by 3 for next pair |
| 9F3A | DEX ; Decrement byte counter |
| 9F3B | BNE loop_setup_addr_bytes ; Loop for all 4 address bytes |
| 9F3D | INX ; X=1 (INX from 0) |
| 9F3E | .loop_copy_offset←1← 9F45 BPL |
| LDA fs_cmd_csd,x ; Load offset from l0f03 | |
| 9F41 | STA fs_func_code,x ; Copy to l0f06 |
| 9F44 | DEX ; Decrement counter |
| 9F45 | BPL loop_copy_offset ; Loop until both bytes copied |
| 9F47 | PLA ; Pull operation code |
| 9F48 | BNE send_with_swap ; Non-zero (OSBPUT): swap addresses |
| 9F4A | LDA fs_cmd_urd ; Load port from l0f02 |
| 9F4D | JSR check_and_setup_txcb ; Check and set up TXCB |
| 9F50 | BCS recv_and_update ; Carry set: skip swap |
| 9F52 | .send_with_swap←1← 9F48 BNE |
| JSR send_txcb_swap_addrs ; Send TXCB and swap start/end addresses | |
| 9F55 | .recv_and_update←1← 9F50 BCS |
| JSR recv_reply_preserve_flags ; Receive and process reply | |
| 9F58 | STX fs_load_addr_2 ; Store result in fs_load_addr_2 |
| 9F5A | JSR update_addr_from_offset9 ; Update addresses from offset 9 |
| 9F5D | DEC fs_load_addr_2 ; Decrement fs_load_addr_2 |
| 9F5F | SEC ; Set carry for subtraction |
| 9F60 | JSR adjust_fsopts_4bytes ; Adjust FS options by 4 bytes |
| 9F63 | ASL fs_cmd_data ; Shift l0f05 left (update status) |
| 9F66 | RTS ; Return |
Receive and process reply, preserving flagsWrapper around recv_and_process_reply that saves and restores the processor status register, so the caller's flag state is not affected by the reply processing. |
|
| 9F67 | .recv_reply_preserve_flags←1← 9F55 JSR |
| PHP ; Save flags before reply processing | |
| 9F68 | JSR recv_and_process_reply ; Process server reply |
| 9F6B | PLP ; Restore flags after reply processing |
| 9F6C | RTS ; Return |
| 9F6D | .send_osbput_data←1← 9F9D BEQ |
| LDY #&15 ; Y=&15: TX buffer size for OSBPUT data | |
| 9F6F | JSR save_net_tx_cb ; Send TX control block |
| 9F72 | LDA fs_boot_option ; Load display flag from l0e05 |
| 9F75 | STA fs_boot_data ; Store in l0f16 |
| 9F78 | STX fs_load_addr ; Clear fs_load_addr (X=0) |
| 9F7A | STX fs_load_addr_hi ; Clear fs_load_addr_hi |
| 9F7C | LDA #&12 ; A=&12: byte count for data block |
| 9F7E | STA fs_load_addr_2 ; Store in fs_load_addr_2 |
| 9F80 | BNE write_data_block ; ALWAYS branch to write data block ALWAYS branch |
| 9F82 | .write_block_entry←1← 9E4D JMP |
| LDY #4 ; Y=4: offset for station comparison | |
| 9F84 | LDA tube_present ; Load stored station from l0d63 |
| 9F87 | BEQ store_station_result ; Zero: skip station check |
| 9F89 | CMP (fs_options),y ; Compare with FS options station |
| 9F8B | BNE store_station_result ; Mismatch: skip subtraction |
| 9F8D | DEY ; Y=3 Y=&03 |
| 9F8E | SBC (fs_options),y ; Subtract FS options value |
| 9F90 | .store_station_result←2← 9F87 BEQ← 9F8B BNE |
| STA svc_state ; Store result in svc_state | |
| 9F92 | .loop_copy_opts_to_ws←1← 9F98 BNE |
| LDA (fs_options),y ; Load FS options byte at Y | |
| 9F94 | STA fs_last_byte_flag,y ; Store in workspace at fs_last_byte_flag+Y |
| 9F97 | DEY ; Decrement index |
| 9F98 | BNE loop_copy_opts_to_ws ; Loop until all bytes copied |
| 9F9A | PLA ; Pull operation code |
| 9F9B | AND #3 ; Mask to 2-bit sub-operation |
| 9F9D | BEQ send_osbput_data ; Zero: send OSBPUT data |
| 9F9F | LSR ; Shift right: check bit 0 |
| 9FA0 | BEQ handle_cat_update ; Zero (bit 0 clear): handle read |
| 9FA2 | BCS update_cat_position ; Carry set: handle catalogue update |
| 9FA4 | .handle_cat_update←1← 9FA0 BEQ |
| TAY ; Transfer to Y (Y=0) | |
| 9FA5 | LDA fs_csd_handle,y ; Load data byte from l0e03 |
| 9FA8 | STA fs_cmd_csd ; Store in l0f03 |
| 9FAB | LDA fs_lib_handle ; Load high data byte from l0e04 |
| 9FAE | STA fs_cmd_lib ; Store in l0f04 |
| 9FB1 | LDA fs_urd_handle ; Load port from l0e02 |
| 9FB4 | STA fs_cmd_urd ; Store in l0f02 |
| 9FB7 | LDX #&12 ; X=&12: buffer size marker |
| 9FB9 | STX fs_cmd_y_param ; Store in l0f01 |
| 9FBC | LDA #&0d ; A=&0D: count value |
| 9FBE | STA fs_func_code ; Store in l0f06 |
| 9FC1 | STA fs_load_addr_2 ; Store in fs_load_addr_2 |
| 9FC3 | LSR ; Shift right (A=6) |
| 9FC4 | STA fs_cmd_data ; Store in l0f05 |
| 9FC7 | CLC ; Clear carry for addition |
| 9FC8 | JSR prep_send_tx_cb ; Prepare and send TX control block |
| 9FCB | STX fs_load_addr_hi ; Store X in fs_load_addr_hi (X=0) |
| 9FCD | INX ; X=1 (INX) |
| 9FCE | 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. |
|
| 9FD0 | .write_data_block←2← 9F80 BNE← A048 JSR |
| LDA svc_state ; Load svc_state (tube flag) | |
| 9FD2 | BNE tube_write_setup ; Non-zero: write via tube |
| 9FD4 | LDX fs_load_addr ; Load source index from fs_load_addr |
| 9FD6 | LDY fs_load_addr_hi ; Load destination index from fs_load_addr_hi |
| 9FD8 | .loop_copy_to_host←1← 9FE1 BNE |
| LDA fs_cmd_data,x ; Load data byte from l0f05 buffer | |
| 9FDB | STA (fs_crc_lo),y ; Store to destination via fs_crc pointer |
| 9FDD | INX ; Advance source index |
| 9FDE | INY ; Advance destination index |
| 9FDF | DEC fs_load_addr_2 ; Decrement byte counter |
| 9FE1 | BNE loop_copy_to_host ; Loop until all bytes transferred |
| 9FE3 | BEQ tail_update_catalogue ; ALWAYS branch to update catalogue ALWAYS branch |
| 9FE5 | .tube_write_setup←1← 9FD2 BNE |
| JSR tube_claim_c3 ; Claim tube with call &C3 | |
| 9FE8 | LDA #1 ; A=1: tube transfer type (write) |
| 9FEA | LDX fs_options ; Load destination low from fs_options |
| 9FEC | LDY fs_block_offset ; Load destination high from fs_block_offset |
| 9FEE | INX ; Increment low byte |
| 9FEF | BNE set_tube_addr ; No wrap: skip high increment |
| 9FF1 | INY ; Carry: increment high byte |
| 9FF2 | .set_tube_addr←1← 9FEF BNE |
| JSR tube_addr_data_dispatch ; Set up tube transfer address | |
| 9FF5 | LDX fs_load_addr ; Load source index |
| 9FF7 | .loop_write_to_tube←1← A005 BNE |
| LDA fs_cmd_data,x ; Load data byte from buffer | |
| 9FFA | STA tube_data_register_3 ; Write to tube data register 3 |
| 9FFD | INX ; Advance source index |
| 9FFE | LDY #6 ; Y=6: tube write delay |
| A000 | .loop_tube_delay←1← A001 BNE |
| DEY ; Delay loop: decrement Y | |
| A001 | BNE loop_tube_delay ; Loop until delay complete |
| A003 | DEC fs_load_addr_2 ; Decrement byte counter |
| A005 | BNE loop_write_to_tube ; Loop until all bytes written to tube |
| A007 | LDA #&83 ; A=&83: release tube claim |
| A009 | JSR tube_addr_data_dispatch ; Release tube |
| A00C | .tail_update_catalogue←2← 9FE3 BEQ← A070 JMP |
| JMP clear_result ; Jump to clear A and finalise return | |
| A00F | .update_cat_position←1← 9FA2 BCS |
| LDY #9 ; Y=9: offset for position byte | |
| A011 | LDA (fs_options),y ; Load position from FS options |
| A013 | STA fs_func_code ; Store in l0f06 |
| A016 | LDY #5 ; Y=5: offset for extent byte |
| A018 | LDA (fs_options),y ; Load extent byte from FS options |
| A01A | STA fs_data_count ; Store in l0f07 |
| A01D | LDX #&0d ; X=&0D: byte count |
| A01F | STX fs_reply_cmd ; Store in l0f08 |
| A022 | LDY #2 ; Y=2: command sub-type |
| A024 | STY fs_load_addr ; Store in fs_load_addr |
| A026 | STY fs_cmd_data ; Store in l0f05 |
| A029 | INY ; Y=3: TX buffer command byte Y=&03 |
| A02A | JSR save_net_tx_cb ; Send TX control block |
| A02D | STX fs_load_addr_hi ; Store X (0) in fs_load_addr_hi |
| A02F | LDA fs_func_code ; Load data offset from l0f06 |
| A032 | STA (fs_options,x) ; Store as first byte of FS options |
| A034 | LDA fs_cmd_data ; Load data count from l0f05 |
| A037 | LDY #9 ; Y=9: position offset in FS options |
| A039 | ADC (fs_options),y ; Add to current position |
| A03B | STA (fs_options),y ; Store updated position |
| A03D | LDA txcb_end ; Load TXCB end byte |
| A03F | SBC #7 ; Subtract 7 (header overhead) |
| A041 | STA fs_func_code ; Store remaining data size |
| A044 | STA fs_load_addr_2 ; Store in fs_load_addr_2 (byte count) |
| A046 | BEQ clear_buf_after_write ; Zero bytes: skip write |
| A048 | JSR write_data_block ; Write data block to host/tube |
| A04B | .clear_buf_after_write←1← A046 BEQ |
| LDX #2 ; X=2: clear 3 bytes (indices 0-2) | |
| A04D | .loop_clear_buf←1← A051 BPL |
| STA fs_data_count,x ; Clear l0f07+X | |
| A050 | DEX ; Decrement index |
| A051 | BPL loop_clear_buf ; Loop until all cleared |
| A053 | JSR update_addr_from_offset1 ; Update addresses from offset 1 |
| A056 | SEC ; Set carry for subtraction |
| A057 | DEC fs_load_addr_2 ; Decrement fs_load_addr_2 |
| A059 | LDA fs_cmd_data ; Load data count from l0f05 |
| A05C | STA fs_func_code ; Copy to l0f06 |
| A05F | JSR adjust_fsopts_4bytes ; Adjust FS options by 4 bytes (subtract) |
| A062 | LDX #3 ; X=3: check 4 bytes |
| A064 | LDY #5 ; Y=5: starting offset |
| A066 | SEC ; Set carry for comparison |
| A067 | .loop_check_remaining←1← A06D BPL |
| LDA (fs_options),y ; Load FS options byte | |
| A069 | BNE done_write_block ; Non-zero: more data remaining |
| A06B | INY ; Advance to next byte |
| A06C | DEX ; Decrement counter |
| A06D | BPL loop_check_remaining ; Loop until all bytes checked |
| A06F | CLC ; All zero: clear carry (transfer complete) |
| A070 | .done_write_block←1← A069 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. |
|
| A073 | .tube_claim_c3←3← 9FE5 JSR← A078 BCC← A2E3 JSR |
| LDA #&c3 ; A=&C3: tube claim protocol | |
| A075 | JSR tube_addr_data_dispatch ; Dispatch tube address/data claim |
| A078 | BCC tube_claim_c3 ; Carry clear: claim failed, retry |
| A07A | 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.
|
||||
| A07B | .cmd_fs | |||
| LDA fs_server_stn ; Load current FS station high | ||||
| A07E | STA fs_work_5 ; Save to fs_work_5 | |||
| A080 | LDA fs_server_net ; Load current FS station low | |||
| A083 | STA fs_work_6 ; Save to l00b6 | |||
| A085 | LDA (fs_crc_lo),y ; Get first character of argument | |||
| A087 | CMP #&0d ; Is it CR (no argument)? | |||
| A089 | BEQ print_current_fs ; No arg: print current FS info | |||
| A08B | JSR parse_fs_ps_args ; Parse FS/PS station arguments | |||
| A08E | LDA #1 ; A=1: write NFS info | |||
| A090 | STA fs_work_4 ; Store OSWORD sub-function | |||
| A092 | LDA #&13 ; OSWORD &13: NFS information | |||
| A094 | LDX #<(fs_work_4) ; Parameter block low | |||
| A096 | LDY #>(fs_work_4) ; Parameter block high | |||
| A098 | JMP osword ; Read/Write NFS information (see https://beebwiki.mdfs.net/OSWORDs) | |||
| A09B | .print_current_fs←1← A089 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. |
|
| A09E | .print_fs_info_newline←1← B0B6 JSR |
| BIT bit_test_ff ; Set V (suppress padding) | |
| A0A1 | JSR print_station_addr ; Print station address |
| A0A4 | 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. |
|
| A0A7 | .parse_fs_ps_args←3← A08B JSR← B011 JSR← B1E8 JSR |
| TXA ; Save X on stack | |
| A0A8 | PHA ; Push X |
| A0A9 | LDA #0 ; A=0: initialise dot-seen flag |
| A0AB | STA fs_work_4 ; Clear dot-seen flag |
| A0AD | JSR parse_addr_arg ; Parse first number (network) |
| A0B0 | BCS skip_if_no_station ; C set: number found, check for dot |
| A0B2 | TYA ; Save Y (command line offset) |
| A0B3 | PHA ; Push Y |
| A0B4 | JSR init_bridge_poll ; Initialise bridge polling |
| A0B7 | EOR fs_load_addr_2 ; Compare bridge result with parsed value |
| A0B9 | BEQ store_station_lo ; Same: keep bridge result |
| A0BB | LDA fs_load_addr_2 ; Different: use parsed value |
| A0BD | .store_station_lo←1← A0B9 BEQ |
| STA fs_work_6 ; Store station low byte | |
| A0BF | PLA ; Restore Y |
| A0C0 | TAY ; Transfer back to Y |
| A0C1 | INY ; Skip dot separator |
| A0C2 | JSR parse_addr_arg ; Parse second number (station) |
| A0C5 | .skip_if_no_station←1← A0B0 BCS |
| BEQ done_parse_fs_ps ; Zero result: skip store | |
| A0C7 | STA fs_work_5 ; Store station high byte |
| A0C9 | .done_parse_fs_ps←1← A0C5 BEQ |
| PLA ; Restore X | |
| A0CA | TAX ; Transfer back to X |
| A0CB | 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. |
|
| A0CC | .get_pb_ptr_as_index←2← A0EA JSR← A0FA 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.
|
|||||||
| A0CE | .byte_to_2bit_index←4← 8F30 JSR← A5D1 JSR← A5EA JSR← B108 JSR | ||||||
| ASL ; Shift left (A * 2) | |||||||
| A0CF | ASL ; Shift left (A * 4) | ||||||
| A0D0 | PHA ; Save A * 4 on stack | ||||||
| A0D1 | ASL ; Shift left (A * 8) | ||||||
| A0D2 | TSX ; Get stack pointer | ||||||
| A0D3 | PHP ; Save flags (carry from shift) | ||||||
| A0D4 | ADC error_text,x ; A*8 + A*4 (from stack) = A*12 | ||||||
| A0D7 | ROR ; Divide by 2 with carry | ||||||
| A0D8 | PLP ; Restore original flags | ||||||
| A0D9 | ASL ; Shift left again | ||||||
| A0DA | TAY ; Result to Y as index | ||||||
| A0DB | PLA ; Pop saved A * 4 | ||||||
| A0DC | CMP #&48 ; A * 4 >= &48 (out of range)? | ||||||
| A0DE | BCC return_from_2bit_index ; In range: return | ||||||
| A0E0 | LDY #0 ; Out of range: Y=0 | ||||||
| A0E2 | TYA ; A=&00 | ||||||
| A0E3 | .return_from_2bit_index←1← A0DE BCC | ||||||
| RTS ; Return with A=index, Y=index | |||||||
| A0E4 | .net_1_read_handle | ||||||
| LDY #&6f ; Y=&6F: source offset | |||||||
| A0E6 | LDA (net_rx_ptr),y ; Load byte from RX buffer | ||||||
| A0E8 | BCC store_pb_result ; C clear: store directly | ||||||
| A0EA | .net_2_read_handle_entry | ||||||
| JSR get_pb_ptr_as_index ; Get index from PB pointer | |||||||
| A0ED | BCS return_zero_uninit ; C set (out of range): clear value | ||||||
| A0EF | LDA (nfs_workspace),y ; Load workspace byte at index | ||||||
| A0F1 | CMP #&3f ; Is it '?' (uninitialised)? | ||||||
| A0F3 | BNE store_pb_result ; No: use value from RX buffer | ||||||
| A0F5 | .return_zero_uninit←1← A0ED BCS | ||||||
| LDA #0 ; A=0: return zero for uninitialised | |||||||
| A0F7 | .store_pb_result←2← A0E8 BCC← A0F3 BNE | ||||||
| STA osword_pb_ptr ; Store result to PB pointer | |||||||
| A0F9 | RTS ; Return | ||||||
| A0FA | .net_3_close_handle | ||||||
| JSR get_pb_ptr_as_index ; Get index from PB pointer | |||||||
| A0FD | BCC mark_ws_uninit ; C clear: store to workspace | ||||||
| A0FF | ROR fs_flags ; Save carry to l0d6c bit 7 | ||||||
| A102 | LDA osword_pb_ptr ; Load PB pointer value | ||||||
| A104 | ROL ; Shift carry back in | ||||||
| A105 | ROL fs_flags ; Restore l0d6c bit 7 | ||||||
| A108 | RTS ; Return | ||||||
| A109 | .mark_ws_uninit←1← A0FD BCC | ||||||
| ROR econet_flags ; Save carry to l0d61 bit 7 | |||||||
| A10C | LDA #&3f ; A='?': mark as uninitialised | ||||||
| A10E | STA (nfs_workspace),y ; Store '?' to workspace | ||||||
| A110 | ROL econet_flags ; Restore l0d61 bit 7 | ||||||
| A113 | RTS ; Return | ||||||
| A114 | .fscv_3_star_cmd←1← 8CF9 JMP | ||||||
| JSR set_text_and_xfer_ptr ; Set text and transfer pointers | |||||||
| A117 | LDY #&ff ; Y=&FF: prepare for INY to 0 | ||||||
| A119 | STY fs_spool_handle ; Clear spool handle (no spool active) | ||||||
| A11B | STY need_release_tube ; Set escapable flag (&FF) | ||||||
| A11D | INY ; Y=&00 | ||||||
| A11E | LDX #&4a ; X=&4A: FS command table offset | ||||||
| A120 | JSR match_fs_cmd ; Match command in FS table | ||||||
| A123 | BCS dispatch_fs_cmd ; C set: command found | ||||||
| A125 | .cmd_fs_reentry←1← 8C5A JMP | ||||||
| BVC dispatch_fs_cmd ; V clear: syntax error | |||||||
| A127 | .error_syntax←1← AF6F JMP | ||||||
| LDA #&dc ; Error code &DC | |||||||
| A129 | JSR error_inline ; Generate 'Syntax' error | ||||||
| A12C | EQUS "Syntax." | ||||||
| A133 | .dispatch_fs_cmd←2← A123 BCS← A125 BVC | ||||||
| LDA #0 ; A=0: clear service state | |||||||
| A135 | STA svc_state ; Store cleared service state | ||||||
| A137 | LDA cmd_table_fs_hi,x ; Load command handler address high | ||||||
| A13A | PHA ; Push high byte | ||||||
| A13B | LDA cmd_table_fs_lo,x ; Load command handler address low | ||||||
| A13E | PHA ; Push low byte | ||||||
| A13F | 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. |
|
| A140 | .match_fs_cmd←5← 8C55 JSR← 8C83 JSR← A120 JSR← B320 JSR← B34D JSR |
| TYA ; Save Y (command line offset) | |
| A141 | PHA ; Push on stack |
| A142 | .restart_table_scan←1← A168 BNE |
| PLA ; Restore saved Y | |
| A143 | PHA ; Push back (keep on stack) |
| A144 | TAY ; Transfer to Y |
| A145 | LDA cmd_table_fs,x ; Load table entry byte |
| A148 | BMI check_char_type ; Bit 7 set: end of table names |
| A14A | .loop_match_char←1← A157 BNE |
| LDA cmd_table_fs,x ; Load table byte | |
| A14D | BMI check_separator ; Bit 7 set: end of this name |
| A14F | EOR (fs_crc_lo),y ; Compare with command line char |
| A151 | AND #&df ; Case-insensitive compare |
| A153 | BNE skip_entry_chars ; Mismatch: skip to next entry |
| A155 | INY ; Match: advance command line |
| A156 | INX ; Advance table pointer |
| A157 | BNE loop_match_char ; Loop for next character |
| A159 | .skip_entry_chars←2← A153 BNE← A15D BPL |
| INX ; Advance past remaining table chars | |
| A15A | LDA cmd_table_fs,x ; Load next table byte |
| A15D | BPL skip_entry_chars ; Bit 7 clear: more chars to skip |
| A15F | LDA (fs_crc_lo),y ; Check command line terminator |
| A161 | CMP #&2e ; Is it '.' (abbreviation)? |
| A163 | BEQ skip_dot_and_spaces ; Yes: skip spaces after dot |
| A165 | .loop_skip_to_next←1← A17A BNE |
| INX ; X += 3: skip flags and address bytes | |
| A166 | INX ; (continued) |
| A167 | INX ; (continued) |
| A168 | BNE restart_table_scan ; Try next table entry |
| A16A | .check_separator←1← A14D BMI |
| TYA ; Save Y (end of matched name) | |
| A16B | PHA ; Push position |
| A16C | LDA (fs_crc_lo),y ; Load char after matched portion |
| A16E | LDY #9 ; Y=9: check 10 separator chars |
| A170 | .loop_check_sep_table←1← A176 BPL |
| CMP sep_table_data,y ; Compare with separator table | |
| A173 | BEQ separator_matched ; Match: valid command separator |
| A175 | DEY ; Try next separator |
| A176 | BPL loop_check_sep_table ; Loop through separator list |
| A178 | PLA ; No separator match: restore Y |
| A179 | TAY ; Transfer back to Y |
| A17A | 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. | |
| A17C | .sep_table_data←1← A170 CMP |
| EQUB &20 ; Space | |
| A17D | EQUB &22 ; '"' double quote |
| A17E | EQUB &23 ; '#' hash |
| A17F | EQUB &24 ; '$' dollar |
| A180 | EQUB &26 ; '&' ampersand |
| A181 | EQUB &2A ; '*' asterisk |
| A182 | EQUB &3A ; ':' colon |
| A183 | EQUB &40 ; '@' at-sign |
| A184 | EQUB &0D ; CR (carriage return) |
| A185 | .separator_matched←1← A173 BEQ |
| PLA ; Restore saved Y | |
| A186 | TAY ; Transfer to Y |
| A187 | .loop_skip_trail_spaces←1← A18E JMP |
| LDA (fs_crc_lo),y ; Load next char | |
| A189 | CMP #&20 ; Is it space? |
| A18B | BNE check_cmd_flags ; No: done skipping |
| A18D | .skip_dot_and_spaces←1← A163 BEQ |
| INY ; Advance past space | |
| A18E | JMP loop_skip_trail_spaces ; Loop for more spaces |
| A191 | .check_cmd_flags←1← A18B BNE |
| LDA cmd_table_fs,x ; Load command flags byte | |
| A194 | ASL ; Shift: check 'no-arg' bit |
| A195 | BPL clear_v_flag ; Bit clear: allow arguments |
| A197 | LDA (fs_crc_lo),y ; Check if line ends here |
| A199 | CMP #&0d ; Is it CR? |
| A19B | BNE clear_v_flag ; No: argument present, V clear |
| A19D | BIT bit_test_ff ; CR found: set V (no argument) |
| A1A0 | BVS clear_c_flag ; V set: command is valid |
| A1A2 | .clear_v_flag←2← A195 BPL← A19B BNE |
| CLV ; Clear V (argument present) | |
| A1A3 | .clear_c_flag←1← A1A0 BVS |
| CLC ; C=0: command not found | |
| A1A4 | .return_with_result←1← A1BF BCS |
| PLA ; Pop saved Y from stack | |
| A1A5 | LDA (fs_crc_lo),y ; Load command line char at Y |
| A1A7 | RTS ; Return (C and V set per result) |
| A1A8 | .loop_scan_past_word←1← A1B5 BNE |
| INY ; Advance past character | |
| A1A9 | .check_char_type←1← A148 BMI |
| LDA (fs_crc_lo),y ; Load current char | |
| A1AB | CMP #&0d ; Is it CR (end of line)? |
| A1AD | BEQ set_c_and_return ; Yes: end of input |
| A1AF | CMP #&2e ; Is it '.' (abbreviation dot)? |
| A1B1 | BEQ skip_sep_spaces ; Yes: skip to next word |
| A1B3 | CMP #&20 ; Is it space? |
| A1B5 | BNE loop_scan_past_word ; No: keep scanning |
| A1B7 | .skip_sep_spaces←2← A1B1 BEQ← A1BC BEQ |
| INY ; Skip past separator | |
| A1B8 | LDA (fs_crc_lo),y ; Load next char |
| A1BA | CMP #&20 ; Is it space? |
| A1BC | BEQ skip_sep_spaces ; Yes: skip consecutive spaces |
| A1BE | .set_c_and_return←1← A1AD BEQ |
| SEC ; C=1: have more text to match | |
| A1BF | BCS return_with_result ; ALWAYS branch |
| A1C1 | .fscv_2_star_run←1← 8E1D JMP |
| JSR save_ptr_to_os_text ; Save text pointer | |
| A1C4 | JSR mask_owner_access ; Set owner-only access mask |
| A1C7 | JSR parse_cmd_arg_y0 ; Parse command argument (Y=0) |
| A1CA | .open_file_for_run←1← A241 BNE |
| LDX #1 ; X=1: buffer index | |
| A1CC | JSR copy_arg_to_buf ; Copy argument to buffer |
| A1CF | LDA #2 ; A=2: open for update |
| A1D1 | STA fs_cmd_data ; Store open mode |
| A1D4 | LDY #&12 ; Y=&12: open file command |
| A1D6 | JSR save_net_tx_cb ; Send open request to server |
| A1D9 | LDA fs_cmd_data ; Load reply status |
| A1DC | CMP #1 ; Status 1 (success)? |
| A1DE | BNE try_library_path ; No: file not found, try library |
| A1E0 | LDX #3 ; X=3: check 4 handle bytes |
| A1E2 | .loop_check_handles←1← A1EB BPL |
| INC fs_handle_check,x ; Increment handle byte | |
| A1E5 | BEQ alloc_run_fcb ; Was &FF (overflow to 0): try next |
| A1E7 | JMP check_exec_addr ; Non-zero: handle valid, execute |
| A1EA | .alloc_run_fcb←1← A1E5 BEQ |
| DEX ; Try next handle byte | |
| A1EB | BPL loop_check_handles ; Loop until all checked |
| A1ED | JSR alloc_fcb_or_error ; Allocate new FCB or raise error |
| A1F0 | LDX #1 ; X=1: open mode index |
| A1F2 | STX fs_cmd_data ; Store in l0f05 |
| A1F5 | STX fs_func_code ; Store in l0f06 |
| A1F8 | INX ; X=2 X=&02 |
| A1F9 | JSR copy_arg_to_buf ; Copy argument to buffer |
| A1FC | LDY #6 ; Y=6: re-open command |
| A1FE | JSR save_net_tx_cb ; Send re-open request |
| A201 | BCS done_run_dispatch ; C set: error on re-open |
| A203 | JMP alloc_run_channel ; C clear: finalise file opening |
| A206 | .done_run_dispatch←1← A201 BCS |
| JMP finalise_and_return ; Jump to finalise and return | |
| A209 | .try_library_path←1← A1DE BNE |
| LDA fs_filename_buf ; Load first char of filename | |
| A20C | CMP #&24 ; Is it '$' (root dir)? |
| A20E | BEQ error_bad_command ; Yes: no library search, error |
| A210 | LDA fs_lib_flags ; Load library flag byte |
| A213 | BMI library_tried ; Bit 7 set: library already tried |
| A215 | ROL ; Rotate bits to check library state |
| A216 | ROL ; Rotate again |
| A217 | BMI restore_filename ; Bit 7 set: restore from backup |
| A219 | BCS error_bad_command ; Carry set: bad command |
| A21B | LDX #&ff ; X=&FF: pre-increment for loop |
| A21D | .loop_find_name_end←1← A223 BNE |
| INX ; Find end of filename | |
| A21E | LDA fs_filename_buf,x ; Load filename byte |
| A221 | CMP #&0d ; Is it CR (end)? |
| A223 | BNE loop_find_name_end ; No: continue scanning |
| A225 | .loop_shift_name_right←1← A22C BPL |
| LDA fs_filename_buf,x ; Shift filename right by 8 bytes | |
| A228 | STA fs_filename_backup,x ; Store shifted byte |
| A22B | DEX ; Previous byte |
| A22C | BPL loop_shift_name_right ; Loop until all shifted |
| A22E | LDX #7 ; X=7: 'Library.' is 8 bytes |
| A230 | .loop_copy_lib_prefix←1← A237 BPL |
| LDA library_dir_prefix,x ; Copy 'Library.' prefix | |
| A233 | STA fs_filename_buf,x ; Store prefix byte |
| A236 | DEX ; Previous byte |
| A237 | BPL loop_copy_lib_prefix ; Loop until prefix copied |
| A239 | LDA fs_lib_flags ; Load library flag |
| A23C | ORA #&60 ; Set bits 5-6: library path active |
| A23E | STA fs_lib_flags ; Store updated flag |
| A241 | .retry_with_library←1← A258 BNE |
| BNE open_file_for_run ; Retry file open with library path | |
| A243 | .restore_filename←1← A217 BMI |
| LDX #&ff ; X=&FF: pre-increment for loop | |
| A245 | .loop_restore_name←1← A24E BNE |
| INX ; Restore original filename | |
| A246 | LDA fs_filename_backup,x ; Load backup byte |
| A249 | STA fs_filename_buf,x ; Store to filename buffer |
| A24C | EOR #&0d ; Is it CR (end)? |
| A24E | BNE loop_restore_name ; No: continue restoring |
| A250 | JSR mask_owner_access ; Set owner-only access mask |
| A253 | ORA #&80 ; Set bit 7: library tried |
| A255 | STA fs_lib_flags ; Store updated flag |
| A258 | BNE retry_with_library ; ALWAYS branch |
| A25A | .library_tried←1← A213 BMI |
| JSR mask_owner_access ; Set owner-only access mask | |
| A25D | .error_bad_command←3← A20E BEQ← A219 BCS← B332 JMP |
| LDA #&fe ; Error code &FE | |
| A25F | JSR error_bad_inline ; Generate 'Bad command' error |
| A262 | EQUS "command." |
| A26A | .check_exec_addr←1← A1E7 JMP |
| LDX #3 ; X=3: check 4 execution bytes | |
| A26C | .loop_check_exec_bytes←1← A272 BNE |
| INC fs_func_code,x ; Increment execution address byte | |
| A26F | BNE setup_oscli_arg ; Non-zero: valid, go to OSCLI |
| A271 | DEX ; Try next byte |
| A272 | BNE loop_check_exec_bytes ; Loop until all checked |
| A274 | LDA #&93 ; Error code &93 |
| A276 | JSR error_inline_log ; Generate 'No!' error |
| A279 | EQUS "No!." |
| A27D | .alloc_run_channel←1← A203 JMP |
| LDA fs_cmd_data ; Load open mode result | |
| A280 | JSR alloc_fcb_slot ; Allocate FCB slot |
| A283 | TAY ; Transfer to Y |
| A284 | LDA #0 ; A=0: clear channel status |
| A286 | STA chan_status,x ; Clear status in channel table |
| A289 | STY cur_dir_handle ; Store handle in l1070 |
| A28C | LDY #3 ; Y=3: OSCLI execution |
| A28E | JMP boot_cmd_oscli ; Execute via boot/OSCLI path |
| A291 | .library_dir_prefix←1← A230 LDA |
| EQUS "Library." | |
| A299 | .setup_oscli_arg←1← A26F BNE |
| JSR copy_arg_to_buf_x0 ; Copy command argument to buffer | |
| A29C | LDY #0 ; Y=0 |
| A29E | CLC ; C=0 for GSINIT |
| A29F | JSR gsinit ; Initialise GS string read |
| A2A2 | .loop_read_gs_string←1← A2A5 BCC |
| JSR gsread ; Read next GS character | |
| A2A5 | BCC loop_read_gs_string ; C clear: more chars |
| A2A7 | DEY ; Back up one position |
| A2A8 | .loop_skip_trailing←1← A2AD BEQ |
| INY ; Skip trailing spaces | |
| A2A9 | LDA (os_text_ptr),y ; Load next char |
| A2AB | CMP #&20 ; Is it space? |
| A2AD | BEQ loop_skip_trailing ; Yes: skip it |
| A2AF | EOR #&0d ; Check for CR (end of line) |
| A2B1 | CLC ; C=0 for addition |
| A2B2 | TYA ; Transfer Y offset to A |
| A2B3 | ADC os_text_ptr ; Add to text pointer low |
| A2B5 | STA fs_cmd_context ; Store as command tail pointer low |
| A2B8 | LDA os_text_ptr_hi ; Load text pointer high |
| A2BA | ADC #0 ; Add carry |
| A2BC | STA fs_context_hi ; Store as command tail pointer high |
| A2BF | JSR save_text_ptr ; Save text pointer for later |
| A2C2 | LDX #&0e ; X=&0E: OSWORD parameter offset |
| A2C4 | STX fs_block_offset ; Store as block offset high |
| A2C6 | LDA #&0e ; A=&0E: OSWORD parameter size |
| A2C8 | STA fs_options ; Store as options pointer |
| A2CA | STA l0e14 ; Store to l0e16 |
| A2CD | LDX #&4a ; X=&4A: FS command table offset |
| A2CF | LDY #5 ; Y=5 |
| A2D1 | JSR do_fs_cmd_iteration ; Execute FS command iteration |
| A2D4 | LDA tube_present ; Load tube flag |
| A2D7 | BEQ dispatch_via_vector ; Zero: no tube transfer needed |
| A2D9 | AND fs_load_upper ; AND with l0f0b |
| A2DC | AND fs_addr_check ; AND with l0f0c |
| A2DF | CMP #&ff ; All &FF? |
| A2E1 | BEQ dispatch_via_vector ; Yes: no tube transfer needed |
| A2E3 | JSR tube_claim_c3 ; Claim tube for data transfer |
| A2E6 | LDX #9 ; X=9: parameter count |
| A2E8 | LDY #&0f ; Y=&0F: parameter offset |
| A2EA | LDA #4 ; A=4: tube transfer type |
| A2EC | JMP tube_addr_data_dispatch ; Dispatch tube address/data |
| A2EF | .dispatch_via_vector←2← A2D7 BEQ← A2E1 BEQ |
| LDA #1 ; A=1 | |
| A2F1 | JMP (fs_load_vector) ; Dispatch via indirect vector |
| A2F4 | .fsreply_3_set_csd←1← 945F JMP |
| JSR find_station_bit3 ; Find station with bit 3 set | |
| A2F7 | JMP return_with_last_flag ; Return with last flag state |
| A2FA | .fsreply_5_set_lib |
| JSR flip_set_station_boot ; Flip/set station boot config | |
| A2FD | 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. |
|
| A300 | .find_station_bit2←1← A39F JSR |
| LDX #&10 ; X=&10: scan 16 slots (15 to 0) | |
| A302 | CLV ; Clear V |
| A303 | .loop_search_stn_bit2←2← A309 BNE← A310 BEQ |
| DEX ; Try next slot | |
| A304 | BMI done_search_bit2 ; All slots checked: not found |
| A306 | JSR match_station_net ; Compare station/network |
| A309 | BNE loop_search_stn_bit2 ; No match: try next |
| A30B | LDA chan_status,x ; Load slot status byte |
| A30E | AND #4 ; Test bit 2 (PS active flag)? |
| A310 | BEQ loop_search_stn_bit2 ; Not set: try next |
| A312 | TYA ; Transfer Y to A |
| A313 | STA fcb_net_or_port,x ; Store Y in slot data |
| A316 | BIT bit_test_ff ; Set V (found match) |
| A319 | .done_search_bit2←1← A304 BMI |
| STY fs_urd_handle ; Store Y to l0e02 | |
| A31C | BVS set_flags_bit2 ; V set: found, skip allocation |
| A31E | TYA ; Transfer Y to A |
| A31F | JSR alloc_fcb_slot ; Allocate FCB slot |
| A322 | STA handle_1_fcb ; Store allocation result |
| A325 | BEQ jmp_restore_fs_ctx ; Zero: failed, restore context |
| A327 | .set_flags_bit2←1← A31C BVS |
| LDA #&26 ; A=&26: station flags value | |
| A329 | 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. |
|
| A32B | .find_station_bit3←3← A2F4 JSR← A35D JSR← A3A5 JSR |
| LDX #&10 ; X=&10: scan 16 slots (15 to 0) | |
| A32D | CLV ; Clear V |
| A32E | .loop_search_stn_bit3←2← A334 BNE← A33B BEQ |
| DEX ; Try next slot | |
| A32F | BMI done_search_bit3 ; All slots checked: not found |
| A331 | JSR match_station_net ; Compare station/network |
| A334 | BNE loop_search_stn_bit3 ; No match: try next |
| A336 | LDA chan_status,x ; Load slot status byte |
| A339 | AND #8 ; Test bit 3 (FS active flag)? |
| A33B | BEQ loop_search_stn_bit3 ; Not set: try next |
| A33D | TYA ; Transfer Y to A |
| A33E | STA fcb_net_or_port,x ; Store Y in slot data |
| A341 | BIT bit_test_ff ; Set V (found match) |
| A344 | .done_search_bit3←1← A32F BMI |
| STY fs_csd_handle ; Store Y to l0e03 | |
| A347 | BVS set_flags_bit3 ; V set: found, skip allocation |
| A349 | TYA ; Transfer Y to A |
| A34A | JSR alloc_fcb_slot ; Allocate FCB slot |
| A34D | STA handle_2_fcb ; Store allocation result |
| A350 | BEQ jmp_restore_fs_ctx ; Zero: failed, restore context |
| A352 | .set_flags_bit3←1← A347 BVS |
| LDA #&2a ; A=&2A: station flags value | |
| A354 | 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.
|
||||
| A356 | .cmd_flip | |||
| LDA fs_csd_handle ; Load current CSD handle | ||||
| A359 | PHA ; Save CSD handle | |||
| A35A | LDY fs_lib_handle ; Load library handle into Y | |||
| A35D | JSR find_station_bit3 ; Install library as new CSD | |||
| A360 | PLA ; Restore original CSD handle | |||
| A361 | 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. |
|
| A362 | .flip_set_station_boot←2← A2FA JSR← A3AB JSR |
| LDX #&10 ; X=&10: max 16 station entries | |
| A364 | CLV ; Clear V (no match found yet) |
| A365 | .loop_search_stn_boot←2← A36B BNE← A372 BEQ |
| DEX ; Decrement station index | |
| A366 | BMI done_search_boot ; All searched: exit loop |
| A368 | JSR match_station_net ; Check if station[X] matches |
| A36B | BNE loop_search_stn_boot ; No match: try next station |
| A36D | LDA chan_status,x ; Load station flags byte |
| A370 | AND #&10 ; Test bit 4 (active flag) |
| A372 | BEQ loop_search_stn_boot ; Not active: try next station |
| A374 | TYA ; Transfer boot type to A |
| A375 | STA fcb_net_or_port,x ; Store boot setting for station |
| A378 | BIT bit_test_ff ; Set V flag (station match found) |
| A37B | .done_search_boot←1← A366 BMI |
| STY fs_lib_handle ; Store boot type | |
| A37E | BVS set_flags_boot ; V set (matched): skip allocation |
| A380 | TYA ; Boot type to A |
| A381 | JSR alloc_fcb_slot ; Allocate FCB slot for new entry |
| A384 | STA handle_3_fcb ; Store allocation result |
| A387 | BEQ jmp_restore_fs_ctx ; Zero: allocation failed, exit |
| A389 | .set_flags_boot←1← A37E BVS |
| LDA #&32 ; A=&32: station flags (active+boot) | |
| A38B | .store_stn_flags_restore←2← A329 BNE← A354 BNE |
| STA chan_status,x ; Store station flags | |
| A38E | .jmp_restore_fs_ctx←3← A325 BEQ← A350 BEQ← A387 BEQ |
| JMP restore_fs_context ; Restore FS context and return | |
| A391 | .fsreply_1_copy_handles_boot |
| JSR close_all_net_chans ; Close all network channels | |
| A394 | SEC ; Set carry flag |
| A395 | LDA fs_reply_cmd ; Load reply boot type |
| A398 | STA fs_boot_option ; Store as current boot type |
| A39B | .fsreply_2_copy_handles |
| PHP ; Save processor status | |
| A39C | LDY fs_cmd_data ; Load station number from reply |
| A39F | JSR find_station_bit2 ; Find station entry with bit 2 |
| A3A2 | LDY fs_func_code ; Load network number from reply |
| A3A5 | JSR find_station_bit3 ; Find station entry with bit 3 |
| A3A8 | LDY fs_data_count ; Load boot type from reply |
| A3AB | JSR flip_set_station_boot ; Set boot config for station |
| A3AE | PLP ; Restore processor status |
| A3AF | BCS check_auto_boot_flag ; Carry set: proceed with boot |
| A3B1 | JMP return_with_last_flag ; Return with last flag |
| A3B4 | .check_auto_boot_flag←1← A3AF BCS |
| LDA fs_lib_flags ; Load config flags | |
| A3B7 | TAX ; Save copy in X |
| A3B8 | AND #4 ; Test bit 2 (auto-boot flag) |
| A3BA | PHP ; Save bit 2 test result |
| A3BB | TXA ; Restore full flags |
| A3BC | AND #&fb ; Clear bit 2 (consume flag) |
| A3BE | STA fs_lib_flags ; Store cleared flags |
| A3C1 | PLP ; Restore bit 2 test result |
| A3C2 | BNE load_boot_type ; Bit 2 was set: skip to boot cmd |
| A3C4 | LDA #osbyte_scan_keyboard ; OSBYTE &79: scan keyboard |
| A3C6 | LDX #(255 - inkey_key_ctrl) EOR 128 ; X=internal key number EOR 128 |
| A3C8 | JSR osbyte ; Test for 'CTRL' key pressed (X=129) |
| A3CB | TXA ; X has top bit set if 'CTRL' pressed |
| A3CC | BPL load_boot_type ; CTRL not pressed: proceed to boot |
| A3CE | .boot_load_cmd←1← A3E6 BEQ |
| RTS ; CTRL pressed: cancel boot, return | |
| A3CF | EQUS "L.!BOOT" |
| A3D6 | EQUB &0D |
| A3D7 | .boot_exec_cmd |
| EQUS "E.!BOOT" | |
| A3DE | 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). | |
| A3DF | .boot_oscli_lo_table←1← A3E8 LDX |
| EQUB &DE | |
| A3E0 | EQUB &CF |
| A3E1 | EQUB &D1 |
| A3E2 | EQUB &D7 |
| A3E3 | .load_boot_type←2← A3C2 BNE← A3CC BPL |
| LDY fs_boot_option ; Load boot type | |
| A3E6 | BEQ boot_load_cmd ; Type 0: no command, just return |
| A3E8 | .boot_cmd_oscli←1← A28E JMP |
| LDX boot_oscli_lo_table,y ; Look up boot command address low | |
| A3EB | LDY #&a3 ; Boot command address high (&A3xx) |
| A3ED | 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 | |
| A3F0 | .cmd_table_fs←12← 8BAE LDA← 8BBD LDA← 8BC5 LDA← 8BD2 LDA← 8C00 LDA← 8C07 LDA← 932A LDA← 9332 LDA← A145 LDA← A14A LDA← A15A LDA← A191 LDA |
| EQUS "C" ; *Close (first char) | |
| A3F1 | .cmd_table_fs_lo←3← 8C93 LDA← A13B LDA← B326 ORA |
| EQUS "l" ; *Close cont (dispatch lo base) | |
| A3F2 | .cmd_table_fs_hi←3← 8C8F LDA← A137 LDA← B353 AND |
| EQUS "ose" ; *Close cont (dispatch hi base) | |
| A3F5 | EQUB &80 ; No syntax |
| A3F6 | EQUW cmd_close-1 |
| A3F8 | EQUS "Dump" ; *Dump |
| A3FC | EQUB &C4 |
| A3FD | EQUW cmd_dump-1 |
| A3FF | EQUS "Net" ; *Net (select NFS) |
| A402 | EQUB &80 ; No syntax |
| A403 | EQUW cmd_net_fs-1 |
| A405 | EQUS "Pollps" ; *Pollps |
| A40B | EQUB &88 ; Syn 8: (<stn. id.>|<ps type>) |
| A40C | EQUW cmd_pollps-1 |
| A40E | EQUS "Print" ; *Print |
| A413 | EQUB &CC ; V no arg; syn 12: <filename> |
| A414 | EQUW cmd_print-1 |
| A416 | EQUS "Prot" ; *Prot |
| A41A | EQUB &8E ; Syn 14: (attribute keywords) |
| A41B | EQUW cmd_prot-1 |
| A41D | EQUS "PS" ; *PS; syn 8: (<stn. id.>|<ps type>) |
| A41F | EQUB &88 |
| A420 | EQUW cmd_ps-1 |
| A422 | EQUS "Roff" ; *Roff |
| A426 | EQUB &80 ; No syntax |
| A427 | EQUW cmd_roff-1 |
| A429 | EQUS "Type" ; *Type |
| A42D | EQUB &CC ; V no arg; syn 12: <filename> |
| A42E | EQUW cmd_type-1 |
| A430 | EQUS "Unprot" ; *Unprot |
| A436 | EQUB &8E ; Syn 14: (attribute keywords) |
| A437 | EQUW cmd_unprot-1 |
| A439 | EQUB &80 ; End of utility sub-table |
| A43A | .cmd_table_nfs |
| EQUS "Access" ; *Access | |
| A440 | EQUB &C9 ; V no arg; syn 9: <obj> (L)(W)(R)... |
| A441 | EQUW cmd_fs_operation-1 |
| A443 | EQUS "Bye" ; *Bye |
| A446 | EQUB &80 ; No syntax |
| A447 | EQUW cmd_bye-1 |
| A449 | EQUS "Cdir" ; *Cdir |
| A44D | EQUB &C6 ; V no arg; syn 6: <dir> (<number>) |
| A44E | EQUW cmd_cdir-1 |
| A450 | EQUS "Delete" ; *Delete |
| A456 | EQUB &C3 ; V no arg; syn 3: <object> |
| A457 | EQUW cmd_fs_operation-1 |
| A459 | EQUS "Dir" ; *Dir |
| A45C | EQUB &81 ; Syn 1: (<dir>) |
| A45D | EQUW cmd_dir-1 |
| A45F | EQUS "Ex" ; *Ex; syn 1: (<dir>) |
| A461 | EQUB &81 |
| A462 | EQUW cmd_ex-1 |
| A464 | EQUS "Flip" ; *Flip |
| A468 | EQUB &80 ; No syntax |
| A469 | EQUW cmd_flip-1 |
| A46B | EQUS "FS" ; *FS; syn 11: (<stn. id.>) |
| A46D | EQUB &8B |
| A46E | EQUW cmd_fs-1 |
| A470 | EQUS "Info" ; *Info |
| A474 | EQUB &C3 ; V no arg; syn 3: <object> |
| A475 | EQUW cmd_fs_operation-1 |
| A477 | .cmd_table_nfs_iam←1← 8DAF LDA |
| EQUS "I am" ; *I am | |
| A47B | EQUB &C2 ; V no arg; syn 2: (<stn>) <user>... |
| A47C | EQUW cmd_iam-1 |
| A47E | EQUS "Lcat" ; *Lcat |
| A482 | EQUB &81 ; Syn 1: (<dir>) |
| A483 | EQUW cmd_lcat-1 |
| A485 | EQUS "Lex" ; *Lex |
| A488 | EQUB &81 ; Syn 1: (<dir>) |
| A489 | EQUW cmd_lex-1 |
| A48B | EQUS "Lib" ; *Lib |
| A48E | EQUB &C5 ; V no arg; syn 5: <dir> |
| A48F | EQUW cmd_fs_operation-1 |
| A491 | EQUS "Pass" ; *Pass |
| A495 | EQUB &C7 ; V no arg; syn 7: <pass> ... |
| A496 | EQUW cmd_pass-1 |
| A498 | EQUS "Remove" ; *Remove |
| A49E | EQUB &C3 |
| A49F | EQUW cmd_remove-1 |
| A4A1 | EQUS "Rename" ; *Rename V no arg; syn 3: <object> |
| A4A7 | EQUB &CA ; V no arg; syn 10: <file> <new file> |
| A4A8 | EQUW cmd_rename-1 |
| A4AA | EQUS "Wipe" ; *Wipe |
| A4AE | EQUB &81 |
| A4AF | EQUW cmd_wipe-1 |
| A4B1 | EQUB &80 ; End of NFS sub-table |
| A4B2 | .cmd_table_help |
| EQUB &14, &8E | |
| A4B4 | EQUS "Net" ; *Net (local) |
| A4B7 | EQUB &80 ; No syntax |
| A4B8 | EQUW help_net-1 |
| A4BA | EQUS "Utils" ; *Utils |
| A4BF | EQUB &80 ; No syntax |
| A4C0 | EQUW help_utils-1 |
| A4C2 | 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 | |
| A4C3 | .cmd_table_copro |
| EQUS "Halt" ; Halt | |
| A4C7 | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4C8 | EQUB &20 ; *Prot OR mask: bit 5 |
| A4C9 | EQUB &DF ; *Unprot AND mask: ~bit 5 |
| A4CA | EQUS "JSR" ; JSR |
| A4CD | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4CE | EQUB &04 ; *Prot OR mask: bit 2 |
| A4CF | EQUB &FB ; *Unprot AND mask: ~bit 2 |
| A4D0 | EQUS "Peek" ; Peek |
| A4D4 | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4D5 | EQUB &01 ; *Prot OR mask: bit 0 |
| A4D6 | EQUB &FE ; *Unprot AND mask: ~bit 0 |
| A4D7 | EQUS "Poke" ; Poke |
| A4DB | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4DC | EQUB &02 ; *Prot OR mask: bit 1 |
| A4DD | EQUB &FD ; *Unprot AND mask: ~bit 1 |
| A4DE | EQUS "Proc" ; Proc |
| A4E2 | EQUB &FC ; Flag &FC: V no arg, syn 28 (unused) |
| A4E3 | EQUB &08 ; *Prot OR mask: bit 3 |
| A4E4 | EQUB &F7 ; *Unprot AND mask: ~bit 3 |
| A4E5 | EQUS "Utils" ; Utils |
| A4EA | EQUB &A9 ; Flag &A9: syn 9 (unused) |
| A4EB | EQUB &10 ; *Prot OR mask: bit 4 |
| A4EC | EQUB &EF ; *Unprot AND mask: ~bit 4 |
| A4ED | 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. |
|
| A4EE | .svc_8_osword |
| CLC ; CLC so SBC subtracts value+1 | |
| A4EF | LDA osbyte_a_copy ; A = OSWORD number |
| A4F1 | SBC #&0d ; A = OSWORD - &0E (CLC+SBC = -&0E) |
| A4F3 | BMI return_from_osword_setup ; Below &0E: not ours, return |
| A4F5 | CMP #7 ; Index >= 7? (OSWORD > &14) |
| A4F7 | BCS return_from_osword_setup ; Above &14: not ours, return |
| A4F9 | TAX ; X=OSWORD handler index (0-6) |
| A4FA | LDY #6 ; Y=6: save 6 workspace bytes |
| A4FC | .loop_ca4fc←1← A507 BNE |
| LDA svc_state,y ; Load current workspace byte | |
| A4FF | PHA ; Save on stack |
| A500 | LDA l00ed,y ; Load OSWORD parameter byte |
| A503 | STA svc_state,y ; Copy parameter to workspace |
| A506 | DEY ; Next byte down |
| A507 | BNE loop_ca4fc ; Loop for all 6 bytes |
| A509 | JSR osword_setup_handler ; Set up dispatch and save state |
| A50C | LDY #&fa ; Y=&FA: restore 6 workspace bytes |
| A50E | .loop_ca50e←1← A513 BNE |
| PLA ; Restore saved workspace byte | |
| A50F | STA lffb0,y ; Store to osword_flag workspace |
| A512 | INY ; Next byte |
| A513 | BNE loop_ca50e ; Loop until all 6 restored |
| A515 | 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.
|
||||
| A516 | .osword_setup_handler←1← A509 JSR | |||
| LDA osword_dispatch_hi_table,x ; X = OSWORD index (0-6) Load handler address high byte | ||||
| A519 | PHA ; Push high byte for RTS dispatch | |||
| A51A | LDA osword_dispatch_lo_table,x ; Load handler address low byte | |||
| A51D | PHA ; Push low byte for RTS dispatch | |||
| A51E | LDA (ws_ptr_hi),y ; Copy 3 bytes (Y=2,1,0) Load PB byte 0 (OSWORD sub-code) | |||
| A520 | STY svc_state ; Load from osword_flag workspace Clear service state | |||
| A522 | .return_from_osword_setup←2← A4F3 BMI← A4F7 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. | ||||
| A523 | .osword_dispatch_lo_table←1← A51A LDA | |||
| EQUB <(osword_0e_handler-1) ; lo-&0E: Read clock lo-&13: Misc operations Store to RX buffer | ||||
| A524 | EQUB <(return_from_osword_setup-1) | |||
| A525 | EQUB <(osword_10_handler-1) | |||
| A526 | EQUB <(osword_11_handler-1) | |||
| A527 | EQUB <(osword_12_handler-1) | |||
| A528 | EQUB <(osword_13_dispatch-1) | |||
| A529 | EQUB <(osword_14_handler-1) | |||
| A52A | .osword_dispatch_hi_table←1← A516 LDA | |||
| EQUB >(osword_0e_handler-1) | ||||
| A52B | EQUB >(return_from_osword_setup-1) ; hi-&0E: Read clock | |||
| A52C | EQUB >(osword_10_handler-1) ; hi-&0F: (unimplemented) | |||
| A52D | EQUB >(osword_11_handler-1) ; hi-&10: Transmit | |||
| A52E | EQUB >(osword_12_handler-1) | |||
| A52F | EQUB >(osword_13_dispatch-1) ; hi-&12: Read station info | |||
| A530 | EQUB >(osword_14_handler-1) | |||
| A531 | .osword_0e_handler | |||
| BIT fs_flags ; hi-&14: Bridge/net config Test station active flag | ||||
| A534 | BPL return_from_osword_0e ; Not active: just return | |||
| A536 | CMP #4 ; Restore A (OSWORD sub-code) Sub-code = 4? (read clock) | |||
| A538 | BEQ save_txcb_and_convert ; Yes: handle clock read | |||
| A53A | LDA #8 ; Other sub-codes: set state = 8 | |||
| A53C | STA svc_state ; Store service state | |||
| A53E | .return_from_osword_0e←1← A534 BPL | |||
| RTS ; Return | ||||
| A53F | .save_txcb_and_convert←1← A538 BEQ | |||
| LDX #0 ; X=0: start of TX control block | ||||
| A541 | LDY #&10 ; Y=&10: length of TXCB to save | |||
| A543 | JSR save_net_tx_cb ; Save current TX control block | |||
| A546 | LDA fs_load_vector ; Load seconds from clock workspace | |||
| A549 | JSR bin_to_bcd ; Convert binary to BCD | |||
| A54C | STA fs_load_upper ; Store BCD seconds | |||
| A54F | LDA fs_reply_cmd ; Load minutes from clock workspace | |||
| A552 | JSR bin_to_bcd ; Convert binary to BCD | |||
| A555 | STA fs_handle_check ; Store BCD minutes | |||
| A558 | LDA fs_data_count ; Load hours from clock workspace | |||
| A55B | JSR bin_to_bcd ; Convert binary to BCD | |||
| A55E | STA fs_load_vector ; Store BCD hours | |||
| A561 | LDA #0 ; Clear hours high position | |||
| A563 | STA fs_reply_cmd ; Store zero | |||
| A566 | LDA fs_func_code ; Load day+month byte | |||
| A569 | PHA ; Save for later high nibble extract | |||
| A56A | LDA fs_cmd_data ; Load day value | |||
| A56D | JSR bin_to_bcd ; Convert day to BCD | |||
| A570 | STA fs_data_count ; Store BCD day | |||
| A573 | PLA ; Restore day+month byte | |||
| A574 | PHA ; Save again for month extract | |||
| A575 | AND #&0f ; Mask low nibble (month low bits) | |||
| A577 | JSR bin_to_bcd ; Convert to BCD | |||
| A57A | STA fs_func_code ; Store BCD month | |||
| A57D | PLA ; Restore day+month byte | |||
| A57E | LSR ; Shift high nibble down | |||
| A57F | LSR ; Continue shifting | |||
| A580 | LSR ; Continue shifting | |||
| A581 | LSR ; 4th shift: isolate high nibble | |||
| A582 | ADC #&51 ; Add &51 for year offset + carry | |||
| A584 | JSR bin_to_bcd ; Convert year to BCD | |||
| A587 | STA fs_cmd_data ; Store BCD year | |||
| A58A | LDY #6 ; Copy 7 bytes (Y=6 down to 0) | |||
| A58C | .loop_copy_bcd_to_pb←1← A592 BPL | |||
| LDA fs_cmd_data,y ; Load BCD byte from workspace | ||||
| A58F | STA (ws_ptr_hi),y ; Store to parameter block | |||
| A591 | DEY ; Next byte down | |||
| A592 | BPL loop_copy_bcd_to_pb ; Loop for all 7 bytes | |||
| A594 | 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.
|
|||||||
| A595 | .bin_to_bcd←6← A549 JSR← A552 JSR← A55B JSR← A56D JSR← A577 JSR← A584 JSR | ||||||
| PHP ; Save processor flags (decimal mode) | |||||||
| A596 | TAX ; X = binary count | ||||||
| A597 | BEQ done_bcd_convert ; Zero: result is 0, skip loop | ||||||
| A599 | SED ; Set decimal mode for BCD add | ||||||
| A59A | LDA #0 ; Start BCD result at 0 | ||||||
| A59C | .loop_bcd_add←1← A5A0 BNE | ||||||
| CLC ; Clear carry for BCD add | |||||||
| A59D | ADC #1 ; Add 1 in decimal mode | ||||||
| A59F | DEX ; Count down binary value | ||||||
| A5A0 | BNE loop_bcd_add ; Loop until zero | ||||||
| A5A2 | .done_bcd_convert←1← A597 BEQ | ||||||
| PLP ; Restore flags (clears decimal mode) | |||||||
| A5A3 | RTS ; Return with BCD result in A | ||||||
| A5A4 | .osword_10_handler | ||||||
| ASL ws_0d60 ; Shift ws_0d60 left (status flag) | |||||||
| A5A7 | TYA ; A = Y (saved index) | ||||||
| A5A8 | BCS setup_ws_rx_ptrs ; C=1: transmit active path | ||||||
| A5AA | STA (ws_ptr_hi),y ; C=0: store Y to parameter block | ||||||
| A5AC | RTS ; Return (transmit not active) | ||||||
| A5AD | .setup_ws_rx_ptrs←1← A5A8 BCS | ||||||
| LDA net_rx_ptr_hi ; Set workspace high byte | |||||||
| A5AF | STA ws_ptr_lo ; Copy to ws_ptr_lo | ||||||
| A5B1 | STA nmi_tx_block_hi ; Also set as NMI TX block high | ||||||
| A5B3 | LDA #&6f ; Low byte = &6F | ||||||
| A5B5 | STA osword_flag ; Set osword_flag | ||||||
| A5B7 | STA nmi_tx_block ; Set NMI TX block low | ||||||
| A5B9 | LDX #&0f ; X=&0F: byte count for copy | ||||||
| A5BB | JSR copy_pb_byte_to_ws ; Copy data and begin transmission | ||||||
| A5BE | JMP tx_begin ; Jump to begin Econet transmission | ||||||
| A5C1 | .osword_11_handler | ||||||
| LDX nfs_workspace_hi ; Load NFS workspace page high byte | |||||||
| A5C3 | STX ws_ptr_lo ; Set workspace pointer high | ||||||
| A5C5 | STY osword_flag ; Set workspace pointer low from Y | ||||||
| A5C7 | ROR econet_flags ; Rotate Econet flags (save interrupt state) | ||||||
| A5CA | TAY ; Y=OSWORD flag (slot specifier) | ||||||
| A5CB | STA work_ae ; Store OSWORD flag | ||||||
| A5CD | BNE use_specified_slot ; Non-zero: use specified slot | ||||||
| A5CF | LDA #3 ; A=3: start searching from slot 3 | ||||||
| A5D1 | .loop_find_rx_slot←1← A5E3 BNE | ||||||
| JSR byte_to_2bit_index ; Convert slot to 2-bit workspace index | |||||||
| A5D4 | BCS store_rx_result ; C set: slot invalid, store result | ||||||
| A5D6 | LSR ; Shift index right (divide by 4) | ||||||
| A5D7 | LSR ; Continue shift | ||||||
| A5D8 | TAX ; Transfer to X as workspace offset | ||||||
| A5D9 | LDA (osword_flag),y ; Load workspace byte at offset | ||||||
| A5DB | BEQ store_rx_result ; Zero: slot empty, store result | ||||||
| A5DD | CMP #&3f ; Compare with &3F ('?' marker) | ||||||
| A5DF | BEQ store_rx_slot_found ; Match: slot found for receive | ||||||
| A5E1 | INX ; Try next slot index | ||||||
| A5E2 | TXA ; Transfer back to A | ||||||
| A5E3 | BNE loop_find_rx_slot ; Loop back (A != 0) | ||||||
| A5E5 | .store_rx_slot_found←1← A5DF BEQ | ||||||
| TXA ; Transfer found slot to A | |||||||
| A5E6 | LDX #0 ; X=0: index for indirect store | ||||||
| A5E8 | STA (ws_ptr_hi,x) ; Store slot number to PB byte 0 | ||||||
| A5EA | .use_specified_slot←1← A5CD BNE | ||||||
| JSR byte_to_2bit_index ; Convert specified slot to workspace index | |||||||
| A5ED | BCS store_rx_result ; C set: slot invalid, store result | ||||||
| A5EF | DEY ; Y=Y-1: adjust workspace offset | ||||||
| A5F0 | STY osword_flag ; Update workspace pointer low | ||||||
| A5F2 | LDA #&c0 ; A=&C0: slot active marker | ||||||
| A5F4 | LDY #1 ; Y=1: workspace byte offset | ||||||
| A5F6 | LDX #&0b ; X=&0B: byte count for PB copy | ||||||
| A5F8 | CPY work_ae ; Compare Y with OSWORD flag | ||||||
| A5FA | ADC (osword_flag),y ; Add workspace byte (check slot state) | ||||||
| A5FC | BEQ copy_pb_and_mark ; Zero: slot ready, copy PB and mark | ||||||
| A5FE | BMI increment_and_retry ; Negative: slot busy, increment and retry | ||||||
| A600 | .loop_copy_slot_data←1← A610 BNE | ||||||
| CLC ; Clear carry for PB copy | |||||||
| A601 | .copy_pb_and_mark←1← A5FC BEQ | ||||||
| JSR copy_pb_byte_to_ws ; Copy PB byte to workspace slot | |||||||
| A604 | BCS osword_11_done ; C set: copy done, finish | ||||||
| A606 | LDA #&3f ; A=&3F: mark slot as pending ('?') | ||||||
| A608 | LDY #1 ; Y=1: workspace flag offset | ||||||
| A60A | STA (osword_flag),y ; Store pending marker to workspace | ||||||
| A60C | BNE osword_11_done ; ALWAYS branch | ||||||
| A60E | .increment_and_retry←1← A5FE BMI | ||||||
| ADC #1 ; Increment retry counter | |||||||
| A610 | BNE loop_copy_slot_data ; Non-zero: retry copy loop | ||||||
| A612 | DEY ; Decrement Y (adjust offset) | ||||||
| A613 | .store_rx_result←3← A5D4 BCS← A5DB BEQ← A5ED BCS | ||||||
| STA (ws_ptr_hi),y ; Store result A to PB via Y | |||||||
| A615 | .osword_11_done←2← A604 BCS← A60C BNE | ||||||
| ROL econet_flags ; Rotate Econet flags back (restore state) | |||||||
| A618 | RTS ; Return from OSWORD 11 handler | ||||||
| A619 | .osword_12_handler | ||||||
| LDA net_rx_ptr_hi ; Set workspace from RX ptr high | |||||||
| A61B | STA ws_ptr_lo ; Store to ws_ptr_lo | ||||||
| A61D | LDY #&7f ; Y=&7F: last byte of RX buffer | ||||||
| A61F | LDA (net_rx_ptr),y ; Load port/count from RX buffer | ||||||
| A621 | INY ; Y=&80: set workspace pointer Y=&80 | ||||||
| A622 | STY osword_flag ; Store as osword_flag | ||||||
| A624 | TAX ; X = port/count value | ||||||
| A625 | DEX ; X-1: adjust count | ||||||
| A626 | LDY #0 ; Y=0 for copy | ||||||
| A628 | JSR copy_pb_byte_to_ws ; Copy workspace data | ||||||
| A62B | JMP commit_state_byte ; Update state and return | ||||||
| A62E | .osword_13_dispatch | ||||||
| TAX ; X = sub-code | |||||||
| A62F | CMP #&13 ; Sub-code < &13? | ||||||
| A631 | BCS return_from_osword_13 ; Out of range: return | ||||||
| A633 | LDA osword_13_hi_table,x ; Load handler address high byte | ||||||
| A636 | PHA ; Push high byte | ||||||
| A637 | LDA osword_13_lo_table,x ; Load handler address low byte | ||||||
| A63A | PHA ; Push low byte | ||||||
| A63B | .return_from_osword_13←1← A631 BCS | ||||||
| RTS ; RTS dispatches to handler | |||||||
| A63C | .osword_13_lo_table←1← A637 LDA | ||||||
| EQUB <(osword_13_read_station-1) | |||||||
| A63D | EQUB <(osword_13_set_station-1) | ||||||
| A63E | EQUB <(osword_13_read_ws_pair-1) | ||||||
| A63F | EQUB <(osword_13_write_ws_pair-1) | ||||||
| A640 | EQUB <(osword_13_read_prot-1) | ||||||
| A641 | EQUB <(osword_13_write_prot-1) | ||||||
| A642 | EQUB <(osword_13_read_handles-1) | ||||||
| A643 | EQUB <(osword_13_set_handles-1) | ||||||
| A644 | EQUB <(osword_13_read_rx_flag-1) | ||||||
| A645 | EQUB <(osword_13_read_rx_port-1) | ||||||
| A646 | EQUB <(osword_13_read_error-1) | ||||||
| A647 | EQUB <(osword_13_read_context-1) | ||||||
| A648 | EQUB <(osword_13_read_csd-1) | ||||||
| A649 | EQUB <(osword_13_write_csd-1) | ||||||
| A64A | EQUB <(osword_13_read_free_bufs-1) | ||||||
| A64B | EQUB <(osword_13_read_ctx_3-1) | ||||||
| A64C | EQUB <(osword_13_write_ctx_3-1) | ||||||
| A64D | EQUB <(osword_13_bridge_query-1) | ||||||
| A64E | .osword_13_hi_table←1← A633 LDA | ||||||
| EQUB >(osword_13_read_station-1) ; hi-sub 0: read FS station | |||||||
| A64F | EQUB >(osword_13_set_station-1) ; hi-sub 1: set FS station | ||||||
| A650 | EQUB >(osword_13_read_ws_pair-1) ; hi-sub 2: read workspace pair | ||||||
| A651 | EQUB >(osword_13_write_ws_pair-1) ; hi-sub 3: write workspace pair | ||||||
| A652 | EQUB >(osword_13_read_prot-1) ; hi-sub 4: read protection mask | ||||||
| A653 | EQUB >(osword_13_write_prot-1) ; hi-sub 5: write protection mask | ||||||
| A654 | EQUB >(osword_13_read_handles-1) ; hi-sub 6: read FCB handles | ||||||
| A655 | EQUB >(osword_13_set_handles-1) ; hi-sub 7: set FCB handles | ||||||
| A656 | EQUB >(osword_13_read_rx_flag-1) ; hi-sub 8: read RX flag | ||||||
| A657 | EQUB >(osword_13_read_rx_port-1) ; hi-sub 9: read RX port | ||||||
| A658 | EQUB >(osword_13_read_error-1) ; hi-sub 10: read error flag | ||||||
| A659 | EQUB >(osword_13_read_context-1) ; hi-sub 11: read context byte | ||||||
| A65A | EQUB >(osword_13_read_csd-1) ; hi-sub 12: read CSD path | ||||||
| A65B | EQUB >(osword_13_write_csd-1) ; hi-sub 13: write CSD path | ||||||
| A65C | EQUB >(osword_13_read_free_bufs-1) ; hi-sub 14: read free buffers | ||||||
| A65D | EQUB >(osword_13_read_ctx_3-1) ; hi-sub 15: read 3 context bytes | ||||||
| A65E | EQUB >(osword_13_write_ctx_3-1) ; hi-sub 16: write 3 context bytes | ||||||
| A65F | 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. |
|
| A660 | .osword_13_read_station |
| BIT fs_flags ; NFS active? | |
| A663 | BMI read_station_bytes ; Yes: read station data |
| A665 | .nfs_inactive_exit←1← A676 BPL |
| JMP return_zero_in_pb ; No: return zero | |
| A668 | .read_station_bytes←1← A663 BMI |
| LDY #2 ; Y=2: copy 2 bytes | |
| A66A | .loop_copy_station←1← A670 BNE |
| LDA fs_server_base,y ; Load station byte | |
| A66D | STA (osword_pb_ptr),y ; Store to PB[Y] |
| A66F | DEY ; Previous byte |
| A670 | BNE loop_copy_station ; Loop for bytes 2..1 |
| A672 | 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. |
|
| A673 | .osword_13_set_station |
| BIT fs_flags ; NFS active? | |
| A676 | BPL nfs_inactive_exit ; No: return zero |
| A678 | LDY #0 ; Y=0 for process_all_fcbs |
| A67A | JSR process_all_fcbs ; Close all open FCBs |
| A67D | LDY #2 ; Y=2: copy 2 bytes |
| A67F | .loop_store_station←1← A685 BNE |
| LDA (osword_pb_ptr),y ; Load new station byte from PB | |
| A681 | STA fs_server_base,y ; Store to l0dff |
| A684 | DEY ; Previous byte |
| A685 | BNE loop_store_station ; Loop for bytes 2..1 |
| A687 | JSR clear_if_station_match ; Clear handles if station matches |
| A68A | LDX #&0f ; X=&0F: scan 16 FCB entries |
| A68C | .scan_fcb_entry←1← A6E5 BPL |
| LDA chan_status,x ; Load FCB flags | |
| A68F | TAY ; Save flags in Y |
| A690 | AND #2 ; Test bit 1 (FCB allocated?) |
| A692 | BEQ next_fcb_entry ; No: skip to next entry |
| A694 | TYA ; Restore flags |
| A695 | AND #&df ; Clear bit 5 (pending update) |
| A697 | STA chan_status,x ; Store updated flags |
| A69A | TAY ; Save in Y |
| A69B | JSR match_station_net ; Does FCB match new station? |
| A69E | BNE next_fcb_entry ; No match: skip to next |
| A6A0 | CLC ; Clear carry for ADC |
| A6A1 | TYA ; Restore flags |
| A6A2 | AND #4 ; Test bit 2 (handle 1 active?) |
| A6A4 | BEQ check_handle_2 ; No: check handle 2 |
| A6A6 | TYA ; Restore flags |
| A6A7 | ORA #&20 ; Set bit 5 (handle reassigned) |
| A6A9 | TAY ; Save updated flags |
| A6AA | LDA fcb_net_or_port,x ; Get FCB high byte |
| A6AD | STA fs_urd_handle ; Store as handle 1 station |
| A6B0 | TXA ; FCB index |
| A6B1 | ADC #&20 ; Add &20 for FCB table offset |
| A6B3 | STA handle_1_fcb ; Store as handle 1 FCB index |
| A6B6 | .check_handle_2←1← A6A4 BEQ |
| TYA ; Restore flags | |
| A6B7 | AND #8 ; Test bit 3 (handle 2 active?) |
| A6B9 | BEQ check_handle_3 ; No: check handle 3 |
| A6BB | TYA ; Restore flags |
| A6BC | ORA #&20 ; Set bit 5 |
| A6BE | TAY ; Save updated flags |
| A6BF | LDA fcb_net_or_port,x ; Get FCB high byte |
| A6C2 | STA fs_csd_handle ; Store as handle 2 station |
| A6C5 | TXA ; FCB index |
| A6C6 | ADC #&20 ; Add &20 for FCB table offset |
| A6C8 | STA handle_2_fcb ; Store as handle 2 FCB index |
| A6CB | .check_handle_3←1← A6B9 BEQ |
| TYA ; Restore flags | |
| A6CC | AND #&10 ; Test bit 4 (handle 3 active?) |
| A6CE | BEQ store_updated_status ; No: store final flags |
| A6D0 | TYA ; Restore flags |
| A6D1 | ORA #&20 ; Set bit 5 |
| A6D3 | TAY ; Save updated flags |
| A6D4 | LDA fcb_net_or_port,x ; Get FCB high byte |
| A6D7 | STA fs_lib_handle ; Store as handle 3 station |
| A6DA | TXA ; FCB index |
| A6DB | ADC #&20 ; Add &20 for FCB table offset |
| A6DD | STA handle_3_fcb ; Store as handle 3 FCB index |
| A6E0 | .store_updated_status←1← A6CE BEQ |
| TYA ; Store final flags for this FCB | |
| A6E1 | STA chan_status,x ; Update l1060[X] |
| A6E4 | .next_fcb_entry←2← A692 BEQ← A69E BNE |
| DEX ; Next FCB entry | |
| A6E5 | BPL scan_fcb_entry ; Loop for all 16 entries |
| A6E7 | RTS ; Return |
OSWORD &13 sub 12: read CSD pathReads 5 current selected directory path bytes from the RX workspace at offset &17 into PB[1..5]. Sets carry clear to select the workspace-to-PB copy direction. |
|
| A6E8 | .osword_13_read_csd |
| CLC ; C=0: workspace-to-PB direction | |
| A6E9 | 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 &17. Sets carry to select the PB-to-workspace copy direction. |
|
| A6EB | .osword_13_write_csd |
| SEC ; C=1: PB-to-workspace direction | |
| A6EC | .setup_csd_copy←1← A6E9 BCC |
| LDA #&17 ; Workspace offset &17 | |
| A6EE | STA osword_flag ; Set ws_ptr_lo |
| A6F0 | LDA net_rx_ptr_hi ; Page from RX pointer high byte |
| A6F2 | STA ws_ptr_lo ; Set ws_ptr_hi |
| A6F4 | LDY #1 ; Y=1: first PB data byte |
| A6F6 | 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.
|
||||||||
| A6F8 | .copy_pb_byte_to_ws←5← A5BB JSR← A601 JSR← A628 JSR← A704 BPL← A711 BCC | |||||||
| BCC copy_ws_byte_to_pb ; C=0: skip PB-to-WS copy | ||||||||
| A6FA | LDA (ws_ptr_hi),y ; C=1: load from parameter block | |||||||
| A6FC | STA (osword_flag),y ; Store to workspace | |||||||
| A6FE | .copy_ws_byte_to_pb←1← A6F8 BCC | |||||||
| LDA (osword_flag),y ; Load from workspace | ||||||||
| A700 | STA (ws_ptr_hi),y ; Store to parameter block | |||||||
| A702 | INY ; Next byte | |||||||
| A703 | DEX ; Count down | |||||||
| A704 | BPL copy_pb_byte_to_ws ; Loop for all bytes | |||||||
| A706 | 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. |
|
| A707 | .osword_13_read_ws_pair |
| LDA nfs_workspace_hi ; Load workspace page high byte | |
| A709 | STA ws_ptr_lo ; Set ws_ptr_hi |
| A70B | INY ; Y=1 |
| A70C | TYA ; A=1 |
| A70D | STA osword_flag ; Set ws_ptr_lo = 1 |
| A70F | TAX ; X=1: copy 2 bytes |
| A710 | CLC ; C=0: workspace-to-PB direction |
| A711 | 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. |
|
| A713 | .osword_13_write_ws_pair |
| INY ; Y=1: first PB data byte | |
| A714 | LDA (ws_ptr_hi),y ; Load PB[1] |
| A716 | INY ; Y=2 |
| A717 | STA (nfs_workspace),y ; Store to (nfs_workspace)+2 |
| A719 | LDA (ws_ptr_hi),y ; Load PB[2] |
| A71B | INY ; Y=3 |
| A71C | STA (nfs_workspace),y ; Store to (nfs_workspace)+3 |
| A71E | JSR init_bridge_poll ; Reinitialise bridge routing |
| A721 | EOR (nfs_workspace),y ; Compare result with workspace |
| A723 | BNE return_from_write_ws_pair ; Different: leave unchanged |
| A725 | STA (nfs_workspace),y ; Same: clear workspace byte |
| A727 | .return_from_write_ws_pair←1← A723 BNE |
| RTS ; Return | |
OSWORD &13 sub 4: read protection maskReturns the current protection mask (ws_0d68) in PB[1]. |
|
| A728 | .osword_13_read_prot |
| LDA ws_0d68 ; Load protection mask | |
| A72B | 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. |
|
| A72E | .osword_13_write_prot |
| INY ; Y=1: PB data offset | |
| A72F | LDA (ws_ptr_hi),y ; Load new mask from PB[1] |
| A731 | 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]. |
|
| A734 | .osword_13_read_handles |
| BIT fs_flags ; NFS active? | |
| A737 | BPL return_zero_in_pb ; No: return zero |
| A739 | LDY #3 ; Y=3: copy 3 bytes |
| A73B | .loop_copy_handles←1← A741 BNE |
| LDA fs_lib_flags,y ; Load handle byte | |
| A73E | STA (ws_ptr_hi),y ; Store to PB[Y] |
| A740 | DEY ; Previous byte |
| A741 | BNE loop_copy_handles ; Loop for bytes 3..1 |
| A743 | 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. |
|
| A744 | .osword_13_set_handles |
| BIT fs_flags ; NFS active? | |
| A747 | BMI start_set_handles ; Yes: process handles |
| A749 | .return_zero_in_pb←2← A665 JMP← A737 BPL |
| LDA #0 ; A=0 | |
| A74B | TAY ; Y=&00 |
| A74C | STA (ws_ptr_hi),y ; Store 0 to PB[0] |
| A74E | RTS ; Return |
| A74F | .start_set_handles←1← A747 BMI |
| LDY #1 ; Y=1: first handle in PB | |
| A751 | .validate_handle←1← A791 BNE |
| LDA (ws_ptr_hi),y ; Load handle value from PB[Y] | |
| A753 | CMP #&20 ; Must be >= &20 |
| A755 | BCC handle_invalid ; Below range: invalid |
| A757 | CMP #&30 ; Must be < &30 |
| A759 | BCS handle_invalid ; Above range: invalid |
| A75B | TAX ; X = handle value |
| A75C | LDA fcb_attr_or_count_mid,x ; Load l1010[handle] |
| A75F | BNE check_handle_alloc ; Non-zero: FCB exists |
| A761 | .handle_invalid←3← A755 BCC← A759 BCS← A76D BEQ |
| LDA #0 ; Invalid: store 0 to PB[0] | |
| A763 | TAX ; X=&00 |
| A764 | STA (ws_ptr_hi,x) ; Clear PB[0] status |
| A766 | BEQ next_handle_slot ; Skip to next handle ALWAYS branch |
| A768 | .check_handle_alloc←1← A75F BNE |
| LDA fcb_flags,x ; Load l1040[handle] flags | |
| A76B | AND #2 ; Test bit 1 (allocated?) |
| A76D | BEQ handle_invalid ; Not allocated: invalid |
| A76F | TXA ; X = handle value |
| A770 | STA fs_lib_flags,y ; Store handle to l1071+Y |
| A773 | LDA fcb_attr_or_count_mid,x ; Load station from l1010 |
| A776 | STA fs_server_net,y ; Store station to l0e01+Y |
| A779 | CPY #1 ; Is this handle 1 (Y=1)? |
| A77B | BNE assign_handle_2 ; No: check handle 2 |
| A77D | TYA ; Save Y |
| A77E | PHA ; Push Y |
| A77F | LDY #4 ; Bit mask &04 for handle 1 |
| A781 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| A784 | PLA ; Restore Y |
| A785 | TAY ; Back to Y |
| A786 | LDA fcb_flags,x ; Reload l1040 flags |
| A789 | ORA #&24 ; Set bits 2+5 (active+updated) |
| A78B | STA fcb_flags,x ; Store updated flags |
| A78E | .next_handle_slot←3← A766 BEQ← A7AA BNE← A7BD BNE |
| INY ; Next handle slot | |
| A78F | CPY #4 ; Done all 3 handles? |
| A791 | BNE validate_handle ; No: process next handle |
| A793 | DEY ; Y=3 for return |
| A794 | RTS ; Return |
| A795 | .assign_handle_2←1← A77B BNE |
| CPY #2 ; Is this handle 2 (Y=2)? | |
| A797 | BNE assign_handle_3 ; No: must be handle 3 |
| A799 | TYA ; Save Y |
| A79A | PHA ; Push Y |
| A79B | LDY #8 ; Bit mask &08 for handle 2 |
| A79D | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| A7A0 | PLA ; Restore Y |
| A7A1 | TAY ; Back to Y |
| A7A2 | LDA fcb_flags,x ; Reload l1040 flags |
| A7A5 | ORA #&28 ; Set bits 3+5 (active+updated) |
| A7A7 | STA fcb_flags,x ; Store updated flags |
| A7AA | BNE next_handle_slot ; Next handle slot ALWAYS branch |
| A7AC | .assign_handle_3←1← A797 BNE |
| TYA ; Handle 3: save Y | |
| A7AD | PHA ; Push Y |
| A7AE | LDY #&10 ; Bit mask &10 for handle 3 |
| A7B0 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| A7B3 | PLA ; Restore Y |
| A7B4 | TAY ; Back to Y |
| A7B5 | LDA fcb_flags,x ; Reload l1040 flags |
| A7B8 | ORA #&30 ; Set bits 4+5 (active+updated) |
| A7BA | STA fcb_flags,x ; Store updated flags |
| A7BD | 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.
|
||||||
| A7BF | .update_fcb_flag_bits←3← A781 JSR← A79D JSR← A7B0 JSR | |||||
| TXA ; Save X (current FCB index) | ||||||
| A7C0 | PHA ; Push X | |||||
| A7C1 | LDX #&0f ; X=&0F: scan 16 FCB entries | |||||
| A7C3 | .loop_scan_fcb_flags←1← A7DF BPL | |||||
| LDA chan_status,x ; Load FCB flags | ||||||
| A7C6 | ROL ; Shift bits 6-7 into bits 7-0 | |||||
| A7C7 | ROL ; Bit 6 now in bit 7 (N flag) | |||||
| A7C8 | BPL next_flag_entry ; Bit 6 clear: skip entry | |||||
| A7CA | TYA ; Restore Y (bit mask) | |||||
| A7CB | AND chan_status,x ; Test mask bits against flags | |||||
| A7CE | BEQ no_flag_match ; Zero: no matching bits | |||||
| A7D0 | TYA ; Matching: restore Y | |||||
| A7D1 | ORA #&20 ; Set bit 5 (updated) | |||||
| A7D3 | BNE clear_flag_bits ; Skip clear path ALWAYS branch | |||||
| A7D5 | .no_flag_match←1← A7CE BEQ | |||||
| TYA ; No match: restore Y | ||||||
| A7D6 | .clear_flag_bits←1← A7D3 BNE | |||||
| EOR #&ff ; Invert all bits | ||||||
| A7D8 | AND chan_status,x ; Clear tested bits in flags | |||||
| A7DB | STA chan_status,x ; Store updated flags | |||||
| A7DE | .next_flag_entry←1← A7C8 BPL | |||||
| DEX ; Next FCB entry | ||||||
| A7DF | BPL loop_scan_fcb_flags ; Loop for all 16 entries | |||||
| A7E1 | PLA ; Restore original X | |||||
| A7E2 | TAX ; Back to X | |||||
| A7E3 | RTS ; Return | |||||
OSWORD &13 sub 8: read RX control block flagReturns byte 1 of the current RX control block in PB[1]. |
|
| A7E4 | .osword_13_read_rx_flag |
| LDY #1 ; Y=1: RX control block offset | |
| A7E6 | LDA (net_rx_ptr),y ; Load (net_rx_ptr)+1 |
| A7E8 | LDY #0 ; Y=0 |
| A7EA | 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]. |
|
| A7ED | .osword_13_read_rx_port |
| LDY #&7f ; Y=&7F: port byte offset | |
| A7EF | LDA (net_rx_ptr),y ; Load (net_rx_ptr)+&7F |
| A7F1 | LDY #1 ; Y=1 |
| A7F3 | STA (ws_ptr_hi),y ; Store to PB[1] |
| A7F5 | INY ; Y=&02 |
| A7F6 | LDA #&80 ; A=&80 |
| A7F8 | STA (ws_ptr_hi),y ; Store &80 to PB[2] |
| A7FA | RTS ; Return |
OSWORD &13 sub 10: read error flagReturns the error flag (l0e09) in PB[1]. |
|
| A7FB | .osword_13_read_error |
| LDA fs_last_error ; Load error flag | |
| fall through ↓ | |
Store A to OSWORD parameter block at offset 1Increments Y to 1 and stores A into the OSWORD parameter block via (ws_ptr_hi),Y. Used by OSWORD 13 sub-handlers to return a single result byte.
|
|||||||
| A7FE | .store_a_to_pb_1←4← A72B JMP← A7EA JMP← A805 BPL← A80D BCS | ||||||
| INY ; Y=1: parameter block offset 1 | |||||||
| A7FF | STA (ws_ptr_hi),y ; Store result to PB[1] | ||||||
| A801 | RTS ; Return | ||||||
OSWORD &13 sub 11: read context byteReturns the context byte (l0d6d) in PB[1]. |
|
| A802 | .osword_13_read_context |
| LDA l0e08 ; Load context byte | |
| A805 | BPL store_a_to_pb_1 ; Bit 7 clear: store context to PB |
| fall through ↓ | |
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. |
|
| A807 | .osword_13_read_free_bufs |
| LDA #&6f ; Total buffers = &6F | |
| A809 | SEC ; Subtract used count |
| A80A | SBC spool_buf_idx ; Free = &6F - l0d6b |
| A80D | BCS store_a_to_pb_1 ; Non-negative: store free count to PB |
| fall through ↓ | |
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. |
|
| A80F | .osword_13_read_ctx_3←1← A817 BNE |
| INY ; Next byte offset | |
| A810 | LDA fs_flags,y ; Return Load l0d6d[Y] |
| A813 | STA (ws_ptr_hi),y ; Store to PB[Y] |
| A815 | CPY #3 ; Done 3 bytes? |
| A817 | BNE osword_13_read_ctx_3 ; No: loop |
| A819 | 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. |
|
| A81A | .osword_13_write_ctx_3←1← A822 BNE |
| INY ; Next byte offset | |
| A81B | LDA (ws_ptr_hi),y ; Load PB[Y] |
| A81D | STA fs_flags,y ; Store to l0d6d[Y] |
| A820 | CPY #3 ; Done 3 bytes? |
| A822 | BNE osword_13_write_ctx_3 ; No: loop |
| A824 | 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. |
|
| A825 | .osword_13_bridge_query |
| JSR init_bridge_poll ; Poll for bridge | |
| A828 | LDY #0 ; Y=0 |
| A82A | LDA l0d71 ; Load bridge status |
| A82D | CMP #&ff ; Is it &FF (no bridge)? |
| A82F | BNE bridge_found ; No: bridge found |
| A831 | TYA ; A=&00 |
| A832 | STA (ws_ptr_hi),y ; PB[0] = 0 (no bridge) |
| A834 | INY ; Y=&01 |
| A835 | BNE store_bridge_station ; ALWAYS branch |
| A837 | .bridge_found←1← A82F BNE |
| INY ; Y=1 | |
| A838 | STA (ws_ptr_hi),y ; PB[1] = bridge status |
| A83A | INY ; Y=2 |
| A83B | INY ; Y=3 |
| A83C | LDA (ws_ptr_hi),y ; Load PB[3] (caller value) |
| A83E | BEQ use_default_station ; Zero: use default station |
| A840 | .compare_bridge_status |
| EOR l0d71 ; Compare with bridge status | |
| A843 | BNE return_from_bridge_query ; Different: return unchanged |
| A845 | BEQ store_bridge_station ; Same: confirm station ALWAYS branch |
| A847 | .use_default_station←1← A83E BEQ |
| LDA fs_server_net ; Load default from l0e01 | |
| A84A | .store_bridge_station←2← A835 BNE← A845 BEQ |
| STA (ws_ptr_hi),y ; Store to PB[3] | |
| A84C | .return_from_bridge_query←1← A843 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. | |
| A84D | .bridge_txcb_init_table←1← A87A LDA |
| EQUB &82 ; TX 0: ctrl = &82 (immediate mode) | |
| A84E | EQUB &9C ; TX 1: port = &9C (bridge discovery) |
| A84F | EQUB &FF ; TX 2: dest station = &FF (broadcast) |
| A850 | EQUB &FF ; TX 3: dest network = &FF (all nets) |
| A851 | EQUS "BRIDGE" ; TX 4-9: immediate data payload |
| A857 | EQUB &9C ; TX 10: &9C (port echo) |
| A858 | EQUB &00 ; TX 11: &00 (terminator) |
| A859 | .bridge_rxcb_init_data |
| EQUB &7F ; RX 0: ctrl = &7F (receive) | |
| A85A | EQUB &9C ; RX 1: port = &9C (bridge discovery) |
| A85B | EQUB &00 ; RX 2: station = &00 (any) |
| A85C | EQUB &00 ; RX 3: network = &00 (any) |
| A85D | EQUB &71 |
| A85E | EQUB &0D ; RX 5: buf start hi (&0D) -> &0D72 |
| A85F | EQUB &FF ; RX 6: extended addr fill (&FF) |
| A860 | EQUB &FF ; RX 7: extended addr fill (&FF) |
| A861 | EQUB &73 |
| A862 | EQUB &0D ; RX 9: buf end hi (&0D) -> &0D74 |
| A863 | EQUB &FF ; RX 10: extended addr fill (&FF) |
| A864 | 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. |
|
| A865 | .init_bridge_poll←4← 8E09 JSR← A0B4 JSR← A71E JSR← A825 JSR |
| LDA l0d71 ; Check bridge status | |
| A868 | CMP #&ff ; Is it &FF (uninitialised)? |
| A86A | BNE return_from_bridge_poll ; No: bridge already active, return |
| A86C | TYA ; Save Y |
| A86D | PHA ; Preserve Y on stack |
| A86E | LDY #&18 ; Y=&18: workspace offset for init |
| A870 | LDX #&0b ; X=&0B: 12 bytes to copy |
| A872 | ROR econet_flags ; Rotate l0d61 right (save flag) |
| A875 | .loop_copy_bridge_init←1← A881 BPL |
| LDA bridge_ws_init_data,y ; Load init data byte | |
| A878 | STA (nfs_workspace),y ; Store to workspace |
| A87A | LDA bridge_txcb_init_table,x ; Load TXCB template byte |
| A87D | STA txcb_ctrl,x ; Store to TX control block |
| A87F | INY ; Next workspace byte |
| A880 | DEX ; Next template byte |
| A881 | BPL loop_copy_bridge_init ; Loop for all 12 bytes |
| A883 | STX l0d71 ; Store X (-1) as bridge counter |
| A886 | ROL econet_flags ; Restore l0d61 flag |
| A889 | .loop_wait_ws_status←2← A88C BCC← A8B3 BPL |
| ASL ws_0d60 ; Shift ws_0d60 left (check status) | |
| A88C | BCC loop_wait_ws_status ; C=0: status clear, retry |
| A88E | LDA #&82 ; Control byte &82 for TX |
| A890 | STA txcb_ctrl ; Set in TX control block |
| A892 | LDA #&c0 ; Data block at &00C0 |
| A894 | STA nmi_tx_block ; Set NMI TX block low |
| A896 | LDA #0 ; High byte = 0 (page 0) |
| A898 | STA nmi_tx_block_hi ; Set NMI TX block high |
| A89A | JSR tx_begin ; Begin Econet transmission |
| A89D | .loop_wait_tx_done←1← A89F BMI |
| BIT txcb_ctrl ; Test TX control block bit 7 | |
| A89F | BMI loop_wait_tx_done ; Negative: TX still in progress |
| A8A1 | TXA ; Transfer TX completion status to A |
| A8A2 | PHA ; Save TX status |
| A8A3 | LDA #osbyte_vsync ; OSBYTE &13: wait for VSYNC |
| A8A5 | JSR osbyte ; Wait for vertical sync |
| A8A8 | PLA ; Restore TX status |
| A8A9 | TAX ; Back to X |
| A8AA | LDY #&18 ; Y=&18: check workspace response |
| A8AC | LDA (nfs_workspace),y ; Load bridge response |
| A8AE | BMI bridge_responded ; Negative: bridge responded |
| A8B0 | JSR advance_x_by_8 ; Advance retry counter by 8 |
| A8B3 | BPL loop_wait_ws_status ; Positive: retry poll loop |
| A8B5 | .bridge_responded←1← A8AE BMI |
| LDA #&3f ; Set response to &3F (OK) | |
| A8B7 | STA (nfs_workspace),y ; Store to workspace |
| A8B9 | PLA ; Restore saved Y |
| A8BA | TAY ; Back to Y |
| A8BB | LDA l0d71 ; Load bridge status |
| A8BE | TAX ; X = bridge status |
| A8BF | EOR #&ff ; Complement status |
| A8C1 | BEQ return_from_bridge_poll ; Status was &FF: return (no bridge) |
| A8C3 | TXA ; Return bridge station in A |
| A8C4 | .return_from_bridge_poll←3← A86A BNE← A8C1 BEQ← A8CC BPL |
| RTS ; Return | |
| A8C5 | .osword_14_handler |
| CMP #1 ; Compare sub-code with 1 | |
| A8C7 | BCS handle_tx_request ; Sub-code >= 1: handle TX request |
| A8C9 | BIT fs_flags ; Test station active flag |
| A8CC | BPL return_from_bridge_poll ; Not active: return |
| A8CE | LDY #&23 ; Y=&23: workspace offset for params |
| A8D0 | JSR mask_owner_access ; Set owner access mask |
| A8D3 | .loop_copy_txcb_init←1← A8E0 BNE |
| LDA init_txcb,y ; Load TXCB init byte | |
| A8D6 | BNE store_txcb_init_byte ; Non-zero: use template value |
| A8D8 | LDA txcb_default_base,y ; Zero: use workspace default value |
| A8DB | .store_txcb_init_byte←1← A8D6 BNE |
| STA (nfs_workspace),y ; Store to workspace | |
| A8DD | DEY ; Next byte down |
| A8DE | CPY #&17 ; Until Y reaches &17 |
| A8E0 | BNE loop_copy_txcb_init ; Loop for all bytes |
| A8E2 | INY ; Y=&18 (INY from &17) |
| A8E3 | STY net_tx_ptr ; Set net_tx_ptr low byte |
| fall through ↓ | |
Store workspace pointer+1 to NFS workspaceComputes ws_ptr_hi + 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 ws_ptr_hi to compute the buffer end pointer, stored at workspace offset &20. |
|
| A8E5 | .store_osword_pb_ptr |
| LDY #&1c ; Y=&1C: workspace offset for PB pointer | |
| A8E7 | LDA ws_ptr_hi ; Load PB page number |
| A8E9 | ADC #1 ; PB starts at next page boundary (+1) |
| A8EB | JSR store_ptr_at_ws_y ; Store PB start pointer at ws[&1C] |
| A8EE | LDY #1 ; Y=1: PB byte 1 (transfer length) |
| A8F0 | LDA (ws_ptr_hi),y ; Load transfer length from PB |
| A8F2 | LDY #&20 ; Y=&20: workspace offset for buffer end |
| A8F4 | ADC ws_ptr_hi ; Add PB base for buffer end address |
| A8F6 | JSR store_ptr_at_ws_y ; Store PB pointer to workspace |
| A8F9 | LDY #2 ; Y=2: parameter offset |
| A8FB | LDA #&90 ; Control byte &90 |
| A8FD | STA need_release_tube ; Set escapable flag |
| A8FF | STA (ws_ptr_hi),y ; Store control byte to PB |
| A901 | INY ; Y=&03 |
| A902 | INY ; Y=&04 |
| A903 | .loop_copy_ws_to_pb←1← A90B BNE |
| LDA osword_ws_base,y ; Load workspace data | |
| A906 | STA (ws_ptr_hi),y ; Store to parameter block |
| A908 | INY ; Next byte |
| A909 | CPY #7 ; Until Y reaches 7 |
| A90B | BNE loop_copy_ws_to_pb ; Loop for 3 bytes (Y=4,5,6) |
| A90D | LDA nfs_workspace_hi ; Set TX pointer high byte |
| A90F | STA net_tx_ptr_hi ; Store to net_tx_ptr_hi |
| A911 | JSR enable_irq_and_poll ; Enable interrupts Send the network packet |
| A914 | LDY #&20 ; Y=&20: workspace offset |
| A916 | LDA #&ff ; Set to &FF (pending) |
| A918 | STA (nfs_workspace),y ; Mark send pending in workspace |
| A91A | INY ; Y=&21 |
| A91B | STA (nfs_workspace),y ; Also mark offset &21 |
| A91D | LDY #&19 ; Y=&19: control offset |
| A91F | LDA #&90 ; Control byte &90 |
| A921 | STA (nfs_workspace),y ; Store to workspace |
| A923 | DEY ; Y=&18: RX control offset Y=&18 |
| A924 | LDA #&7f ; Control byte &7F |
| A926 | STA (nfs_workspace),y ; Store RX control |
| A928 | JMP wait_net_tx_ack ; Wait for TX acknowledgement |
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 table_idx plus carry, supporting pointer arithmetic across page boundaries.
|
||||||||
| A92B | .store_ptr_at_ws_y←2← A8EB JSR← A8F6 JSR | |||||||
| STA (nfs_workspace),y ; Store address low byte at ws[Y] | ||||||||
| A92D | INY ; Advance to high byte offset | |||||||
| A92E | LDA table_idx ; Load high byte base (table_idx) | |||||||
| A930 | ADC #0 ; Add carry for page crossing | |||||||
| A932 | STA (nfs_workspace),y ; Store address high byte at ws[Y+1] | |||||||
| A934 | RTS ; Return | |||||||
| A935 | .handle_tx_request←1← A8C7 BCS | |||||||
| PHP ; Save processor flags | ||||||||
| A936 | LDY #1 ; Y=1: PB offset for station | |||||||
| A938 | LDA (ws_ptr_hi),y ; Load station number from PB | |||||||
| A93A | TAX ; X = station number | |||||||
| A93B | INY ; Y=&02 | |||||||
| A93C | LDA (ws_ptr_hi),y ; Load network number from PB | |||||||
| A93E | INY ; Y=3: workspace start offset Y=&03 | |||||||
| A93F | STY osword_flag ; Store Y as ws_ptr_lo | |||||||
| A941 | LDY #&72 ; Y=&72: workspace offset for dest | |||||||
| A943 | STA (net_rx_ptr),y ; Store network to workspace | |||||||
| A945 | DEY ; Y=&71 | |||||||
| A946 | TXA ; A = station (from X) | |||||||
| A947 | STA (net_rx_ptr),y ; Store station to workspace | |||||||
| A949 | PLP ; Restore flags from PHP | |||||||
| A94A | BNE handle_burst_xfer ; Non-zero sub-code: handle burst | |||||||
| A94C | .loop_send_pb_chars←1← A968 BNE | |||||||
| LDY osword_flag ; Load current offset | ||||||||
| A94E | INC osword_flag ; Advance offset for next byte | |||||||
| A950 | LDA (ws_ptr_hi),y ; Load next char from PB | |||||||
| A952 | BEQ return_1 ; Zero: end of data, return | |||||||
| A954 | LDY #&7d ; Y=&7D: workspace char offset | |||||||
| A956 | STA (net_rx_ptr),y ; Store char to RX buffer | |||||||
| A958 | PHA ; Save char for later test | |||||||
| A959 | JSR init_ws_copy_wide ; Init workspace copy for wide xfer | |||||||
| A95C | SEC ; Set carry for flag set | |||||||
| A95D | ROR need_release_tube ; Set bit 7: Tube needs release | |||||||
| A95F | JSR enable_irq_and_poll ; Enable IRQ and send packet | |||||||
| A962 | .loop_bridge_tx_delay←1← A963 BNE | |||||||
| DEX ; Delay countdown | ||||||||
| A963 | BNE loop_bridge_tx_delay ; Loop for short delay | |||||||
| A965 | PLA ; Restore char | |||||||
| A966 | EOR #&0d ; Test if char was CR (&0D) | |||||||
| A968 | BNE loop_send_pb_chars ; Not CR: send next char | |||||||
| A96A | .return_1←1← A952 BEQ | |||||||
| RTS ; CR sent: return | ||||||||
| A96B | .handle_burst_xfer←1← A94A BNE | |||||||
| JSR init_ws_copy_wide ; Init workspace for wide copy | ||||||||
| A96E | LDY #&7b ; Y=&7B: workspace offset | |||||||
| A970 | LDA (net_rx_ptr),y ; Load buffer size | |||||||
| A972 | ADC #3 ; Add 3 for header | |||||||
| A974 | 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. |
|
| A976 | .enable_irq_and_poll←2← A911 JSR← A95F JSR |
| CLI ; Enable interrupts | |
| A977 | 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 (&8E8A) in the extended vector data area. |
|
| A97A | .netv_handler |
| PHP ; Save processor flags | |
| A97B | PHA ; Save A |
| A97C | TXA ; Save X |
| A97D | PHA ; Push X |
| A97E | TYA ; Save Y |
| A97F | PHA ; Push Y |
| A980 | TSX ; Get stack pointer |
| A981 | LDA stack_page_3,x ; Read OSWORD number from stack |
| A984 | CMP #9 ; OSWORD >= 9? |
| A986 | BCS restore_regs_return ; Yes: out of range, restore + return |
| A988 | TAX ; X = OSWORD number |
| A989 | JSR push_osword_handler_addr ; Push handler address for dispatch |
| A98C | .restore_regs_return←1← A986 BCS |
| PLA ; Restore Y | |
| A98D | TAY ; Back to Y |
| A98E | PLA ; Restore X |
| A98F | TAX ; Back to X |
| A990 | PLA ; Restore A |
| A991 | PLP ; Restore processor flags |
| A992 | 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. |
|
| A993 | .push_osword_handler_addr←1← A989 JSR |
| LDA osword_handler_hi_table,x ; Load handler high byte from table | |
| A996 | PHA ; Push for RTS dispatch |
| A997 | LDA osword_handler_lo_table,x ; Load handler low byte from table |
| A99A | PHA ; Push for RTS dispatch |
| A99B | LDA osbyte_a_copy ; Reload OSWORD number for handler |
| A99D | 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. | |
| A99E | .osword_handler_lo_table←1← A997 LDA |
| EQUB <(dispatch_rts-1) | |
| A99F | EQUB <(netv_print_data-1) |
| A9A0 | EQUB <(netv_print_data-1) |
| A9A1 | EQUB <(netv_print_data-1) |
| A9A2 | EQUB <(osword_4_handler-1) |
| A9A3 | EQUB <(netv_spool_check-1) |
| A9A4 | EQUB <(dispatch_rts-1) |
| A9A5 | EQUB <(netv_claim_release-1) |
| A9A6 | EQUB <(osword_8_handler-1) |
| A9A7 | .osword_handler_hi_table←1← A993 LDA |
| EQUB >(dispatch_rts-1) ; hi OSWORD 0: no-op (RTS) | |
| A9A8 | EQUB >(netv_print_data-1) ; hi OSWORD 1: printer spool data |
| A9A9 | EQUB >(netv_print_data-1) ; hi OSWORD 2: printer spool data |
| A9AA | EQUB >(netv_print_data-1) ; hi OSWORD 3: printer spool data |
| A9AB | EQUB >(osword_4_handler-1) ; hi OSWORD 4: clear carry + abort |
| A9AC | EQUB >(netv_spool_check-1) ; hi OSWORD 5: spool buffer check |
| A9AD | EQUB >(dispatch_rts-1) ; hi OSWORD 6: no-op (RTS) |
| A9AE | EQUB >(netv_claim_release-1) ; hi OSWORD 7: claim/release handler |
| A9AF | 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). |
|
| A9B0 | .osword_4_handler |
| TSX ; Get stack pointer | |
| A9B1 | ROR stack_page_6,x ; Clear bit 0 of stacked P (carry) |
| A9B4 | ASL stack_page_6,x ; Shift back (clears carry flag) |
| A9B7 | TYA ; A = original Y |
| A9B8 | LDY #&da ; Y=&DA: workspace offset |
| A9BA | STA (nfs_workspace),y ; Store Y to workspace |
| A9BC | 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. |
|
| A9BE | .tx_econet_abort←3← 8AE7 JSR← AA11 JSR← AA77 JSR |
| LDY #&d9 ; Y=&D9: workspace abort offset | |
| A9C0 | STA (nfs_workspace),y ; Store abort code to workspace |
| A9C2 | LDA #&80 ; Control byte &80 (abort) |
| A9C4 | LDY #&0c ; Y=&0C: control offset |
| A9C6 | STA (nfs_workspace),y ; Store control byte |
| A9C8 | LDA net_tx_ptr ; Save current TX ptr low |
| A9CA | PHA ; Push on stack |
| A9CB | LDA net_tx_ptr_hi ; Save current TX ptr high |
| A9CD | PHA ; Push on stack |
| A9CE | STY net_tx_ptr ; Set TX ptr to workspace offset |
| A9D0 | LDX nfs_workspace_hi ; Load workspace high byte |
| A9D2 | STX net_tx_ptr_hi ; Set TX ptr high |
| A9D4 | JSR send_net_packet ; Send the abort packet |
| A9D7 | LDA #&3f ; Set status to &3F (complete) |
| A9D9 | STA (net_tx_ptr,x) ; Store at TX ptr offset 0 |
| A9DB | PLA ; Restore TX ptr high |
| A9DC | STA net_tx_ptr_hi ; Back to net_tx_ptr_hi |
| A9DE | PLA ; Restore TX ptr low |
| A9DF | STA net_tx_ptr ; Back to net_tx_ptr |
| A9E1 | 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. |
|
| A9E2 | .netv_claim_release |
| LDY osword_pb_ptr_hi ; Load PB pointer high | |
| A9E4 | CMP #&81 ; Compare with &81 (special case) |
| A9E6 | BEQ process_match_result ; Match: skip to processing |
| A9E8 | LDY #1 ; Y=1: first claim code position |
| A9EA | LDX #&0a ; X=&0A: 11 codes to check |
| A9EC | JSR match_rx_code ; Search claim code table |
| A9EF | BEQ process_match_result ; Found: skip to processing |
| A9F1 | DEY ; Try second table range |
| A9F2 | DEY ; Y=-1: flag second range |
| A9F3 | LDX #&11 ; X=&11: 18 codes to check |
| A9F5 | JSR match_rx_code ; Search claim code table |
| A9F8 | BEQ process_match_result ; Found: skip to processing |
| A9FA | INY ; Not found: increment Y |
| A9FB | .process_match_result←3← A9E6 BEQ← A9EF BEQ← A9F8 BEQ |
| LDX #2 ; X=2: default state | |
| A9FD | TYA ; A = Y (search result) |
| A9FE | BEQ return_from_claim_release ; Zero: not found, return |
| AA00 | PHP ; Save result flags |
| AA01 | BPL save_tube_state ; Positive: use state X=2 |
| AA03 | INX ; X=&03 |
| AA04 | .save_tube_state←1← AA01 BPL |
| LDY #&dc ; Y=&DC: workspace offset for save | |
| AA06 | .loop_save_tube_bytes←1← AA0E BPL |
| LDA tube_claimed_id,y ; Load tube claim ID byte | |
| AA09 | STA (nfs_workspace),y ; Store to workspace |
| AA0B | DEY ; Next byte down |
| AA0C | CPY #&da ; Until Y reaches &DA |
| AA0E | BPL loop_save_tube_bytes ; Loop for 3 bytes |
| AA10 | TXA ; A = state (2 or 3) |
| AA11 | JSR tx_econet_abort ; Send abort with state code |
| AA14 | PLP ; Restore flags |
| AA15 | BPL return_from_claim_release ; Positive: return without poll |
| AA17 | LDA #&7f ; Set control to &7F |
| AA19 | LDY #&0c ; Y=&0C: control offset |
| AA1B | STA (nfs_workspace),y ; Store control byte |
| AA1D | .loop_poll_ws_status←1← AA1F BPL |
| LDA (nfs_workspace),y ; Load status from workspace | |
| AA1F | BPL loop_poll_ws_status ; Positive: keep waiting |
| AA21 | TSX ; Get stack pointer |
| AA22 | LDY #&dd ; Y=&DD: workspace result offset |
| AA24 | LDA (nfs_workspace),y ; Load result byte |
| AA26 | ORA #&44 ; Set bit 6 and bit 2 |
| AA28 | BNE store_stack_byte ; Always branch (NZ from ORA) ALWAYS branch |
| AA2A | .loop_restore_stack←1← AA33 BNE |
| DEY ; Previous workspace byte | |
| AA2B | DEX ; Previous stack position |
| AA2C | LDA (nfs_workspace),y ; Load workspace byte |
| AA2E | .store_stack_byte←1← AA28 BNE |
| STA stack_page_6,x ; Store to caller's stack frame | |
| AA31 | CPY #&da ; Reached start of save area? |
| AA33 | BNE loop_restore_stack ; No: copy next byte |
| AA35 | .return_from_claim_release←2← A9FE BEQ← AA15 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.
|
|||||||||
| AA36 | .match_rx_code←3← A9EC JSR← A9F5 JSR← AA3C BPL | ||||||||
| CMP osword_claim_codes,x ; Compare A with code at index X | |||||||||
| AA39 | BEQ return_from_match_rx_code ; Match: return with Z set | ||||||||
| AA3B | DEX ; Try next code | ||||||||
| AA3C | BPL match_rx_code ; More codes: continue search | ||||||||
| AA3E | .return_from_match_rx_code←2← AA39 BEQ← AA59 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. | |||||||||
| AA3F | .osword_claim_codes←1← AA36 CMP | ||||||||
| EQUB &04 ; Range 1+2: OSWORD &04 | |||||||||
| AA40 | EQUB &09 ; Range 1+2: OSWORD &09 | ||||||||
| AA41 | EQUB &0A ; Range 1+2: OSWORD &0A | ||||||||
| AA42 | EQUB &14 ; Range 1+2: OSWORD &14 | ||||||||
| AA43 | EQUB &15 ; Range 1+2: OSWORD &15 | ||||||||
| AA44 | EQUB &9A ; Range 1+2: OSWORD &9A | ||||||||
| AA45 | EQUB &9B ; Range 1+2: OSWORD &9B | ||||||||
| AA46 | EQUB &E1 ; Range 1+2: OSWORD &E1 | ||||||||
| AA47 | EQUB &E2 ; Range 1+2: OSWORD &E2 | ||||||||
| AA48 | EQUB &E3 ; Range 1+2: OSWORD &E3 | ||||||||
| AA49 | EQUB &E4 ; Range 1+2: OSWORD &E4 | ||||||||
| AA4A | EQUB &0B ; Range 2 only: OSWORD &0B | ||||||||
| AA4B | EQUB &0C ; Range 2 only: OSWORD &0C | ||||||||
| AA4C | EQUB &0F ; Range 2 only: OSWORD &0F | ||||||||
| AA4D | EQUB &79 ; Range 2 only: OSWORD &79 | ||||||||
| AA4E | EQUB &7A ; Range 2 only: OSWORD &7A | ||||||||
| AA4F | EQUB &86 ; Range 2 only: OSWORD &86 | ||||||||
| AA50 | 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. |
|
| AA51 | .osword_8_handler |
| LDY #&0e ; Y=&0E: copy 15 bytes (0-14) | |
| AA53 | CMP #7 ; OSWORD 7? |
| AA55 | BEQ copy_pb_to_ws ; Yes: handle |
| AA57 | CMP #8 ; OSWORD 8? |
| AA59 | BNE return_from_match_rx_code ; No: return |
| AA5B | .copy_pb_to_ws←1← AA55 BEQ |
| LDX #&db ; Workspace low = &DB | |
| AA5D | STX nfs_workspace ; Set nfs_workspace low byte |
| AA5F | .loop_copy_pb_to_ws←1← AA64 BPL |
| LDA (osword_pb_ptr),y ; Load PB[Y] | |
| AA61 | STA (nfs_workspace),y ; Store to workspace[Y] |
| AA63 | DEY ; Next byte down |
| AA64 | BPL loop_copy_pb_to_ws ; Loop for 15 bytes |
| AA66 | INY ; Y=0 |
| AA67 | DEC nfs_workspace ; Workspace low = &DA |
| AA69 | LDA osbyte_a_copy ; Load OSWORD number |
| AA6B | STA (nfs_workspace),y ; Store at workspace+0 (= &DA) |
| AA6D | STY nfs_workspace ; Workspace low = 0 (restore) |
| AA6F | LDY #&14 ; Y=&14: control offset |
| AA71 | LDA #&e9 ; Control value &E9 |
| AA73 | STA (nfs_workspace),y ; Store to workspace+&14 |
| AA75 | LDA #1 ; Abort code = 1 |
| AA77 | JSR tx_econet_abort ; Send abort packet |
| AA7A | 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. |
|
| AA7C | .init_ws_copy_wide←2← A959 JSR← A96B JSR |
| LDX #&0d ; X=&0D: 14 bytes to copy | |
| AA7E | LDY #&7c ; Y=&7C: workspace destination offset |
| AA80 | BIT bit_test_ff ; Test bit 6 via BIT (V flag check) |
| AA83 | 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. |
|
| AA85 | .init_ws_copy_narrow←1← 95A2 JSR |
| LDY #&17 ; Y=&17: narrow mode dest offset | |
| AA87 | 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. |
|
| AA89 | .ws_copy_vclr_entry←1← AB4A JSR |
| CLV ; Clear V flag for narrow mode | |
| AA8A | .loop_copy_ws_template←2← AA83 BVS← AAAB BPL |
| LDA ws_txcb_template_data,x ; Load template byte | |
| AA8D | CMP #&fe ; Is it &FE? (end marker) |
| AA8F | BEQ done_ws_template_copy ; Yes: finished, set TX ptr |
| AA91 | CMP #&fd ; Is it &FD? (skip marker) |
| AA93 | BEQ advance_template_idx ; Yes: skip store, just advance |
| AA95 | CMP #&fc ; Is it &FC? (page ptr marker) |
| AA97 | BNE select_store_target ; No: use literal value |
| AA99 | LDA net_rx_ptr_hi ; &FC: load RX buffer page |
| AA9B | BVS store_tx_ptr_hi ; V=1: use net_rx_ptr_hi |
| AA9D | LDA nfs_workspace_hi ; V=0: use nfs_workspace_hi |
| AA9F | .store_tx_ptr_hi←1← AA9B BVS |
| STA net_tx_ptr_hi ; Store as TX ptr high | |
| AAA1 | .select_store_target←1← AA97 BNE |
| BVS store_via_rx_ptr ; V=1: store to net_rx_ptr target | |
| AAA3 | STA (nfs_workspace),y ; V=0: store to nfs_workspace |
| AAA5 | BVC advance_template_idx ; Continue to next byte ALWAYS branch |
| AAA7 | .store_via_rx_ptr←1← AAA1 BVS |
| STA (net_rx_ptr),y ; V=1: store to net_rx_ptr | |
| AAA9 | .advance_template_idx←2← AA93 BEQ← AAA5 BVC |
| DEY ; Advance workspace offset down | |
| AAAA | DEX ; Advance template index |
| AAAB | BPL loop_copy_ws_template ; More bytes: continue copy |
| AAAD | .done_ws_template_copy←1← AA8F BEQ |
| INY ; Adjust Y for start of TX data | |
| AAAE | STY net_tx_ptr ; Set net_tx_ptr from Y |
| AAB0 | 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. | |
| AAB1 | .ws_txcb_template_data←1← AA8A LDA |
| EQUB &85 ; Wide &6F: ctrl=&85 | |
| AAB2 | EQUB &00 ; Wide &70: port=&00 |
| AAB3 | EQUB &FD ; Wide &71: skip (dest station) |
| AAB4 | EQUB &FD ; Wide &72: skip (dest network) |
| AAB5 | EQUB &7D ; Wide &73: buf start lo=&7D |
| AAB6 | EQUB &FC ; Wide &74: buf start hi=page ptr |
| AAB7 | EQUB &FF ; Wide &75: buf start ext lo |
| AAB8 | EQUB &FF ; Wide &76: buf start ext hi |
| AAB9 | EQUB &7E ; Wide &77: buf end lo=&7E |
| AABA | EQUB &FC ; Wide &78: buf end hi=page ptr |
| AABB | EQUB &FF ; Wide &79: buf end ext lo |
| AABC | EQUB &FF ; Wide &7A: buf end ext hi |
| AABD | EQUB &00 ; Wide &7B: zero |
| AABE | EQUB &00 ; Wide &7C: zero |
| AABF | EQUB &FE ; Narrow stop (&FE terminator) |
| AAC0 | EQUB &80 ; Narrow &0C: ctrl=&80 (standard) |
| AAC1 | EQUB &93 ; Narrow &0D: port=&93 |
| AAC2 | EQUB &FD ; Narrow &0E: skip (dest station) |
| AAC3 | EQUB &FD ; Narrow &0F: skip (dest network) |
| AAC4 | EQUB &D9 ; Narrow &10: buf start lo=&D9 |
| AAC5 | EQUB &FC ; Narrow &11: buf start hi=page ptr |
| AAC6 | EQUB &FF ; Narrow &12: buf start ext lo |
| AAC7 | EQUB &FF ; Narrow &13: buf start ext hi |
| AAC8 | EQUB &DE ; Narrow &14: buf end lo=&DE |
| AAC9 | EQUB &FC ; Narrow &15: buf end hi=page ptr |
| AACA | EQUB &FF ; Narrow &16: buf end ext lo |
| AACB | EQUB &FF ; Narrow &17: buf end ext hi |
| AACC | EQUB &FE ; Spool stop (&FE terminator) |
| AACD | EQUB &D1 ; Spool &01: port=&D1 |
| AACE | EQUB &FD |
| AACF | EQUB &FD ; Spool &03: skip (dest network) |
| AAD0 | EQUB &21 |
| AAD1 | EQUB &FD ; Spool &05: skip (buf start hi) |
| AAD2 | EQUB &FF ; Spool &06: buf start ext lo |
| AAD3 | EQUB &FF ; Spool &07: buf start ext hi |
| AAD4 | EQUB &FD ; Spool &08: skip (buf end lo) |
| AAD5 | EQUB &FD ; Spool &09: skip (buf end hi) |
| AAD6 | EQUB &FF ; Spool &0A: buf end ext lo |
| AAD7 | 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. |
|
| AAD8 | .netv_spool_check |
| DEX ; X = X - 1 | |
| AAD9 | CPX osword_pb_ptr ; Match osword_pb_ptr? |
| AADB | BNE return_from_spool_reset ; No: return (not our PB) |
| AADD | LDA vdu_status ; Load spool state byte |
| AADF | ROR ; Rotate bit 0 into carry |
| AAE0 | 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. |
|
| AAE2 | .reset_spool_buf_state←2← 8F27 JSR← AB33 JMP |
| LDA #&21 ; Buffer start at &25 | |
| AAE4 | STA spool_buf_idx ; Store as buffer pointer |
| AAE7 | LDA #&41 ; Control state &41 |
| AAE9 | STA ws_0d6a ; Store as spool control state |
| AAEC | .return_from_spool_reset←4← AADB BNE← AAE0 BCS← AAEF BNE← AB03 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. |
|
| AAED | .netv_print_data |
| CPY #4 ; Check Y == 4 | |
| AAEF | BNE return_from_spool_reset ; No: return |
| AAF1 | TXA ; A = X (control byte) |
| AAF2 | DEX ; Decrement X |
| AAF3 | BNE handle_spool_ctrl_byte ; Non-zero: handle spool ctrl byte |
| AAF5 | TSX ; Get stack pointer |
| AAF6 | ORA stack_page_6,x ; OR with stack value |
| AAF9 | STA stack_page_6,x ; Store back to stack |
| AAFC | .loop_drain_printer_buf←2← AB0B BCC← AB10 BCC |
| LDA #osbyte_read_buffer ; OSBYTE &91: read buffer | |
| AAFE | LDX #buffer_printer ; X=3: printer buffer |
| AB00 | JSR osbyte ; Read character from buffer Get character from input buffer (C is set if the buffer is empty, otherwise Y=extracted character) |
| AB03 | BCS return_from_spool_reset ; C=1: buffer empty, return |
| AB05 | TYA ; A = extracted character Y is the character extracted from the buffer |
| AB06 | JSR append_byte_to_rxbuf ; Add byte to RX buffer |
| AB09 | CPY #&6e ; Buffer past &6E limit? |
| AB0B | BCC loop_drain_printer_buf ; No: read more from buffer |
| AB0D | JSR process_spool_data ; Buffer full: send packet |
| AB10 | 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.
|
||||
| AB12 | .append_byte_to_rxbuf←3← AB06 JSR← AB2D JSR← ABE4 JSR | |||
| LDY spool_buf_idx ; Load current buffer index | ||||
| AB15 | STA (net_rx_ptr),y ; Store byte at buffer position | |||
| AB17 | INC spool_buf_idx ; Advance buffer index | |||
| AB1A | 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. |
|
| AB1B | .handle_spool_ctrl_byte←2← 8F5A JSR← AAF3 BNE |
| ROR ; Rotate bit 0 into carry | |
| AB1C | BCC check_spool_state ; Bit 0 clear: not active path |
| AB1E | LDA ws_0d6a ; Load spool control state |
| AB21 | PHA ; Save for bit test |
| AB22 | ROR ; Rotate bit 0 into carry |
| AB23 | PLA ; Restore state |
| AB24 | BCS done_spool_ctrl ; C=1: already started, reset |
| AB26 | ORA #3 ; Set bits 0-1 (active + pending) |
| AB28 | STA ws_0d6a ; Store updated state |
| AB2B | LDA #3 ; Control byte 3 for header |
| AB2D | JSR append_byte_to_rxbuf ; Add to RX buffer |
| AB30 | JSR process_spool_data ; Send current buffer |
| AB33 | .done_spool_ctrl←1← AB24 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. |
|
| AB36 | .process_spool_data←4← AB0D JSR← AB30 JSR← AB79 BCC← ABE7 JSR |
| LDY #8 ; Y=8: workspace offset for length | |
| AB38 | LDA spool_buf_idx ; Load buffer index (=length) |
| AB3B | STA (nfs_workspace),y ; Store length to workspace |
| AB3D | LDA net_rx_ptr_hi ; Set data page high byte |
| AB3F | INY ; Y=&09 |
| AB40 | STA (nfs_workspace),y ; Store to workspace+9 |
| AB42 | LDY #5 ; Y=5: workspace offset |
| AB44 | STA (nfs_workspace),y ; Store page to workspace+5 |
| AB46 | LDY #&0b ; Y=&0B: template start offset |
| AB48 | LDX #&26 ; X=&26: template index |
| AB4A | JSR ws_copy_vclr_entry ; Copy template to workspace |
| AB4D | DEY ; Adjust Y down |
| AB4E | LDA ws_0d6a ; Load spool control state |
| AB51 | PHA ; Save state |
| AB52 | ROL ; Rotate to get carry (bit 7) |
| AB53 | PLA ; Restore state |
| AB54 | EOR #&80 ; Toggle bit 7 |
| AB56 | STA ws_0d6a ; Store updated state |
| AB59 | ROL ; Shift to get both flag bits |
| AB5A | STA (nfs_workspace),y ; Store flags to workspace |
| AB5C | LDA vdu_status ; Save l00d0 (exec flag) |
| AB5E | PHA ; Push for later restore |
| AB5F | AND #&fe ; Clear bit 0 of exec flag |
| AB61 | STA vdu_status ; Store modified exec flag |
| AB63 | LDY #&21 ; Reset buffer start to &25 |
| AB65 | STY spool_buf_idx ; Store reset buffer index |
| AB68 | LDA #0 ; A=0 for disconnect reply |
| AB6A | TAX ; X=0 X=&00 |
| AB6B | LDY nfs_workspace_hi ; Y = workspace page |
| AB6D | CLI ; Enable interrupts |
| AB6E | JSR send_disconnect_reply ; Send disconnect reply packet |
| AB71 | PLA ; Restore exec flag |
| AB72 | STA vdu_status ; Store original exec flag |
| AB74 | RTS ; Return |
| AB75 | .check_spool_state←1← AB1C BCC |
| LDA ws_0d6a ; Load spool control state | |
| AB78 | ROR ; Rotate bit 0 to carry |
| AB79 | BCC process_spool_data ; C=0: send current buffer |
| AB7B | LDA vdu_status ; Save exec flag |
| AB7D | PHA ; Push for restore |
| AB7E | AND #&fe ; Clear bit 0 |
| AB80 | STA vdu_status ; Store modified flag |
| AB82 | LDA #&14 ; Control byte &14 (repeat count) |
| AB84 | .start_spool_retry←1← ABF8 BNE |
| PHA ; Save retry count | |
| AB85 | LDX #&0b ; X=&0B: 12 bytes of template |
| AB87 | LDY #&2c ; Y=&2C: workspace offset for TXCB |
| AB89 | .loop_copy_spool_tx←1← AB90 BPL |
| LDA tx_econet_txcb_template,x ; Load template byte | |
| AB8C | STA (net_rx_ptr),y ; Store to workspace |
| AB8E | DEY ; Next byte down |
| AB8F | DEX ; Next template byte |
| AB90 | BPL loop_copy_spool_tx ; Loop for 12 bytes |
| AB92 | STX need_release_tube ; X=-1: disable escape checking |
| AB94 | LDY #2 ; Y=2: workspace offset for station |
| AB96 | LDA (nfs_workspace),y ; Load station number |
| AB98 | PHA ; Save station |
| AB99 | INY ; Y=&03 |
| AB9A | LDA (nfs_workspace),y ; Load network number |
| AB9C | LDY #&24 ; Y=&24: TXCB dest network offset |
| AB9E | STA (net_rx_ptr),y ; Store network to TXCB |
| ABA0 | DEY ; Y=&23 |
| ABA1 | PLA ; Restore station |
| ABA2 | STA (net_rx_ptr),y ; Store station to TXCB |
| ABA4 | LDX #&0b ; X=&0B: 12 bytes of RX template |
| ABA6 | LDY #&0b ; Y=&0B: workspace RX offset |
| ABA8 | .loop_copy_spool_rx←1← ABB9 BPL |
| LDA rx_palette_txcb_template,x ; Load RX template byte | |
| ABAB | CMP #&fd ; Is it &FD? (skip marker) |
| ABAD | BEQ advance_spool_rx_idx ; Yes: skip store |
| ABAF | CMP #&fc ; Is it &FC? (page ptr marker) |
| ABB1 | BNE store_spool_rx_byte ; No: use literal value |
| ABB3 | LDA net_rx_ptr_hi ; &FC: substitute RX buffer page |
| ABB5 | .store_spool_rx_byte←1← ABB1 BNE |
| STA (nfs_workspace),y ; Store to workspace | |
| ABB7 | .advance_spool_rx_idx←1← ABAD BEQ |
| DEY ; Next byte down | |
| ABB8 | DEX ; Next template byte |
| ABB9 | BPL loop_copy_spool_rx ; Loop for 12 bytes |
| ABBB | LDA #&21 ; TX data start at &25 |
| ABBD | STA net_tx_ptr ; Set net_tx_ptr low |
| ABBF | LDA net_rx_ptr_hi ; Set data page high byte |
| ABC1 | STA net_tx_ptr_hi ; Set net_tx_ptr high |
| ABC3 | JSR setup_pass_txbuf ; Set up password in TX buffer |
| ABC6 | JSR send_net_packet ; Send the packet |
| ABC9 | LDA #0 ; Clear net_tx_ptr low (page base) |
| ABCB | STA net_tx_ptr ; Store zero |
| ABCD | LDA nfs_workspace_hi ; Set TX high to workspace page |
| ABCF | STA net_tx_ptr_hi ; Store workspace high byte |
| ABD1 | JSR wait_net_tx_ack ; Wait for TX acknowledgement |
| ABD4 | LDY #&2d ; Y=&2D: reply status offset |
| ABD6 | LDA (net_rx_ptr),y ; Load reply byte |
| ABD8 | BEQ spool_tx_succeeded ; Zero: success |
| ABDA | CMP #3 ; Status = 3? (busy, can retry) |
| ABDC | BNE spool_tx_retry ; Other error: handle failure |
| ABDE | .spool_tx_succeeded←1← ABD8 BEQ |
| PLA ; Discard retry count | |
| ABDF | PLA ; Discard saved exec flag |
| ABE0 | STA vdu_status ; Restore l00d0 |
| ABE2 | LDA #0 ; A=0: null terminator |
| ABE4 | JSR append_byte_to_rxbuf ; Add zero to RX buffer (end marker) |
| ABE7 | JSR process_spool_data ; Send final buffer |
| ABEA | LDA ws_0d6a ; Load spool state |
| ABED | AND #&f0 ; Clear low nibble |
| ABEF | STA ws_0d6a ; Store cleaned state |
| ABF2 | RTS ; Return |
| ABF3 | .spool_tx_retry←1← ABDC BNE |
| TAX ; X = error code | |
| ABF4 | PLA ; Restore retry count |
| ABF5 | SEC ; Set carry for subtract |
| ABF6 | SBC #1 ; Decrement retry count |
| ABF8 | BNE start_spool_retry ; Non-zero: retry send |
| ABFA | CPX #1 ; Error code = 1? (busy) |
| ABFC | BNE error_printer_jammed ; No: printer jammed error |
| ABFE | .err_printer_busy←1← AFF5 JMP |
| LDA #&a6 ; A=&A6: printer busy error number | |
| AC00 | JSR error_inline_log ; Generate 'Printer busy' error |
| AC03 | EQUS "Printer busy." |
| AC10 | .error_printer_jammed←1← ABFC BNE |
| LDA #&a7 ; A=&A7: printer jammed error number | |
| AC12 | JSR error_inline_log ; Generate 'Printer jammed' error |
| AC15 | 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. |
|
| AC24 | .send_disconnect_reply←3← 9509 JSR← AB6E JSR← B951 JSR |
| STX net_tx_ptr ; Set TX ptr low byte | |
| AC26 | STY net_tx_ptr_hi ; Set TX ptr high byte |
| AC28 | PHA ; Save disconnect code |
| AC29 | ORA #0 ; Test if zero |
| AC2B | BEQ send_disconnect_status ; Zero: skip station search |
| AC2D | LDX #&ff ; X=&FF: start search from -1 |
| AC2F | TAY ; Y = disconnect code |
| AC30 | .loop_scan_disconnect←2← AC39 BNE← AC43 BNE |
| TYA ; A = disconnect code | |
| AC31 | INX ; Next station index |
| AC32 | CMP fcb_net_or_port,x ; Compare with station table entry |
| AC35 | BEQ verify_stn_match ; Match: verify station/network |
| AC37 | CPX #&0f ; Past last station? |
| AC39 | BNE loop_scan_disconnect ; No: try next |
| AC3B | LDA #0 ; Not found: A=0 |
| AC3D | BEQ send_disconnect_status ; Skip to status update ALWAYS branch |
| AC3F | .verify_stn_match←1← AC35 BEQ |
| TAY ; Y = disconnect code for compare | |
| AC40 | JSR match_station_net ; Check station and network match |
| AC43 | BNE loop_scan_disconnect ; No match: try next station |
| AC45 | LDA chan_status,x ; Load station status flags |
| AC48 | AND #1 ; Isolate bit 0 (active flag) |
| AC4A | .send_disconnect_status←2← AC2B BEQ← AC3D BEQ |
| LDY #0 ; Y=0: TX buffer status offset | |
| AC4C | ORA (net_tx_ptr),y ; OR with existing status byte |
| AC4E | PHA ; Save combined status |
| AC4F | STA (net_tx_ptr),y ; Store to TX buffer |
| AC51 | JSR send_net_packet ; Send the packet |
| AC54 | LDA #&ff ; Set end markers to &FF |
| AC56 | LDY #8 ; Y=8: first end marker offset |
| AC58 | STA (net_tx_ptr),y ; Store &FF |
| AC5A | INY ; Y=&09 |
| AC5B | STA (net_tx_ptr),y ; Store &FF at offset 9 too |
| AC5D | PLA ; Restore disconnect code |
| AC5E | TAX ; X = status for control byte |
| AC5F | LDY #&d1 ; Y=&D1: default control |
| AC61 | PLA ; Check original disconnect code |
| AC62 | PHA ; Peek but keep on stack |
| AC63 | BEQ store_tx_ctrl_byte ; Zero: use &D1 control |
| AC65 | LDY #&90 ; Non-zero: use &90 control |
| AC67 | .store_tx_ctrl_byte←1← AC63 BEQ |
| TYA ; A = control byte (Y) | |
| AC68 | LDY #1 ; Y=1: control byte offset |
| AC6A | STA (net_tx_ptr),y ; Store control byte |
| AC6C | TXA ; A = X (status) |
| AC6D | DEY ; Y=0 Y=&00 |
| AC6E | PHA ; Save status on stack |
| AC6F | .loop_wait_disc_tx_ack←1← AC7B BCS |
| LDA #&7f ; Set status to &7F (waiting) | |
| AC71 | STA (net_tx_ptr),y ; Store at TX buffer offset 0 |
| AC73 | JSR wait_net_tx_ack ; Wait for TX acknowledgement |
| AC76 | PLA ; Restore status |
| AC77 | PHA ; Keep on stack for next check |
| AC78 | EOR (net_tx_ptr),y ; Compare with current TX buffer |
| AC7A | ROR ; Rotate result bit 0 to carry |
| AC7B | BCS loop_wait_disc_tx_ack ; C=1: status changed, retry |
| AC7D | PLA ; Done: discard status |
| AC7E | PLA ; Discard disconnect code |
| AC7F | RTS ; Return |
| ; Spool TX control block template | |
| ; 12-byte TXCB template copied directly (no | |
| ; marker processing) to workspace at offset | |
| ; &21..&2C. Destination station and network | |
| ; (&23/&24) are filled in from (nfs_workspace) | |
| ; after the copy. | |
| AC80 | .tx_econet_txcb_template←1← AB89 LDA |
| EQUB &80 ; ctrl=&80 (standard TX) | |
| AC81 | EQUB &9F ; port=&9F |
| AC82 | EQUB &00 ; dest station=&00 (filled later) |
| AC83 | EQUB &00 ; dest network=&00 (filled later) |
| AC84 | EQUB &59 |
| AC85 | EQUB &8E ; buf start hi=&8E |
| AC86 | EQUB &FF ; buf start ext lo=&FF |
| AC87 | EQUB &FF ; buf start ext hi=&FF |
| AC88 | EQUB &61 |
| AC89 | EQUB &8E ; buf end hi=&8E |
| AC8A | EQUB &FF ; buf end ext lo=&FF |
| AC8B | 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. | |
| AC8C | .rx_palette_txcb_template←1← ABA8 LDA |
| EQUB &7F ; ctrl=&7F (RX listen) | |
| AC8D | EQUB &9E ; port=&9E |
| AC8E | EQUB &FD ; skip: preserve dest station |
| AC8F | EQUB &FD |
| AC90 | EQUB &2D |
| AC91 | EQUB &FC ; buf start hi=page ptr (&FC) |
| AC92 | EQUB &FF ; buf start ext lo=&FF |
| AC93 | EQUB &FF ; buf start ext hi=&FF |
| AC94 | EQUB &30 |
| AC95 | EQUB &FC ; buf end hi=page ptr (&FC) |
| AC96 | EQUB &FF ; buf end ext lo=&FF |
| AC97 | EQUB &FF ; buf end ext hi=&FF |
| AC98 | .lang_2_save_palette_vdu |
| LDA osword_flag ; Save l00ad counter | |
| AC9A | PHA ; Push for later restore |
| AC9B | LDA #&e9 ; Set workspace low to &E9 |
| AC9D | STA nfs_workspace ; Store to nfs_workspace low |
| AC9F | LDY #0 ; Y=0: initial palette index |
| ACA1 | STY osword_flag ; Clear palette counter |
| ACA3 | LDA vdu_screen_mode ; Load current screen mode |
| ACA6 | STA (nfs_workspace),y ; Store mode to workspace |
| ACA8 | INC nfs_workspace ; Advance workspace ptr |
| ACAA | LDA vdu_display_start_hi ; Load video ULA copy |
| ACAD | PHA ; Save for later restore |
| ACAE | TYA ; A=0 for first palette entry A=&00 |
| ACAF | .loop_read_palette←1← ACCE BNE |
| STA (nfs_workspace),y ; Store logical colour to workspace | |
| ACB1 | LDX nfs_workspace ; X = workspace ptr low |
| ACB3 | LDY nfs_workspace_hi ; Y = workspace ptr high |
| ACB5 | LDA #osword_read_palette ; OSWORD &0B: read palette |
| ACB7 | JSR osword ; Read palette entry Read palette |
| ACBA | PLA ; Restore previous ULA value |
| ACBB | LDY #0 ; Y=0: reset index |
| ACBD | STA (nfs_workspace),y ; Store ULA value to workspace |
| ACBF | INY ; Y=1: physical colour offset Y=&01 |
| ACC0 | LDA (nfs_workspace),y ; Load physical colour |
| ACC2 | PHA ; Save for next iteration |
| ACC3 | LDX nfs_workspace ; X = workspace ptr |
| ACC5 | INC nfs_workspace ; Advance workspace ptr |
| ACC7 | INC osword_flag ; Advance palette counter |
| ACC9 | DEY ; Y=0 Y=&00 |
| ACCA | LDA osword_flag ; Load counter |
| ACCC | CPX #&f9 ; Reached &F9 workspace limit? |
| ACCE | BNE loop_read_palette ; No: read next palette entry |
| ACD0 | PLA ; Discard last ULA value |
| ACD1 | STY osword_flag ; Clear counter |
| ACD3 | INC nfs_workspace ; Advance workspace ptr |
| ACD5 | JSR serialise_palette_entry ; Store extra palette info |
| ACD8 | INC nfs_workspace ; Advance workspace ptr again |
| ACDA | PLA ; Restore original l00ad |
| ACDB | STA osword_flag ; 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. |
|
| ACDD | .commit_state_byte←4← 9586 JMP← 95AE JSR← 95D5 JSR← A62B JMP |
| LDA ws_0d69 ; Load current state | |
| ACE0 | STA ws_0d68 ; Store as committed state |
| ACE3 | 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. |
|
| ACE4 | .serialise_palette_entry←1← ACD5 JSR |
| LDA vdu_mode ; Load palette register value | |
| ACE7 | STA (nfs_workspace),y ; Store to workspace |
| ACE9 | LDX vdu_mode ; X = palette register |
| ACEC | JSR read_osbyte_to_ws ; Read OSBYTE for this mode |
| ACEF | INC nfs_workspace ; Advance workspace ptr |
| ACF1 | TYA ; A=0 |
| ACF2 | STA (nfs_workspace,x) ; Store zero to workspace |
| ACF4 | 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. |
|
| ACF7 | .read_osbyte_to_ws_x0←1← ACF4 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. |
|
| ACF9 | .read_osbyte_to_ws←1← ACEC JSR |
| LDY osword_flag ; Load OSBYTE code index | |
| ACFB | INC osword_flag ; Advance index counter |
| ACFD | INC nfs_workspace ; Advance workspace ptr |
| ACFF | LDA osbyte_mode_read_codes,y ; Load OSBYTE number from table |
| AD02 | LDY #&ff ; Y=&FF: read current value |
| AD04 | JSR osbyte ; Call OSBYTE to read value |
| AD07 | TXA ; A = result (from X) |
| AD08 | LDX #0 ; X=0 for indexed store |
| AD0A | STA (nfs_workspace,x) ; Store result to workspace |
| AD0C | 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. | |
| AD0D | .osbyte_mode_read_codes←1← ACFF LDA |
| EQUB &84 | |
| AD0E | EQUB &C2 ; OSBYTE &C2: read video ULA ctrl |
| AD0F | 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.
|
||||
| AD10 | .cmd_cdir | |||
| TYA ; Save command line offset | ||||
| AD11 | PHA ; Push onto stack | |||
| AD12 | JSR mask_owner_access ; Set owner-only access mask | |||
| AD15 | JSR skip_to_next_arg ; Skip to optional size argument | |||
| AD18 | CMP #&0d ; End of line? | |||
| AD1A | BNE parse_cdir_size ; No: parse size argument | |||
| AD1C | LDX #2 ; Default allocation size index = 2 | |||
| AD1E | BNE done_cdir_size ; ALWAYS branch | |||
| AD20 | .parse_cdir_size←1← AD1A BNE | |||
| LDA #&ff ; A=&FF: mark as decimal parse | ||||
| AD22 | STA fs_work_4 ; Store decimal parse flag | |||
| AD24 | JSR parse_addr_arg ; Parse numeric size argument | |||
| AD27 | LDX #&1b ; X=&1B: top of 26-entry size table | |||
| AD29 | .loop_find_alloc_size←1← AD2D BCC | |||
| DEX ; Try next lower index | ||||
| AD2A | CMP cdir_alloc_size_table,x ; Compare size with threshold | |||
| AD2D | BCC loop_find_alloc_size ; A < threshold: keep searching | |||
| AD2F | .done_cdir_size←1← AD1E BNE | |||
| STX fs_cmd_data ; Store allocation size index | ||||
| AD32 | PLA ; Restore command line offset | |||
| AD33 | TAY ; Transfer to Y | |||
| AD34 | JSR save_ptr_to_os_text ; Save text pointer for filename parse | |||
| AD37 | JSR parse_filename_arg ; Parse directory name argument | |||
| AD3A | LDX #1 ; X=1: one argument to copy | |||
| AD3C | JSR copy_arg_to_buf ; Copy directory name to TX buffer | |||
| AD3F | LDY #&1b ; Y=&1B: *CDir FS command code | |||
| AD41 | .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 (cdir_dispatch_col+2) overlaps with | ||||
| ; the JMP high byte (entry 0, 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. | ||||
| AD44 | EQUB &00 ; Index 1: threshold 0 (catch-all) | |||
| AD45 | EQUB &0A ; Index 2: threshold 10 (default) | |||
| AD46 | EQUB &14 ; Index 3: threshold 20 | |||
| AD47 | EQUB &1D ; Index 4: threshold 29 | |||
| AD48 | EQUB &27 ; Index 5: threshold 39 | |||
| AD49 | EQUB &31 ; Index 6: threshold 49 | |||
| AD4A | EQUB &3B ; Index 7: threshold 59 | |||
| AD4B | EQUB &45 ; Index 8: threshold 69 | |||
| AD4C | EQUB &4F ; Index 9: threshold 79 | |||
| AD4D | EQUB &58 ; Index 10: threshold 88 | |||
| AD4E | EQUB &62 ; Index 11: threshold 98 | |||
| AD4F | EQUB &6C ; Index 12: threshold 108 | |||
| AD50 | EQUB &76 ; Index 13: threshold 118 | |||
| AD51 | EQUB &80 ; Index 14: threshold 128 | |||
| AD52 | EQUB &8A ; Index 15: threshold 138 | |||
| AD53 | EQUB &94 ; Index 16: threshold 148 | |||
| AD54 | EQUB &9D ; Index 17: threshold 157 | |||
| AD55 | EQUB &A7 ; Index 18: threshold 167 | |||
| AD56 | EQUB &B1 ; Index 19: threshold 177 | |||
| AD57 | EQUB &BB ; Index 20: threshold 187 | |||
| AD58 | EQUB &C5 ; Index 21: threshold 197 | |||
| AD59 | EQUB &CF ; Index 22: threshold 207 | |||
| AD5A | EQUB &D8 ; Index 23: threshold 216 | |||
| AD5B | EQUB &E2 ; Index 24: threshold 226 | |||
| AD5C | EQUB &EC ; Index 25: threshold 236 | |||
| AD5D | EQUB &F6 ; Index 26: threshold 246 | |||
| AD5E | 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.
|
||||
| AD5F | .cmd_lcat | |||
| ROR fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| AD62 | SEC ; Set carry (= library directory) | |||
| AD63 | 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.
|
||||
| AD65 | .cmd_lex | |||
| ROR fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| AD68 | SEC ; Set carry (= library directory) | |||
| AD69 | 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.
|
||||
| AD6B | .cmd_ex | |||
| ROR fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| AD6E | CLC ; Clear carry (= current directory) | |||
| AD6F | .ex_set_lib_flag←1← AD69 BCS | |||
| ROL fs_lib_flags ; Rotate carry back, clearing bit 7 | ||||
| AD72 | LDA #&ff ; A=&FF: initial column counter | |||
| AD74 | STA fs_spool_handle ; Store column counter | |||
| AD76 | LDA #1 ; One entry per line (Ex format) | |||
| AD78 | STA fs_work_7 ; Store entries per page | |||
| AD7A | LDA #3 ; FS command code 3: Examine | |||
| AD7C | STA fs_work_5 ; Store command code | |||
| AD7E | BNE setup_ex_request ; ALWAYS branch | |||
| AD80 | .fscv_5_cat | |||
| JSR set_xfer_params ; Set transfer parameters | ||||
| AD83 | LDY #0 ; Y=0: start from entry 0 | |||
| AD85 | ROR fs_lib_flags ; Rotate carry into lib flag | |||
| AD88 | CLC ; Clear carry (= current directory) | |||
| AD89 | .cat_set_lib_flag←1← AD63 BCS | |||
| ROL fs_lib_flags ; Rotate carry back, clearing bit 7 | ||||
| AD8C | LDA #3 ; Three entries per column (Cat) | |||
| AD8E | STA fs_spool_handle ; Store column counter | |||
| AD90 | STA fs_work_7 ; Store entries per page | |||
| AD92 | LDA #&0b ; FS command code &0B: Catalogue | |||
| AD94 | STA fs_work_5 ; Store command code | |||
| AD96 | .setup_ex_request←1← AD7E BNE | |||
| JSR save_ptr_to_os_text ; Save text pointer | ||||
| AD99 | LDA #&ff ; A=&FF: enable escape checking | |||
| AD9B | STA need_release_tube ; Set escapable flag | |||
| AD9D | LDA #6 ; Command code 6 | |||
| AD9F | STA fs_cmd_data ; Store in TX buffer | |||
| ADA2 | JSR parse_filename_arg ; Parse directory argument | |||
| ADA5 | LDX #1 ; X=1: offset in buffer | |||
| ADA7 | JSR copy_arg_to_buf ; Copy argument to TX buffer | |||
| ADAA | LDA fs_lib_flags ; Get library/FS flags | |||
| ADAD | LSR ; Shift bit 0 to carry | |||
| ADAE | BCC store_owner_flags ; Bit 0 clear: skip | |||
| ADB0 | ORA #&40 ; Set bit 6 (owner access flag) | |||
| ADB2 | .store_owner_flags←1← ADAE BCC | |||
| ROL ; Rotate back Get cycle number | ||||
| ADB3 | STA fs_lib_flags ; Store modified flags | |||
| ADB6 | LDY #&12 ; Y=&12: FS command for examine | |||
| ADB8 | JSR save_net_tx_cb ; Send request to file server Print ') ' | |||
| ADBB | LDX #3 ; X=3: offset to directory title | |||
| ADBD | JSR print_10_chars ; Print directory title (10 chars) | |||
| ADC0 | JSR print_inline ; Print '(' | |||
| ADC3 | EQUS "(" | |||
| ADC4 | LDA fs_reply_stn | |||
| ADC7 | JSR print_decimal_3dig | |||
| ADCA | JSR print_inline | |||
| ADCD | EQUS ") " | |||
| ADD3 | LDY fs_access_level ; Get owner/public flag | |||
| ADD6 | BNE print_public_label ; Non-zero: public access | |||
| ADD8 | JSR print_inline ; Print 'Owner' + CR | |||
| ADDB | EQUS "Owner." | |||
| ADE1 | BNE send_dir_info_req ; Skip public; ALWAYS branch | |||
| ADE3 | .print_public_label←1← ADD6 BNE | |||
| JSR print_inline ; Print 'Public' + CR | ||||
| ADE6 | EQUS "Public." | |||
| ADED | .send_dir_info_req←1← ADE1 BNE | |||
| LDA fs_lib_flags ; Get flags | ||||
| ADF0 | PHA ; Save flags | |||
| ADF1 | JSR mask_owner_access ; Mask owner access bits | |||
| ADF4 | LDY #&15 ; Y=&15: FS command for dir info | |||
| ADF6 | JSR save_net_tx_cb ; Send request to file server | |||
| ADF9 | INX ; Advance X past header | |||
| ADFA | LDY #&10 ; Y=&10: print 16 chars | |||
| ADFC | JSR print_chars_from_buf ; Print file entry | |||
| ADFF | JSR print_inline ; Print ' Option ' | |||
| AE02 | EQUS " Option " | |||
| AE0D | LDA fs_boot_option ; Get option byte | |||
| AE10 | TAX ; Transfer to X for table lookup | |||
| AE11 | JSR print_hex_byte ; Print option as hex | |||
| AE14 | JSR print_inline ; Print ' (' | |||
| AE17 | EQUS " (" | |||
| AE19 | LDY option_str_offset_data,x ; Y=string offset for boot option X | |||
| AE1C | .loop_print_option_str←1← AE25 BNE | |||
| LDA roff_off_string,y ; Load option description character | ||||
| AE1F | BMI print_dir_header ; Bit 7 set: end of option string | |||
| AE21 | JSR osasci ; Write character | |||
| AE24 | INY | |||
| AE25 | BNE loop_print_option_str | |||
| AE27 | .print_dir_header←1← AE1F BMI | |||
| JSR print_inline | ||||
| AE2A | EQUS ").Dir. " | |||
| AE31 | LDX #&11 ; Offset &11: directory name | |||
| AE33 | JSR print_10_chars ; Print directory name (10 chars) | |||
| AE36 | JSR print_inline ; Print ' Lib. ' | |||
| AE39 | EQUS " Lib. " ; Print ' Lib. ' | |||
| AE43 | LDX #&1b ; Offset &1B: library name | |||
| AE45 | JSR print_10_chars ; Print library name (10 chars) | |||
| AE48 | JSR osnewl ; Write newline (characters 10 and 13) | |||
| AE4B | PLA ; Restore flags | |||
| AE4C | STA fs_lib_flags ; Store restored flags | |||
| AE4F | .setup_ex_pagination←1← AE80 BNE | |||
| STY fs_func_code ; Store entry count | ||||
| AE52 | STY fs_work_4 ; Also store in work_4 | |||
| AE54 | LDX fs_work_5 ; Get command code | |||
| AE56 | STX fs_data_count ; Store in buffer | |||
| AE59 | LDX fs_work_7 ; Get entries per page | |||
| AE5B | STX fs_cmd_data ; Store in buffer | |||
| AE5E | LDX #3 ; X=3: buffer offset | |||
| AE60 | JSR copy_arg_to_buf ; Copy argument to buffer | |||
| AE63 | LDY #3 ; Y=3: FS command for examine/cat | |||
| AE65 | JSR save_net_tx_cb ; Send request to file server | |||
| AE68 | INX ; Advance past header | |||
| AE69 | LDA fs_cmd_data ; Get number of entries returned | |||
| AE6C | BEQ jmp_osnewl ; Zero: no more entries | |||
| AE6E | PHA ; Save entry count | |||
| AE6F | .loop_scan_entry_data←1← AE73 BPL | |||
| INY ; Advance Y | ||||
| AE70 | LDA fs_cmd_data,y ; Get entry data byte | |||
| AE73 | BPL loop_scan_entry_data ; Bit 7 clear: more data | |||
| AE75 | STA fs_cmd_lib,y ; Store terminator byte | |||
| AE78 | JSR ex_print_col_sep ; Print entry with column separator | |||
| AE7B | PLA ; Restore entry count | |||
| AE7C | CLC ; Clear carry for addition | |||
| AE7D | ADC fs_work_4 ; Add entries processed | |||
| AE7F | TAY ; Transfer to Y | |||
| AE80 | 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.
|
||||
| AE82 | .print_10_chars←3← ADBD JSR← AE33 JSR← AE45 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.
|
||||||
| AE84 | .print_chars_from_buf←2← ADFC JSR← AE8C BNE | |||||
| LDA fs_cmd_data,x ; Get character from buffer | ||||||
| AE87 | JSR osasci ; Write character | |||||
| AE8A | INX ; Next buffer position | |||||
| AE8B | DEY ; Decrement count | |||||
| AE8C | BNE print_chars_from_buf ; Loop until 10 printed | |||||
| AE8E | RTS ; Return | |||||
| AE8F | .jmp_osnewl←1← AE6C 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. |
|
| AE92 | .parse_cmd_arg_y0←2← 9D05 JSR← A1C7 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. |
|
| AE94 | .parse_filename_arg←4← AD37 JSR← ADA2 JSR← AF77 JSR← B363 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. |
|
| AE97 | .parse_access_prefix←4← 92EE JSR← 9396 JSR← 93CF JSR← 993E JSR |
| LDA fs_filename_buf ; Get first parsed character | |
| AE9A | EOR #&26 ; Is it '&'? |
| AE9C | BNE check_colon_prefix ; No: check for ':' prefix |
| AE9E | LDA fs_lib_flags ; Get flags |
| AEA1 | ORA #&40 ; Set FS selection flag (bit 6) |
| AEA3 | STA fs_lib_flags ; Store updated flags |
| AEA6 | JSR strip_token_prefix ; Remove '&' prefix character |
| AEA9 | LDA fs_filename_buf ; Get next character |
| AEAC | EOR #&2e ; Is it '.'? |
| AEAE | BNE check_hash_prefix ; No: check for '#' |
| AEB0 | LDA fs_filename_buf_1 ; Get char after '.' |
| AEB3 | EOR #&0d ; Is it CR (end of line)? |
| AEB5 | 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 ':'. |
|
| AEB7 | .strip_token_prefix←5← 931C JSR← 93B6 JSR← 93BC JSR← AEA6 JSR← AEF9 BNE |
| TXA ; Save X | |
| AEB8 | PHA ; Push X |
| AEB9 | LDX #&ff ; X=&FF, will increment to 0 |
| AEBB | .loop_shift_str_left←1← AEC4 BNE |
| INX ; Increment X | |
| AEBC | LDA fs_filename_buf_1,x ; Get character at offset+1 |
| AEBF | STA fs_filename_buf,x ; Store at offset (shift left) |
| AEC2 | EOR #&0d ; Is it CR (end of line)? |
| AEC4 | BNE loop_shift_str_left ; No: continue shifting |
| AEC6 | TXA ; Get shifted string length |
| AEC7 | BEQ done_strip_prefix ; Zero length: skip trailing trim |
| AEC9 | .loop_trim_trailing←1← AED6 BNE |
| LDA fs_filename_buf_m1,x ; Get character at end of string | |
| AECC | EOR #&20 ; Is it a space? |
| AECE | BNE done_strip_prefix ; No: done trimming |
| AED0 | LDA #&0d ; Replace trailing space with CR |
| AED2 | STA fs_filename_buf_m1,x ; Store CR |
| AED5 | DEX ; Move back |
| AED6 | BNE loop_trim_trailing ; Loop while more trailing spaces |
| AED8 | .done_strip_prefix←2← AEC7 BEQ← AECE BNE |
| PLA ; Restore X | |
| AED9 | TAX ; Transfer back to X |
| AEDA | .return_from_strip_prefix←3← AEDD BEQ← AEE4 BNE← AEEF BNE |
| RTS ; Return | |
| AEDB | .check_hash_prefix←1← AEAE BNE |
| EOR #&23 ; Is it '#'? | |
| AEDD | BEQ return_from_strip_prefix ; Yes: '#' prefix accepted |
| AEDF | .error_bad_prefix←2← AEB5 BEQ← AF12 BEQ |
| JMP error_bad_filename ; Bad filename error | |
| AEE2 | .check_colon_prefix←1← AE9C BNE |
| EOR #&1c ; Check for ':' prefix | |
| AEE4 | BNE return_from_strip_prefix ; Neither '&' nor ':': no prefix |
| AEE6 | LDA fs_filename_buf_2 ; Get character after ':' |
| AEE9 | EOR #&2e ; Is it '.'? |
| AEEB | BEQ set_fs_select_flag ; Yes: ':.' qualified prefix |
| AEED | EOR #&23 ; Is it CR (end of line)? |
| AEEF | BNE return_from_strip_prefix ; No: no FS prefix, return |
| AEF1 | .set_fs_select_flag←1← AEEB BEQ |
| LDA fs_lib_flags ; Get flags | |
| AEF4 | ORA #&40 ; Set FS selection flag (bit 6) |
| AEF6 | STA fs_lib_flags ; Store updated flags |
| AEF9 | BNE strip_token_prefix ; ALWAYS branch |
| AEFB | .option_str_offset_data←1← AE19 LDY |
| BRK ; Data: option string offset table | |
| AEFC | EQUS "/<c" |
| AEFF | .roff_off_string←1← AE1C 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. |
|
| AF02 | .copy_arg_to_buf_x0←6← 8DBC JSR← 8E20 JSR← 994C JSR← 9B3C JSR← A299 JSR← AF7A 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. |
|
| AF04 | .copy_arg_to_buf←10← 99FE JSR← 9B35 JSR← 9B58 JSR← 9D0A JSR← A1CC JSR← A1F9 JSR← AD3C JSR← ADA7 JSR← AE60 JSR← B378 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.
|
||||||||
| AF06 | .copy_arg_validated←3← 8DB7 JSR← 941E JSR← 9454 JSR | |||||||
| SEC ; Set carry: enable '&' validation | ||||||||
| AF07 | .loop_copy_char←1← AF1A BNE | |||||||
| LDA (fs_crc_lo),y ; Get character from command line | ||||||||
| AF09 | STA fs_cmd_data,x ; Store in TX buffer | |||||||
| AF0C | BCC advance_positions ; Carry clear: skip validation | |||||||
| AF0E | CMP #&21 ; Is it '!' or above? | |||||||
| AF10 | EOR #&26 ; Is it '&'? | |||||||
| AF12 | BEQ error_bad_prefix ; Yes: '&' not allowed in filenames | |||||||
| AF14 | .restore_after_check | |||||||
| EOR #&26 ; '&' in filename: bad filename Restore A (undo '&' EOR) | ||||||||
| AF16 | .advance_positions←1← AF0C BCC | |||||||
| INX ; Advance buffer position | ||||||||
| AF17 | INY ; Advance source position | |||||||
| AF18 | EOR #&0d ; Is it CR (end of line)? | |||||||
| AF1A | BNE loop_copy_char ; No: continue copying | |||||||
| AF1C | .loop_caf1c←1← AF29 BNE | |||||||
| LDA fs_cmd_csd,x ; Load character from end of buffer | ||||||||
| AF1F | EOR #&20 ; Test for space (&20) | |||||||
| AF21 | BNE done_trim_spaces ; Not a space: done trimming | |||||||
| AF23 | DEX ; Back up one position | |||||||
| AF24 | LDA #&0d ; CR terminator | |||||||
| AF26 | STA fs_cmd_lib,x ; Replace trailing space with CR | |||||||
| AF29 | BNE loop_caf1c ; ALWAYS: trim next character back ALWAYS branch | |||||||
| AF2B | .done_trim_spaces←1← AF21 BNE | |||||||
| LDA #0 ; A=0: success return code | ||||||||
| AF2D | .return_from_copy_arg←1← AF43 BMI | |||||||
| RTS ; Return | ||||||||
| AF2E | 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. |
|
| AF32 | .mask_owner_access←13← 8E3B JSR← 9390 JSR← 93CA JSR← 993B JSR← 9D55 JSR← 9E36 JSR← A1C4 JSR← A250 JSR← A25A JSR← A8D0 JSR← AD12 JSR← ADF1 JSR← B359 JSR |
| LDA fs_lib_flags ; Get flags | |
| AF35 | AND #&1f ; Mask to low 5 bits only |
| AF37 | STA fs_lib_flags ; Store masked flags |
| AF3A | RTS ; Return |
| AF3B | EQUS "Run" |
| AF3E | .fsreply_0_print_dir |
| LDX #0 ; X=0: start from first entry | |
| AF40 | .loop_scan_entries←1← AF60 BNE |
| LDA fs_cmd_data,x ; Get entry byte from buffer | |
| AF43 | BMI return_from_copy_arg ; High bit set: end of entries |
| AF45 | 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. |
|
| AF47 | .ex_print_col_sep←1← AE78 JSR |
| LDY fs_spool_handle ; Get column counter | |
| AF49 | BMI print_col_newline ; Negative: newline mode (Ex) |
| AF4B | INY ; Increment column counter |
| AF4C | TYA ; Transfer to A |
| AF4D | AND #3 ; Modulo 4 (Cat: 3 per row) |
| AF4F | STA fs_spool_handle ; Store updated counter |
| AF51 | BEQ print_col_newline ; Zero: row full, print newline |
| AF53 | JSR print_inline ; Print ' ' column separator |
| AF56 | EQUS " " |
| AF58 | BNE next_col_entry ; Skip newline; ALWAYS branch |
| AF5A | .print_col_newline←2← AF49 BMI← AF51 BEQ |
| LDA #&0d ; CR character for newline | |
| AF5C | .print_entry_char←1← AF45 BNE |
| JSR osasci ; Write character 13 | |
| AF5F | .next_col_entry←1← AF58 BNE |
| INX ; Advance to next entry | |
| AF60 | BNE loop_scan_entries ; Loop for more entries |
| AF62 | EOR zp_0078 ; Embedded string data 'Exec' |
| AF64 | 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.
|
||||
| AF66 | .cmd_remove | |||
| TYA ; Save command line offset | ||||
| AF67 | PHA ; Push onto stack | |||
| AF68 | JSR skip_to_next_arg ; Skip to check for extra arguments | |||
| AF6B | CMP #&0d ; End of line? | |||
| AF6D | BEQ done_extra_arg_check ; Yes: single arg, proceed | |||
| AF6F | JMP error_syntax ; No: extra args, syntax error | |||
| AF72 | .done_extra_arg_check←1← AF6D BEQ | |||
| PLA ; Restore command line offset | ||||
| AF73 | TAY ; Transfer to Y | |||
| AF74 | JSR save_ptr_to_os_text ; Save text pointer for parsing | |||
| AF77 | JSR parse_filename_arg ; Parse filename argument | |||
| AF7A | JSR copy_arg_to_buf_x0 ; Copy filename to TX buffer | |||
| AF7D | LDY #&14 ; Y=&14: *Delete FS command code | |||
| AF7F | BIT bit_test_ff ; Set V flag (via BIT #&FF) | |||
| AF82 | 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.
|
||||
| AF85 | .print_num_no_leading←1← 9007 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.
|
||||||
| AF88 | .print_decimal_3dig←3← ADC7 JSR← B19D JSR← B1B4 JMP | |||||
| TAY ; Transfer value to Y (remainder) | ||||||
| AF89 | LDA #&64 ; A=100: hundreds divisor | |||||
| AF8B | JSR print_decimal_digit ; Print hundreds digit | |||||
| AF8E | LDA #&0a ; A=10: tens divisor | |||||
| AF90 | JSR print_decimal_digit ; Print tens digit | |||||
| AF93 | CLV ; Clear V (always print units) | |||||
| AF94 | 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'.
|
|||||||||
| AF96 | .print_decimal_digit←2← AF8B JSR← AF90 JSR | ||||||||
| STA fs_error_ptr ; Store divisor | |||||||||
| AF98 | TYA ; Get remaining value | ||||||||
| AF99 | LDX #&2f ; X='0'-1: digit counter | ||||||||
| AF9B | SEC ; Set carry for subtraction | ||||||||
| AF9C | PHP ; Save V flag for leading zero check | ||||||||
| AF9D | .loop_divide_digit←1← AFA0 BCS | ||||||||
| INX ; Count quotient digit | |||||||||
| AF9E | SBC fs_error_ptr ; Subtract divisor | ||||||||
| AFA0 | BCS loop_divide_digit ; No underflow: continue dividing | ||||||||
| AFA2 | ADC fs_error_ptr ; Add back divisor (get remainder) | ||||||||
| AFA4 | TAY ; Remainder to Y for next digit | ||||||||
| AFA5 | TXA ; Digit character to A | ||||||||
| AFA6 | PLP ; Restore V flag | ||||||||
| AFA7 | BVC print_nonzero_digit ; V clear: always print digit | ||||||||
| AFA9 | CMP #&30 ; V set: is digit '0'? | ||||||||
| AFAB | BEQ return_from_print_digit ; Yes: suppress leading zero | ||||||||
| AFAD | .print_nonzero_digit←1← AFA7 BVC | ||||||||
| LDX fs_error_ptr ; Save divisor across OSASCI call | |||||||||
| AFAF | JSR osasci ; Write character | ||||||||
| AFB2 | STX fs_error_ptr ; Restore divisor | ||||||||
| AFB4 | .return_from_print_digit←1← AFAB BEQ | ||||||||
| RTS ; Return | |||||||||
| AFB5 | .save_ptr_to_os_text←8← 9D5B JSR← A1C1 JSR← AD34 JSR← AD96 JSR← AF74 JSR← B038 JSR← B216 JSR← B360 JSR |
| PHA ; Save A | |
| AFB6 | LDA fs_crc_lo ; Copy text pointer low byte |
| AFB8 | STA os_text_ptr ; To OS text pointer low |
| AFBA | LDA fs_crc_hi ; Copy text pointer high byte |
| AFBC | STA os_text_ptr_hi ; To OS text pointer high |
| AFBE | PLA ; Restore A |
| AFBF | RTS ; Return |
| AFC0 | .loop_advance_char←1← AFCB 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.
|
||||||
| AFC1 | .skip_to_next_arg←2← AD15 JSR← AF68 JSR | |||||
| LDA (fs_crc_lo),y ; Load char from command line | ||||||
| AFC3 | CMP #&20 ; Space? | |||||
| AFC5 | BEQ loop_skip_space_chars ; Yes: skip trailing spaces | |||||
| AFC7 | CMP #&0d ; CR (end of line)? | |||||
| AFC9 | BEQ return_from_skip_arg ; Yes: return (at end) | |||||
| AFCB | BNE loop_advance_char ; ALWAYS branch | |||||
| AFCD | .loop_skip_space_chars←2← AFC5 BEQ← AFD2 BEQ | |||||
| INY ; Advance past space | ||||||
| AFCE | LDA (fs_crc_lo),y ; Load next character | |||||
| AFD0 | CMP #&20 ; Still a space? | |||||
| AFD2 | BEQ loop_skip_space_chars ; Yes: skip multiple spaces | |||||
| AFD4 | .return_from_skip_arg←1← AFC9 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. |
|
| AFD5 | .save_ptr_to_spool_buf←2← AFFB JSR← B1D2 JSR |
| PHA ; Save A | |
| AFD6 | LDA fs_crc_lo ; Copy text pointer low byte |
| AFD8 | STA fs_options ; To spool buffer pointer low |
| AFDA | LDA fs_crc_hi ; Copy text pointer high byte |
| AFDC | STA fs_block_offset ; To spool buffer pointer high |
| AFDE | PLA ; Restore A |
| AFDF | 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. |
|
| AFE0 | .init_spool_drive←2← AFF8 JSR← B1C5 JSR |
| TYA ; Save Y | |
| AFE1 | PHA ; Push it |
| AFE2 | JSR get_ws_page ; Get workspace page number |
| AFE5 | STA addr_work ; Store as spool drive page high |
| AFE7 | PLA ; Restore Y |
| AFE8 | TAY ; Transfer to Y |
| AFE9 | LDA #0 ; A=0 |
| AFEB | STA work_ae ; Clear spool drive page low |
| AFED | 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.
|
||||
| AFEE | .cmd_ps | |||
| LDA #1 ; A=1: check printer ready | ||||
| AFF0 | BIT ws_0d6a ; Test printer server workspace flag | |||
| AFF3 | BNE done_ps_available ; Non-zero: printer available | |||
| AFF5 | JMP err_printer_busy ; Printer not available: error | |||
| AFF8 | .done_ps_available←1← AFF3 BNE | |||
| JSR init_spool_drive ; Initialise spool drive | ||||
| AFFB | JSR save_ptr_to_spool_buf ; Save pointer to spool buffer | |||
| AFFE | LDA (fs_options),y ; Get first argument character | |||
| B000 | CMP #&0d ; End of command line? | |||
| B002 | BEQ no_ps_name_given ; Yes: no argument given | |||
| B004 | CLV ; Clear V (= explicit PS name given) | |||
| B005 | JSR is_decimal_digit ; Is first char a decimal digit? | |||
| B008 | BCC save_ps_cmd_ptr ; Yes: station number, skip PS name | |||
| B00A | TYA ; PS name follows | |||
| B00B | PHA ; Save Y | |||
| B00C | JSR load_ps_server_addr ; Load PS server address | |||
| B00F | PLA ; Restore Y | |||
| B010 | TAY ; Back to Y register | |||
| B011 | JSR parse_fs_ps_args ; Parse FS/PS arguments | |||
| B014 | JMP store_ps_station_addr ; Jump to store station address | |||
Copy printer server template at offset &18Sets Y=&18 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. |
|
| B017 | .copy_ps_data_y1c←1← 8EF9 JSR |
| LDY #&18 ; 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 (&8E59). This 6502 trick reaches data 248 bytes past the base label using a single instruction. |
|
| B019 | .copy_ps_data←1← B1F8 JSR |
| LDX #&f8 ; X=&F8: offset into template | |
| B01B | .loop_copy_ps_tmpl←1← B022 BNE |
| LDA ps_template_base,x ; Get template byte | |
| B01E | STA (net_rx_ptr),y ; Store in RX buffer |
| B020 | INY ; Next destination offset |
| B021 | INX ; Next source offset |
| B022 | BNE loop_copy_ps_tmpl ; Loop until X wraps to 0 |
| B024 | RTS ; Return |
| B025 | .no_ps_name_given←1← B002 BEQ |
| BIT bit_test_ff ; Set V (= no explicit PS name) | |
| B028 | .save_ps_cmd_ptr←1← B008 BCC |
| STY ws_ptr_hi ; Save command line pointer | |
| B02A | BVS done_ps_name_parse ; V set: skip PS name parsing |
| B02C | LDX #6 ; Max 6 characters for PS name |
| B02E | LDY #&18 ; Buffer offset &1C for PS name |
| B030 | LDA #&20 ; Space character |
| B032 | .loop_pad_ps_name←1← B036 BNE |
| STA (net_rx_ptr),y ; Fill buffer with space | |
| B034 | INY ; Next position |
| B035 | DEX ; Count down |
| B036 | BNE loop_pad_ps_name ; Loop until 6 spaces filled |
| B038 | JSR save_ptr_to_os_text ; Save text pointer |
| B03B | LDY ws_ptr_hi ; Restore command line pointer |
| B03D | JSR gsinit ; Initialise string reading |
| B040 | BEQ done_ps_name_parse ; Empty string: skip to send |
| B042 | LDX #6 ; Max 6 characters |
| B044 | STY ws_ptr_hi ; Save updated string pointer |
| B046 | LDY #&18 ; Buffer offset for PS name |
| B048 | STY table_idx ; Save buffer position |
| B04A | .loop_read_ps_char←1← B056 BNE |
| LDY ws_ptr_hi ; Restore string pointer | |
| B04C | JSR gsread ; Read next character |
| B04F | STY ws_ptr_hi ; Save updated pointer |
| B051 | BCS done_ps_name_parse ; End of string: go to send |
| B053 | JSR store_char_uppercase ; Store char uppercased in buffer |
| B056 | BNE loop_read_ps_char ; Loop for more characters |
| B058 | .done_ps_name_parse←3← B02A BVS← B040 BEQ← B051 BCS |
| JSR reverse_ps_name_to_tx ; Copy reversed PS name to TX | |
| B05B | JSR send_net_packet ; Send PS status request |
| B05E | JSR pop_requeue_ps_scan ; Pop and requeue PS scan |
| B061 | JSR load_ps_server_addr ; Load PS server address |
| B064 | LDA #0 ; A=0 |
| B066 | TAX ; X=&00 |
| B067 | LDY #&20 ; Offset &24 in buffer |
| B069 | STA (net_rx_ptr),y ; Clear PS status byte |
| B06B | .loop_pop_ps_slot←1← B097 BNE |
| PLA ; Get slot offset from stack | |
| B06C | BEQ done_ps_scan ; Zero: all slots done |
| B06E | PHA ; Save slot offset |
| B06F | TAY ; Transfer to Y |
| B070 | LDA (nfs_workspace),y ; Read slot status |
| B072 | BPL done_ps_slot_mark ; Bit 7 clear: slot inactive |
| B074 | JSR advance_y_by_4 ; Advance Y by 4 (to status page) |
| B077 | LDA (nfs_workspace),y ; Read status page pointer |
| B079 | STA work_ae ; Store pointer low |
| B07B | LDA (work_ae,x) ; Read printer status byte |
| B07D | BEQ read_ps_station_addr ; Zero (idle): show station info |
| B07F | CMP #3 ; Status 3 (paused)? |
| B081 | BNE done_ps_slot_mark ; Non-zero (busy): skip |
| B083 | .read_ps_station_addr←1← B07D BEQ |
| DEY ; Back to network number | |
| B084 | LDA (nfs_workspace),y ; Read network number |
| B086 | STA fs_work_6 ; Store network number |
| B088 | DEY ; Back to station number |
| B089 | LDA (nfs_workspace),y ; Read station number |
| B08B | STA fs_work_5 ; Store station low |
| B08D | LDY #&20 ; Offset &24 in buffer |
| B08F | STA (net_rx_ptr),y ; Store ready station in buffer |
| B091 | .done_ps_slot_mark←2← B072 BPL← B081 BNE |
| PLA ; Retrieve slot offset | |
| B092 | TAY ; Transfer to Y |
| B093 | LDA #&3f ; Mark slot as processed (&3F) |
| B095 | STA (nfs_workspace),y ; Write marker to workspace |
| B097 | BNE loop_pop_ps_slot ; ALWAYS branch |
| B099 | .done_ps_scan←1← B06C BEQ |
| JSR print_printer_server_is ; Print 'Printer server is ' | |
| B09C | LDY #&20 ; Offset &24: PS station number |
| B09E | LDA (net_rx_ptr),y ; Get stored station number |
| B0A0 | BNE print_ps_now ; Non-zero: server changed |
| B0A2 | JSR print_inline ; Print 'still ' |
| B0A5 | EQUS "still " |
| B0AB | CLV ; Clear V |
| B0AC | BVC done_ps_status_msg ; ALWAYS branch |
| B0AE | .print_ps_now←1← B0A0 BNE |
| JSR print_inline ; Print 'now ' | |
| B0B1 | EQUS "now " |
| B0B5 | NOP ; Padding |
| B0B6 | .done_ps_status_msg←1← B0AC BVC |
| JSR print_fs_info_newline ; Workspace offset 2 | |
| B0B9 | .store_ps_station_addr←1← B014 JMP |
| LDY #2 ; Y=2: workspace offset for station | |
| B0BB | LDA fs_work_5 ; Get station low |
| B0BD | STA (nfs_workspace),y ; Store in workspace |
| B0BF | INY ; Y=&03 |
| B0C0 | LDA fs_work_6 ; Get network number |
| B0C2 | STA (nfs_workspace),y ; Store in workspace |
| B0C4 | 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. |
|
| B0C5 | .print_file_server_is←1← A09B JSR |
| JSR print_inline ; Print 'File' | |
| B0C8 | EQUS "File" |
| B0CC | CLV ; Clear V |
| B0CD | 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. |
|
| B0CF | .print_printer_server_is←2← B099 JSR← B240 JSR |
| JSR print_inline ; Print 'Printer' | |
| B0D2 | EQUS "Printer" |
| B0D9 | NOP ; Padding |
| B0DA | .print_server_is_suffix←1← B0CD BVC |
| JSR print_inline ; Print ' server is ' | |
| B0DD | EQUS " server is " |
| B0E8 | NOP ; Padding |
| B0E9 | RTS ; Return |
Load printer server address from workspaceReads the station and network bytes from workspace offsets 2 and 3 into the station/network variables. |
|
| B0EA | .load_ps_server_addr←4← B00C JSR← B061 JSR← B1E3 JSR← B243 JSR |
| LDY #2 ; Workspace offset 2 | |
| B0EC | LDA (nfs_workspace),y ; Read station low |
| B0EE | STA fs_work_5 ; Store station low |
| B0F0 | INY ; Y=&03 |
| B0F1 | LDA (nfs_workspace),y ; Read network number |
| B0F3 | STA fs_work_6 ; Store network number |
| B0F5 | 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. |
|
| B0F6 | .pop_requeue_ps_scan←2← B05E JSR← B23D JSR |
| PLA ; Pop return address low | |
| B0F7 | STA osword_flag ; Save return address low |
| B0F9 | PLA ; Pop return address high |
| B0FA | STA ws_ptr_lo ; Save return address high |
| B0FC | LDA #0 ; Push 0 as end-of-list marker |
| B0FE | PHA ; Push it |
| B0FF | LDA #&84 ; Start scanning from offset &84 |
| B101 | STA ws_ptr_hi ; Store scan position |
| B103 | LSR econet_flags ; Shift PS slot flags right |
| B106 | LDA #3 ; Counter: 3 PS slots |
| B108 | .loop_scan_ps_slots←1← B11A BNE |
| JSR byte_to_2bit_index ; Convert to 2-bit workspace index | |
| B10B | BCS done_ps_slot_scan ; Carry set: no more slots |
| B10D | LSR ; Shift right twice |
| B10E | LSR ; To get slot offset |
| B10F | TAX ; Transfer to X |
| B110 | LDA (nfs_workspace),y ; Read slot status byte |
| B112 | BEQ done_ps_slot_scan ; Zero: empty slot, done |
| B114 | CMP #&3f ; Is it processed marker (&3F)? |
| B116 | BEQ reinit_ps_slot ; Yes: re-initialise this slot |
| B118 | .skip_next_ps_slot←1← B141 JMP |
| INX ; Try next slot | |
| B119 | TXA ; Transfer slot index to A |
| B11A | BNE loop_scan_ps_slots ; Loop for more slots |
| B11C | .reinit_ps_slot←1← B116 BEQ |
| TYA ; Y = workspace offset of slot | |
| B11D | PHA ; Push slot offset for scan list |
| B11E | LDA #&7f ; Set active status (&7F) |
| B120 | STA (nfs_workspace),y ; Write status byte |
| B122 | INY ; Next byte |
| B123 | LDA #&9e ; Low byte: workspace page |
| B125 | STA (nfs_workspace),y ; Write workspace pointer low |
| B127 | LDA #0 ; A=0 |
| B129 | JSR write_two_bytes_inc_y ; Write two zero bytes + advance Y |
| B12C | LDA ws_ptr_hi ; Get current scan page |
| B12E | STA (nfs_workspace),y ; Write RX buffer page low |
| B130 | CLC ; Clear carry for addition |
| B131 | PHP ; Save processor status |
| B132 | ADC #3 ; Advance by 3 pages |
| B134 | PLP ; Restore processor status |
| B135 | STA ws_ptr_hi ; Update scan position |
| B137 | JSR write_ps_slot_byte_ff ; Write buffer page + &FF bytes |
| B13A | LDA ws_ptr_hi ; Get updated scan position |
| B13C | STA (nfs_workspace),y ; Write RX buffer page high |
| B13E | .write_ps_slot_hi_link |
| JSR write_ps_slot_byte_ff ; Write another page + &FF bytes | |
| B141 | JMP skip_next_ps_slot ; Continue scanning slots |
| B144 | .done_ps_slot_scan←2← B10B BCS← B112 BEQ |
| ASL econet_flags ; Shift PS slot flags back | |
| B147 | LDA ws_ptr_lo ; Restore return address high |
| B149 | PHA ; Push onto stack |
| B14A | LDA osword_flag ; Restore return address low |
| B14C | PHA ; Push onto stack |
| B14D | LDA #&0a ; Delay counter: 10 |
| B14F | TAY ; Y=&0a |
| B150 | TAX ; X=&0a |
| B151 | STA fs_work_4 ; Outer loop counter = 10 |
| B153 | .loop_ps_delay←3← B154 BNE← B157 BNE← B15B BNE |
| DEY ; Decrement Y (inner loop) | |
| B154 | BNE loop_ps_delay ; Inner loop: 10 iterations |
| B156 | DEX ; Decrement X (middle loop) |
| B157 | BNE loop_ps_delay ; Middle loop: 10 iterations |
| B159 | DEC fs_work_4 ; Decrement outer counter |
| B15B | BNE loop_ps_delay ; Outer loop: ~1000 delay cycles |
| B15D | 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. |
|
| B15E | .write_ps_slot_byte_ff←2← B137 JSR← B13E JSR |
| INY ; Advance Y | |
| B15F | LDA addr_work ; Get buffer page |
| B161 | STA (nfs_workspace),y ; Store in workspace |
| B163 | 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.
|
||||||
| B165 | .write_two_bytes_inc_y←1← B129 JSR | |||||
| INY ; Advance Y | ||||||
| B166 | STA (nfs_workspace),y ; Write byte to workspace | |||||
| B168 | INY ; Advance Y | |||||
| B169 | STA (nfs_workspace),y ; Write byte to workspace | |||||
| B16B | INY ; Advance Y | |||||
| B16C | 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. |
|
| B16D | .reverse_ps_name_to_tx←2← B058 JSR← B1CA JSR |
| LDY #&18 ; Start of PS name at offset &1C | |
| B16F | .loop_push_ps_name←1← B175 BNE |
| LDA (net_rx_ptr),y ; Load byte from RX buffer | |
| B171 | PHA ; Push to stack (for reversal) |
| B172 | INY ; Next source byte |
| B173 | CPY #&20 ; End of PS name field (&20)? |
| B175 | BNE loop_push_ps_name ; No: continue pushing |
| B177 | LDY #&17 ; End of TX name field at &1B |
| B179 | .loop_pop_ps_name←1← B17F BNE |
| PLA ; Pop byte (reversed order) | |
| B17A | STA (net_rx_ptr),y ; Store in RX buffer |
| B17C | DEY ; Previous position |
| B17D | CPY #&0f ; Start of TX field (&0F)? |
| B17F | BNE loop_pop_ps_name ; No: continue popping |
| B181 | LDA net_rx_ptr_hi ; Copy RX page to TX |
| B183 | STA net_tx_ptr_hi ; Set TX pointer high |
| B185 | LDA #&0c ; TX offset &10 |
| B187 | STA net_tx_ptr ; Set TX pointer low |
| B189 | LDY #3 ; Copy 4 header bytes |
| B18B | .loop_copy_tx_hdr←1← B191 BPL |
| LDA ps_tx_header_template,y ; Get header template byte | |
| B18E | STA (net_tx_ptr),y ; Store in TX buffer |
| B190 | DEY ; Previous byte |
| B191 | BPL loop_copy_tx_hdr ; Loop until all 4 copied |
| B193 | 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. | |
| B194 | .ps_tx_header_template←1← B18B LDA |
| EQUB &80 ; Control byte &80 (immediate TX) | |
| B195 | EQUB &9F ; Port &9F (printer server) |
| B196 | EQUB &FF ; Station &FF (any) |
| B197 | 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. |
|
| B198 | .print_station_addr←4← A0A1 JSR← B249 JSR← B281 JSR← B2D1 JSR |
| PHP ; Save V flag (controls padding) | |
| B199 | LDA fs_work_6 ; Get network number |
| B19B | BEQ skip_if_local_net ; Zero: no network prefix |
| B19D | JSR print_decimal_3dig ; Print network as 3 digits |
| B1A0 | LDA #&2e ; '.' separator |
| B1A2 | JSR osasci ; Write character 46 |
| B1A5 | BIT bit_test_ff ; Set V (suppress station padding) |
| B1A8 | .skip_if_local_net←1← B19B BEQ |
| BVS print_station_only ; V set: skip padding spaces | |
| B1AA | JSR print_inline ; Print 4 spaces (padding) |
| B1AD | EQUS " " |
| B1B1 | .print_station_only←1← B1A8 BVS |
| LDA fs_work_5 ; Get station number | |
| B1B3 | PLP ; Restore flags |
| B1B4 | 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 ; &B1B7. | |
| ; 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. | |
| B1B7 | .ps_slot_txcb_template |
| EQUB &80 ; Control byte &80 (immediate TX) | |
| B1B8 | EQUB &9F ; Port &9F (printer server) |
| B1B9 | EQUB &00 ; Station 0 (filled in later) |
| B1BA | EQUB &00 ; Network 0 (filled in later) |
| B1BB | EQUB &10 |
| B1BC | EQUB &00 ; Data buffer start hi (= rx page) |
| B1BD | EQUB &FF ; Data buffer end lo (placeholder) |
| B1BE | EQUB &FF ; Data buffer end hi (placeholder) |
| B1BF | EQUB &18 |
| B1C0 | EQUB &00 ; Reply buffer start hi (= rx page) |
| B1C1 | EQUB &FF ; Reply buffer end lo (placeholder) |
| B1C2 | 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.
|
||||
| B1C3 | .cmd_pollps | |||
| STY ws_ptr_hi ; Save command line pointer high | ||||
| B1C5 | JSR init_spool_drive ; Initialise spool/print drive | |||
| B1C8 | STA fs_work_4 ; Save spool drive number | |||
| B1CA | JSR reverse_ps_name_to_tx ; Copy PS name to TX buffer | |||
| B1CD | JSR init_ps_slot_from_rx ; Init PS slot from RX data | |||
| B1D0 | LDY ws_ptr_hi ; Restore command line pointer | |||
| B1D2 | JSR save_ptr_to_spool_buf ; Save pointer to spool buffer | |||
| B1D5 | LDA (fs_options),y ; Get first argument character | |||
| B1D7 | CMP #&0d ; End of command line? | |||
| B1D9 | BEQ no_poll_name_given ; Yes: no argument given | |||
| B1DB | CLV ; Clear V (= explicit PS name given) | |||
| B1DC | JSR is_decimal_digit ; Is first char a decimal digit? | |||
| B1DF | BCC skip_if_no_poll_arg ; Yes: station number, skip PS name | |||
| B1E1 | TYA ; PS name follows | |||
| B1E2 | PHA ; Save Y | |||
| B1E3 | JSR load_ps_server_addr ; Load PS server address | |||
| B1E6 | PLA ; Restore Y | |||
| B1E7 | TAY ; Back to Y register | |||
| B1E8 | JSR parse_fs_ps_args ; Parse FS/PS arguments | |||
| B1EB | LDY #&7a ; Offset &7A in slot buffer | |||
| B1ED | LDA fs_work_5 ; Get parsed station low | |||
| B1EF | STA (work_ae),y ; Store station number low | |||
| B1F1 | INY ; Y=&7b | |||
| B1F2 | LDA fs_work_6 ; Get parsed network number | |||
| B1F4 | STA (work_ae),y ; Store station number high | |||
| B1F6 | LDY #&10 ; Offset &14 in TX buffer | |||
| B1F8 | JSR copy_ps_data ; Copy PS data to TX buffer | |||
| B1FB | LDA addr_work ; Get buffer page high | |||
| B1FD | STA net_tx_ptr_hi ; Set TX pointer high byte | |||
| B1FF | LDA #&78 ; Offset &78 in buffer | |||
| B201 | STA net_tx_ptr ; Set TX pointer low byte | |||
| B203 | BNE done_poll_name_parse ; ALWAYS branch | |||
| B205 | .no_poll_name_given←1← B1D9 BEQ | |||
| BIT bit_test_ff ; Set V (= no explicit PS name) | ||||
| B208 | .skip_if_no_poll_arg←1← B1DF BCC | |||
| BVS done_poll_name_parse ; V set (no arg): skip to send | ||||
| B20A | LDX #6 ; Max 6 characters for PS name | |||
| B20C | LDY #&10 ; Buffer offset for PS name | |||
| B20E | LDA #&20 ; Space character | |||
| B210 | .loop_pad_poll_name←1← B214 BNE | |||
| STA (net_rx_ptr),y ; Fill buffer position with space | ||||
| B212 | INY ; Next position | |||
| B213 | DEX ; Count down | |||
| B214 | BNE loop_pad_poll_name ; Loop until 6 spaces filled | |||
| B216 | JSR save_ptr_to_os_text ; Save pointer to OS text | |||
| B219 | LDY ws_ptr_hi ; Restore command line pointer | |||
| B21B | JSR gsinit ; Initialise string reading | |||
| B21E | BEQ done_poll_name_parse ; Empty string: skip to send | |||
| B220 | LDX #6 ; Max 6 characters | |||
| B222 | STY ws_ptr_hi ; Save updated string pointer | |||
| B224 | LDY #&10 ; Buffer offset for PS name | |||
| B226 | STY table_idx ; Save buffer position | |||
| B228 | .loop_read_poll_char←1← B234 BNE | |||
| LDY ws_ptr_hi ; Restore string pointer | ||||
| B22A | JSR gsread ; Read next char from string | |||
| B22D | STY ws_ptr_hi ; Save updated string pointer | |||
| B22F | BCS done_poll_name_parse ; End of string: go to send | |||
| B231 | JSR store_char_uppercase ; Store char uppercased in buffer | |||
| B234 | BNE loop_read_poll_char ; Loop if more chars to copy | |||
| B236 | .done_poll_name_parse←4← B203 BNE← B208 BVS← B21E BEQ← B22F BCS | |||
| LDA #&80 ; Enable escape checking | ||||
| B238 | STA need_release_tube ; Set escapable flag | |||
| B23A | JSR send_net_packet ; Send the poll request packet | |||
| B23D | JSR pop_requeue_ps_scan ; Pop and requeue PS scan | |||
| B240 | JSR print_printer_server_is ; Print 'Printer server ' | |||
| B243 | JSR load_ps_server_addr ; Load PS server address | |||
| B246 | BIT bit_test_ff ; Set V and N flags | |||
| B249 | JSR print_station_addr ; Print station address | |||
| B24C | JSR print_inline ; Print ' "' | |||
| B24F | EQUS " "" | |||
| B251 | LDY #&18 ; Y=&18: name field offset in RX buffer | |||
| B253 | .loop_print_poll_name←1← B25F BNE | |||
| LDA (net_rx_ptr),y ; Get character from name field | ||||
| B255 | CMP #&20 ; Is it a space? | |||
| B257 | BEQ done_poll_name_print ; Yes: end of name | |||
| B259 | JSR osasci ; Write character | |||
| B25C | INY ; Next character | |||
| B25D | CPY #&1e ; Past end of name field? | |||
| B25F | BNE loop_print_poll_name ; No: continue printing name | |||
| B261 | .done_poll_name_print←1← B257 BEQ | |||
| JSR print_inline ; Print '"' + CR | ||||
| B264 | EQUS ""." | |||
| B266 | NOP ; Padding byte | |||
| B267 | .loop_pop_poll_slot←1← B2DD BNE | |||
| PLA ; Get slot offset from stack | ||||
| B268 | BEQ return_from_poll_slots ; Zero: all slots done, return | |||
| B26A | PHA ; Save slot offset | |||
| B26B | TAY ; Transfer to Y | |||
| B26C | LDA (nfs_workspace),y ; Read slot status byte | |||
| B26E | BPL done_poll_slot_mark ; Bit 7 clear: slot inactive | |||
| B270 | INY ; Advance to station number | |||
| B271 | INY ; Offset+2 in slot | |||
| B272 | LDA (nfs_workspace),y ; Read station number low | |||
| B274 | STA fs_work_5 ; Store station low | |||
| B276 | INY ; Next byte (offset+3) | |||
| B277 | LDA (nfs_workspace),y ; Read network number | |||
| B279 | STA fs_work_6 ; Store network number | |||
| B27B | INY ; Next byte (offset+4) | |||
| B27C | LDA (nfs_workspace),y ; Read status page pointer | |||
| B27E | STA work_ae ; Store pointer low | |||
| B280 | CLV ; Clear V flag | |||
| B281 | JSR print_station_addr ; Print station address (V=0) | |||
| B284 | JSR print_inline ; Print ' is ' | |||
| B287 | EQUS " is " | |||
| B28B | LDX #0 ; X=0 for indirect indexed access | |||
| B28D | LDA (work_ae,x) ; Read printer status byte | |||
| B28F | BNE check_poll_jammed ; Non-zero: not ready | |||
| B291 | JSR print_inline ; Print 'ready' | |||
| B294 | EQUS "ready" | |||
| B299 | CLV ; Clear V | |||
| B29A | BVC done_poll_status_line ; ALWAYS branch | |||
| B29C | .check_poll_jammed←1← B28F BNE | |||
| CMP #2 ; Status = 2? | ||||
| B29E | BNE check_poll_busy ; No: check for busy | |||
| B2A0 | .print_poll_jammed←1← B2AE BNE | |||
| JSR print_inline ; Print 'jammed' | ||||
| B2A3 | EQUS "jammed" | |||
| B2A9 | CLV ; Clear V | |||
| B2AA | BVC done_poll_status_line ; ALWAYS branch | |||
| B2AC | .check_poll_busy←1← B29E BNE | |||
| CMP #1 ; Status = 1? | ||||
| B2AE | BNE print_poll_jammed ; Not 1 or 2: default to jammed | |||
| B2B0 | JSR print_inline ; Print 'busy' | |||
| B2B3 | EQUS "busy" | |||
| B2B7 | INC work_ae ; Advance past status byte | |||
| B2B9 | LDA (work_ae,x) ; Read client station number | |||
| B2BB | STA fs_work_5 ; Store station low | |||
| B2BD | BEQ done_poll_status_line ; Zero: no client info, skip | |||
| B2BF | JSR print_inline ; Print ' with station ' | |||
| B2C2 | EQUS " with " | |||
| B2C8 | INC work_ae ; Advance pointer to network byte | |||
| B2CA | LDA (work_ae,x) ; Load client network number | |||
| B2CC | STA fs_work_6 ; Store network number | |||
| B2CE | BIT bit_test_ff ; Set V flag | |||
| B2D1 | JSR print_station_addr ; Print client station address | |||
| B2D4 | .done_poll_status_line←3← B29A BVC← B2AA BVC← B2BD BEQ | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| B2D7 | .done_poll_slot_mark←1← B26E BPL | |||
| PLA ; Retrieve slot offset | ||||
| B2D8 | TAY ; Transfer to Y | |||
| B2D9 | LDA #&3f ; Mark slot as processed (&3F) | |||
| B2DB | STA (nfs_workspace),y ; Write marker to workspace | |||
| B2DD | BNE loop_pop_poll_slot ; ALWAYS branch | |||
| B2DF | .return_from_poll_slots←1← B268 BEQ | |||
| RTS ; Return | ||||
Initialise PS slot buffer from template dataCopies the 12-byte ps_slot_txcb_template (&B1B7) 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. |
|
| B2E0 | .init_ps_slot_from_rx←1← B1CD JSR |
| LDY #&78 ; Start at offset &78 | |
| B2E2 | .loop_copy_slot_tmpl←1← B2F4 BNE |
| LDA write_ps_slot_link_addr,y ; Load template byte | |
| B2E5 | CPY #&7d ; At offset &7D? |
| B2E7 | BEQ subst_rx_page_byte ; Yes: substitute RX page |
| B2E9 | CPY #&81 ; At offset &81? |
| B2EB | BNE store_slot_tmpl_byte ; No: use template byte |
| B2ED | .subst_rx_page_byte←1← B2E7 BEQ |
| LDA net_rx_ptr_hi ; Use RX buffer page instead | |
| B2EF | .store_slot_tmpl_byte←1← B2EB BNE |
| STA (work_ae),y ; Store byte in slot buffer | |
| B2F1 | INY ; Next offset |
| B2F2 | CPY #&84 ; Past end of slot (&84)? |
| B2F4 | BNE loop_copy_slot_tmpl ; No: continue copying |
| B2F6 | 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.
|
||||
| B2F7 | .store_char_uppercase←2← B053 JSR← B231 JSR | |||
| LDY table_idx ; Y = current buffer position | ||||
| B2F9 | AND #&7f ; Strip high bit | |||
| B2FB | CMP #&61 ; Is it lowercase 'a' or above? | |||
| B2FD | BCC done_uppercase_store ; Below 'a': not lowercase | |||
| B2FF | CMP #&7b ; Above 'z'? | |||
| B301 | BCS done_uppercase_store ; Yes: not lowercase | |||
| B303 | AND #&5f ; Convert to uppercase | |||
| B305 | .done_uppercase_store←2← B2FD BCC← B301 BCS | |||
| STA (net_rx_ptr),y ; Store in RX buffer | ||||
| B307 | INY ; Next buffer position | |||
| B308 | STY table_idx ; Update buffer position | |||
| B30A | DEX ; Decrement character count | |||
| B30B | 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.
|
||||
| B30C | .cmd_prot | |||
| LDA (fs_crc_lo),y ; Get next char from command line | ||||
| B30E | EOR #&0d ; Compare with CR (end of line) | |||
| B310 | BNE parse_prot_keywords ; Not CR: attribute keywords follow | |||
| B312 | LDA #&ff ; A=&FF: protect all attributes | |||
| B314 | BNE store_prot_mask ; ALWAYS branch | |||
| B316 | .parse_prot_keywords←1← B310 BNE | |||
| LDA ws_0d68 ; Load current protection mask | ||||
| B319 | PHA ; Save as starting value | |||
| B31A | .loop_match_prot_attr←1← B32A BNE | |||
| LDX #&d3 ; X=&D3: attribute keyword table offset | ||||
| B31C | LDA (fs_crc_lo),y ; Get next char from command line | |||
| B31E | STA ws_page ; Save for end-of-args check | |||
| B320 | JSR match_fs_cmd ; Match attribute keyword in table | |||
| B323 | BCS prot_check_arg_end ; No match: check if end of arguments | |||
| B325 | PLA ; Retrieve accumulated mask | |||
| B326 | ORA cmd_table_fs_lo,x ; OR in attribute bit for keyword | |||
| B329 | PHA ; Save updated mask | |||
| B32A | BNE loop_match_prot_attr ; Always non-zero after ORA: loop | |||
| B32C | .prot_check_arg_end←2← B323 BCS← B350 BCS | |||
| LDA ws_page ; Get the unmatched character | ||||
| B32E | EOR #&0d ; Is it CR? | |||
| B330 | BEQ done_prot_args ; Yes: arguments ended correctly | |||
| B332 | JMP error_bad_command ; No: invalid attribute keyword | |||
| B335 | .done_prot_args←1← B330 BEQ | |||
| PLA ; Retrieve final protection mask | ||||
| B336 | .store_prot_mask←3← A731 JMP← B314 BNE← B341 BEQ | |||
| STA ws_0d68 ; Store protection mask | ||||
| B339 | STA ws_0d69 ; Store protection mask copy | |||
| B33C | 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.
|
||||
| B33D | .cmd_unprot | |||
| LDA (fs_crc_lo),y ; Get next char from command line | ||||
| B33F | EOR #&0d ; Compare with CR (end of line) | |||
| B341 | BEQ store_prot_mask ; No args: A=0 clears all protection | |||
| B343 | LDA ws_0d68 ; Load current protection mask | |||
| B346 | PHA ; Save as starting value | |||
| B347 | .loop_match_unprot_attr←1← B357 BCC | |||
| LDX #&d3 ; X=&D3: attribute keyword table offset | ||||
| B349 | LDA (fs_crc_lo),y ; Get next char from command line | |||
| B34B | STA ws_page ; Save for end-of-args check | |||
| B34D | JSR match_fs_cmd ; Match attribute keyword in table | |||
| B350 | BCS prot_check_arg_end ; No match: check if end of arguments | |||
| B352 | PLA ; Retrieve accumulated mask | |||
| B353 | AND cmd_table_fs_hi,x ; AND to clear matched attribute bit | |||
| B356 | PHA ; Save updated mask | |||
| B357 | 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.
|
||||
| B359 | .cmd_wipe | |||
| JSR mask_owner_access ; Mask owner access flags to 5 bits | ||||
| B35C | LDA #0 ; Initialise file index to 0 | |||
| B35E | STA fs_work_5 ; Store file counter | |||
| B360 | JSR save_ptr_to_os_text ; Save pointer to command text | |||
| B363 | JSR parse_filename_arg ; Parse wildcard filename argument | |||
| B366 | INX ; Advance past CR terminator | |||
| B367 | STX fs_work_6 ; Save end-of-argument buffer position | |||
| B369 | .request_next_wipe←1← B3A5 JMP | |||
| LDA #1 ; Command code 1 = examine directory | ||||
| B36B | STA fs_cmd_data ; Store command in TX buffer byte 0 | |||
| B36E | STA fs_data_count ; Store flag in TX buffer byte 2 | |||
| B371 | LDX fs_work_5 ; Load current file index | |||
| B373 | STX fs_func_code ; Store file index in TX buffer byte 1 | |||
| B376 | LDX #3 ; X=3: copy from TX buffer offset 3 | |||
| B378 | JSR copy_arg_to_buf ; Copy filename argument to TX buffer | |||
| B37B | LDY #3 ; Function code 3 = examine | |||
| B37D | LDA #&80 ; Flag &80 = escapable | |||
| B37F | STA need_release_tube ; Mark operation as escapable | |||
| B381 | JSR save_net_tx_cb ; Send examine request to file server | |||
| B384 | LDA fs_cmd_data ; Get server response status | |||
| B387 | BNE check_wipe_attr ; Non-zero: file found, process it | |||
| B389 | LDA #osbyte_flush_buffer_class ; OSBYTE &0F: flush buffer class | |||
| B38B | LDX #1 ; X=1: flush input buffers | |||
| B38D | JSR osbyte ; Flush keyboard buffer Flush input buffers (X non-zero) | |||
| B390 | LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: keyboard scan from 16 | |||
| B392 | JSR osbyte ; Scan keyboard to clear state Keyboard scan starting from key 16 | |||
| B395 | LDY #0 ; Y=0: no key pressed Y=key | |||
| B397 | LDA #osbyte_write_keys_pressed ; OSBYTE &78: write keys pressed | |||
| B399 | JMP osbyte ; Clear keyboard state and return Write current keys pressed (X and Y) | |||
| B39C | .check_wipe_attr←1← B387 BNE | |||
| LDA fs_exam_attr_char ; Load first attribute char of response | ||||
| B39F | .loop_check_if_locked←1← B3AF BNE | |||
| CMP #&4c ; Is file locked? | ||||
| B3A1 | BNE check_wipe_dir ; No: check if directory | |||
| B3A3 | .skip_wipe_locked←1← B420 JMP | |||
| INC fs_work_5 ; Skip locked file, advance index | ||||
| B3A5 | JMP request_next_wipe ; Request next file from server | |||
| B3A8 | .check_wipe_dir←1← B3A1 BNE | |||
| CMP #&44 ; Is it a directory entry? | ||||
| B3AA | BNE show_wipe_prompt ; No: regular file, show prompt | |||
| B3AC | LDA fs_exam_dir_flag ; Check directory contents flag | |||
| B3AF | BNE loop_check_if_locked ; Non-empty dir: treat as locked, skip | |||
| B3B1 | .show_wipe_prompt←1← B3AA BNE | |||
| LDX #1 ; X=1: start from response byte 1 | ||||
| B3B3 | LDY fs_work_6 ; Y = destination index in delete buffer | |||
| B3B5 | .loop_copy_wipe_name←1← B3C2 BNE | |||
| LDA fs_func_code,x ; Load filename char from response | ||||
| B3B8 | JSR osasci ; Print filename character to screen Write character | |||
| B3BB | STA fs_filename_buf,y ; Store in delete command buffer too | |||
| B3BE | INY ; Advance destination index | |||
| B3BF | INX ; Advance source index | |||
| B3C0 | CPX #&0c ; Copied all 11 filename characters? | |||
| B3C2 | BNE loop_copy_wipe_name ; No: continue copying | |||
| B3C4 | JSR print_inline ; Print '(Y/N/?) ' prompt | |||
| B3C7 | EQUS "(?/" | |||
| B3CA | NOP ; Inline string terminator (NOP) | |||
| B3CB | JSR prompt_yn ; Read user response character | |||
| B3CE | CMP #&3f ; User pressed '?'? | |||
| B3D0 | BNE check_wipe_response ; No: check for Y/N response | |||
| B3D2 | LDA #&0d ; Carriage return before full info | |||
| B3D4 | JSR oswrch ; Print CR Write character 13 | |||
| B3D7 | LDX #2 ; X=2: start from response byte 2 | |||
| B3D9 | .loop_print_wipe_info←1← B3E2 BNE | |||
| LDA fs_cmd_data,x ; Load file info character | ||||
| B3DC | JSR osasci ; Print file info character Write character | |||
| B3DF | INX ; Advance to next character | |||
| B3E0 | CPX #&3e ; Printed all &3C info bytes? | |||
| B3E2 | BNE loop_print_wipe_info ; No: continue printing | |||
| B3E4 | JSR print_inline ; Print ' (Y/N) ' prompt (no '?') | |||
| B3E7 | EQUS " (" | |||
| B3E9 | NOP ; Padding (string terminator) | |||
| B3EA | JSR prompt_yn ; Read user response (Y/N only) | |||
| B3ED | .check_wipe_response←1← B3D0 BNE | |||
| AND #&df ; Force uppercase | ||||
| B3EF | CMP #&59 ; User said 'Y' (yes)? | |||
| B3F1 | BNE skip_wipe_to_next ; No: print newline, skip to next file | |||
| B3F3 | JSR osasci ; Echo 'Y' to screen Write character | |||
| B3F6 | LDX #0 ; X=0: start of stored filename | |||
| B3F8 | LDA fs_filename_buf,x ; Check first byte of stored name | |||
| B3FB | CMP #&0d ; Is first byte CR (empty first field)? | |||
| B3FD | BEQ use_wipe_leaf_name ; Yes: use second filename field | |||
| B3FF | .loop_build_wipe_cmd←1← B414 BNE | |||
| LDA fs_filename_buf,x ; Load byte from stored filename | ||||
| B402 | CMP #&0d ; Is it CR (field separator)? | |||
| B404 | BNE skip_if_not_space ; No: check for space | |||
| B406 | LDA #&2e ; Replace CR with '.' directory sep | |||
| B408 | .skip_if_not_space←1← B404 BNE | |||
| CMP #&20 ; Is it a space (name terminator)? | ||||
| B40A | BNE store_wipe_tx_char ; No: keep character as-is | |||
| B40C | .set_wipe_cr_end←1← B42F BEQ | |||
| LDA #&0d ; Replace space with CR (end of name) | ||||
| B40E | .store_wipe_tx_char←1← B40A BNE | |||
| STA fs_cmd_data,x ; Store in delete command TX buffer | ||||
| B411 | INX ; Advance to next character | |||
| B412 | CMP #&0d ; Was it the CR terminator? | |||
| B414 | BNE loop_build_wipe_cmd ; No: continue building delete command | |||
| B416 | LDY #&14 ; Function code &14 = delete file | |||
| B418 | JSR save_net_tx_cb ; Send delete request to file server | |||
| B41B | DEC fs_work_5 ; Adjust file index after deletion | |||
| B41D | .skip_wipe_to_next←1← B3F1 BNE | |||
| JSR osnewl ; Print newline after user response Write newline (characters 10 and 13) | ||||
| B420 | JMP skip_wipe_locked ; Advance index, process next file | |||
| B423 | .use_wipe_leaf_name←1← B3FD BEQ | |||
| DEX ; DEX to offset following INX | ||||
| B424 | .loop_copy_wipe_leaf←1← B42D BNE | |||
| INX ; Advance to next byte | ||||
| B425 | LDA fs_filename_buf_1,x ; Load byte from second field | |||
| B428 | STA fs_cmd_data,x ; Store in delete command TX buffer | |||
| B42B | CMP #&20 ; Is it a space (field terminator)? | |||
| B42D | BNE loop_copy_wipe_leaf ; No: continue copying second field | |||
| B42F | BEQ set_wipe_cr_end ; Space found: terminate with CR ALWAYS branch | |||
Print Y/N prompt and read user responsePrints 'Y/N) ' via inline string, flushes the input buffer, and reads a single character from the keyboard.
|
||||
| B431 | .prompt_yn←2← B3CB JSR← B3EA JSR | |||
| JSR print_inline ; Print 'Y/N) ' prompt | ||||
| B434 | EQUS "Y/N) " | |||
| fall through ↓ | ||||
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). |
|
| B439 | .flush_and_read_char |
| LDA #osbyte_flush_buffer_class ; OSBYTE &0F: flush buffer class | |
| B43B | LDX #1 ; X=1: flush input buffers |
| B43D | JSR osbyte ; Flush keyboard buffer before read Flush input buffers (X non-zero) |
| B440 | JSR osrdch ; Read character from input stream Read a character from the current input stream |
| B443 | BCC return_2 ; C clear: character read OK |
| B445 | JMP raise_escape_error ; Escape pressed: raise error |
| B448 | .return_2←1← B443 BCC |
| RTS ; Return with character in A | |
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). |
|
| B449 | .init_channel_table←1← 8B75 JSR |
| LDA #0 ; A=0: clear value | |
| B44B | TAY ; Y=0: start index Y=&00 |
| B44C | .loop_clear_chan_table←1← B450 BNE |
| STA fcb_count_lo,y ; Clear channel table entry | |
| B44F | INY ; Next entry |
| B450 | BNE loop_clear_chan_table ; Loop until all 256 bytes cleared |
| B452 | LDY #&0b ; Offset &0F in receive buffer |
| B454 | LDA (net_rx_ptr),y ; Get number of available channels |
| B456 | SEC ; Prepare subtraction |
| B457 | SBC #&5a ; Subtract 'Z' to get negative count |
| B459 | TAY ; Y = negative channel count (index) |
| B45A | LDA #&40 ; Channel marker &40 (available) |
| B45C | .loop_mark_chan_avail←1← B462 BPL |
| STA fcb_count_lo,y ; Mark channel slot as available | |
| B45F | DEY ; Previous channel slot |
| B460 | CPY #&b8 ; Reached start of channel range? |
| B462 | BPL loop_mark_chan_avail ; No: continue marking channels |
| B464 | INY ; Point to first channel slot |
| B465 | LDA #&c0 ; Active channel marker &C0 |
| B467 | STA fcb_count_lo,y ; Mark first channel as active |
| B46A | 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.
|
|||||||
| B46B | .attr_to_chan_index←5← 92BD JSR← 92D4 JSR← 9C6E JSR← 9CAC JSR← B73D JSR | ||||||
| PHP ; Save flags | |||||||
| B46C | SEC ; Prepare subtraction | ||||||
| B46D | SBC #&20 ; Subtract &20 to get table index | ||||||
| B46F | BMI error_chan_out_of_range ; Negative: out of valid range | ||||||
| B471 | CMP #&10 ; Above maximum channel index &0F? | ||||||
| B473 | BCC return_chan_index ; In range: valid index | ||||||
| B475 | .error_chan_out_of_range←1← B46F BMI | ||||||
| LDA #&ff ; Out of range: return &FF (invalid) | |||||||
| B477 | .return_chan_index←1← B473 BCC | ||||||
| PLP ; Restore flags | |||||||
| B478 | TAX ; X = channel index (or &FF) | ||||||
| B479 | 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.
|
||||
| B47A | .check_chan_char←2← 9DA3 JSR← B4F2 JSR | |||
| CMP #&20 ; Below space? | ||||
| B47C | BCC err_net_chan_invalid ; Yes: invalid channel character | |||
| B47E | CMP #&30 ; Below '0'? | |||
| B480 | BCC lookup_chan_by_char ; In range &20-&2F: look up channel | |||
| B482 | .err_net_chan_invalid←2← 9BD5 JMP← B47C BCC | |||
| PHA ; Save channel character | ||||
| B483 | .error_chan_not_found←1← B4B5 BEQ | |||
| LDA #&de ; Error code &DE | ||||
| B485 | .err_net_chan_not_found | |||
| JSR error_inline_log ; Generate 'Net channel' error | ||||
| B488 | EQUS "Net channel." | |||
| B494 | JSR false_ref_6f6e ; Error string continuation (unreachable) | |||
| B497 | EQUS "t on this file server" | |||
| B4AC | 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.
|
|||||||
| B4AD | .lookup_chan_by_char←2← 9ED6 JSR← B480 BCC | ||||||
| PHA ; Save channel character | |||||||
| B4AE | SEC ; Prepare subtraction | ||||||
| B4AF | SBC #&20 ; Convert char to table index | ||||||
| B4B1 | TAX ; X = channel table index | ||||||
| B4B2 | LDA fcb_net_or_port,x ; Look up network number for channel | ||||||
| B4B5 | BEQ error_chan_not_found ; Zero: channel not found, raise error | ||||||
| B4B7 | JSR match_station_net ; Check station/network matches current | ||||||
| B4BA | BNE error_chan_not_here ; No match: build detailed error msg | ||||||
| B4BC | PLA ; Discard saved channel character | ||||||
| B4BD | LDA chan_status,x ; Load channel status flags | ||||||
| B4C0 | RTS ; Return; A = channel flags | ||||||
| B4C1 | .error_chan_not_here←1← B4BA BNE | ||||||
| LDA #&de ; Error code &DE | |||||||
| B4C3 | STA error_text ; Store error code in error block | ||||||
| B4C6 | LDA #0 ; BRK opcode | ||||||
| B4C8 | STA error_block ; Store BRK at start of error block | ||||||
| B4CB | TAX ; X=0: copy index X=&00 | ||||||
| B4CC | .loop_copy_chan_err_str←1← B4D3 BNE | ||||||
| INX ; Advance copy position | |||||||
| B4CD | LDA net_channel_err_string,x ; Load 'Net channel' string byte | ||||||
| B4D0 | STA error_text,x ; Copy to error text | ||||||
| B4D3 | BNE loop_copy_chan_err_str ; Continue until NUL terminator | ||||||
| B4D5 | STX fs_load_addr_2 ; Save end-of-string position | ||||||
| B4D7 | STX fs_work_4 ; Save for suffix append | ||||||
| B4D9 | PLA ; Retrieve channel character | ||||||
| B4DA | JSR append_space_and_num ; Append ' N' (channel number) | ||||||
| B4DD | LDY fs_work_4 ; Load 'Net channel' end position | ||||||
| B4DF | .loop_append_err_suffix←1← B4E7 BNE | ||||||
| INY ; Skip past NUL to suffix string | |||||||
| B4E0 | INX ; Advance destination position | ||||||
| B4E1 | LDA net_channel_err_string,y ; Load ' not on this...' suffix byte | ||||||
| B4E4 | STA error_text,x ; Append to error message | ||||||
| B4E7 | BNE loop_append_err_suffix ; Continue until NUL | ||||||
| B4E9 | 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. |
|
| B4EC | .store_result_check_dir←2← B7D3 JSR← B859 JSR |
| LDA cur_chan_attr ; Load current channel attribute | |
| B4EF | JSR store_rx_attribute ; Store channel attribute to RX 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. |
|
| B4F2 | .check_not_dir←2← 9C08 JSR← 9E53 JSR |
| JSR check_chan_char ; Validate and look up channel | |
| B4F5 | AND #2 ; Test directory flag (bit 1) |
| B4F7 | BEQ return_from_dir_check ; Not a directory: return OK |
| B4F9 | LDA #&a8 ; Error code &A8 |
| B4FB | JSR error_inline_log ; Generate 'Is a dir.' error |
| B4FE | EQUS "Is a dir.." |
| B508 | .return_from_dir_check←1← B4F7 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.
|
||||||
| B509 | .alloc_fcb_slot←7← 9D49 JSR← 9D7E JSR← A280 JSR← A31F JSR← A34A JSR← A381 JSR← B540 JSR | |||||
| PHA ; Save channel attribute | ||||||
| B50A | LDX #&20 ; Start scanning from FCB slot &20 | |||||
| B50C | .loop_scan_fcb_slots←1← B514 BNE | |||||
| LDA fcb_attr_or_count_mid,x ; Load FCB station byte | ||||||
| B50F | BEQ done_found_free_slot ; Zero: slot is free, use it | |||||
| B511 | INX ; Try next slot | |||||
| B512 | CPX #&30 ; Past last FCB slot &2F? | |||||
| B514 | BNE loop_scan_fcb_slots ; No: check next slot | |||||
| B516 | PLA ; No free slot: discard saved attribute | |||||
| B517 | LDA #0 ; A=0: return failure (Z set) | |||||
| B519 | RTS ; Return | |||||
| B51A | .done_found_free_slot←1← B50F BEQ | |||||
| PLA ; Restore channel attribute | ||||||
| B51B | STA fcb_attr_or_count_mid,x ; Store attribute in FCB slot | |||||
| B51E | LDA #0 ; A=0: clear value | |||||
| B520 | STA fcb_xfer_count_lo,x ; Clear FCB transfer count low | |||||
| B523 | STA fcb_xfer_count_mid,x ; Clear FCB transfer count mid | |||||
| B526 | STA fcb_count_lo,x ; Clear FCB transfer count high | |||||
| B529 | LDA fs_server_stn ; Load current station number | |||||
| B52C | STA fcb_station_or_count_hi,x ; Store station in FCB | |||||
| B52F | LDA fs_server_net ; Load current network number | |||||
| B532 | STA fcb_net_or_port,x ; Store network in FCB | |||||
| B535 | TXA ; Get FCB slot index | |||||
| B536 | PHA ; Save slot index | |||||
| B537 | SEC ; Prepare subtraction | |||||
| B538 | SBC #&20 ; Convert slot to channel index (0-&0F) | |||||
| B53A | TAX ; X = channel index | |||||
| B53B | PLA ; Restore A = FCB slot index | |||||
| B53C | 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. |
|
| B53D | .alloc_fcb_or_error←2← 9CF8 JSR← A1ED JSR |
| PHA ; Save argument | |
| B53E | LDA #0 ; A=0: allocate any available slot |
| B540 | JSR alloc_fcb_slot ; Try to allocate an FCB slot |
| B543 | BNE return_alloc_success ; Success: slot allocated |
| B545 | LDA #&c0 ; Error code &C0 |
| B547 | JSR error_inline_log ; Generate 'No more FCBs' error |
| B54A | EQUS "No more FCBs." |
| B557 | .return_alloc_success←1← B543 BNE |
| PLA ; Restore argument | |
| B558 | 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.
|
||||
| B559 | .close_all_net_chans←3← 94A8 JSR← 9533 JSR← A391 JSR | |||
| CLC ; C=0: close all matching channels | ||||
| B55A | .skip_set_carry | |||
| BIT bit_test_ff ; Branch always to scan entry 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. |
|
| B55D | .scan_fcb_flags←1← 9DB8 JSR |
| LDX #&10 ; Start from FCB slot &10 | |
| B55F | .loop_scan_fcb_down←4← B56B BVC← B575 BVS← B57A BNE← B584 BEQ |
| DEX ; Previous FCB slot | |
| B560 | BPL skip_if_slots_done ; More slots to check |
| B562 | RTS ; All FCB slots processed, return |
| B563 | .skip_if_slots_done←1← B560 BPL |
| LDA chan_status,x ; Load channel flags for this slot | |
| B566 | TAY ; Save flags in Y |
| B567 | AND #2 ; Test active flag (bit 1) |
| B569 | BEQ done_check_station ; Not active: check station match |
| B56B | BVC loop_scan_fcb_down ; V clear (close all): next slot |
| B56D | BCC done_check_station ; C clear: check station match |
| B56F | TYA ; Restore original flags |
| B570 | AND #&df ; Clear write-pending flag (bit 5) |
| B572 | STA chan_status,x ; Update channel flags |
| B575 | BVS loop_scan_fcb_down ; Next slot (V always set here) ALWAYS branch |
| B577 | .done_check_station←2← B569 BEQ← B56D BCC |
| JSR match_station_net ; Check if channel belongs to station | |
| B57A | BNE loop_scan_fcb_down ; No match: skip to next slot |
| B57C | LDA #0 ; A=0: clear channel |
| B57E | STA chan_status,x ; Clear channel flags (close it) |
| B581 | STA fcb_net_or_port,x ; Clear network number |
| B584 | 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.
|
|||||||
| B586 | .match_station_net←7← A306 JSR← A331 JSR← A368 JSR← A69B JSR← AC40 JSR← B4B7 JSR← B577 JSR | ||||||
| LDA fcb_flags,x ; Load FCB station number | |||||||
| B589 | EOR fs_server_stn ; Compare with current station (EOR) | ||||||
| B58C | BNE return_from_match_stn ; Different: Z=0, no match | ||||||
| B58E | LDA fcb_net_num,x ; Load FCB network number | ||||||
| B591 | EOR fs_server_net ; Compare with current network (EOR) | ||||||
| B594 | .return_from_match_stn←1← B58C 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. |
|
| B595 | .find_open_fcb←2← B694 JSR← B782 JSR |
| LDX cur_fcb_index ; Load current FCB index | |
| B598 | BIT bit_test_ff ; Set V flag (first pass marker) |
| B59B | .loop_find_fcb←4← B5AA BVC← B5B0 BPL← B5CD BVS← B5D4 BNE |
| INX ; Next FCB slot | |
| B59C | CPX #&10 ; Past end of table (&10)? |
| B59E | BNE skip_if_no_wrap ; No: continue checking |
| B5A0 | LDX #0 ; Wrap around to slot 0 |
| B5A2 | .skip_if_no_wrap←1← B59E BNE |
| CPX cur_fcb_index ; Back to starting slot? | |
| B5A5 | BNE done_check_fcb_status ; No: check this slot |
| B5A7 | BVC loop_scan_empty_fcb ; V clear (second pass): scan empties |
| B5A9 | CLV ; Clear V for second pass |
| B5AA | BVC loop_find_fcb ; Continue scanning ALWAYS branch |
| B5AC | .done_check_fcb_status←1← B5A5 BNE |
| LDA fcb_status,x ; Load FCB status flags | |
| B5AF | ROL ; Shift bit 7 (in-use) into carry |
| B5B0 | BPL loop_find_fcb ; Not in use: skip |
| B5B2 | AND #4 ; Test bit 2 (modified flag) |
| B5B4 | BNE skip_if_modified_fcb ; Modified: check further conditions |
| B5B6 | .done_select_fcb←1← B5D6 BEQ |
| DEX ; Adjust for following INX | |
| B5B7 | .loop_scan_empty_fcb←2← B5A7 BVC← B5C2 BPL |
| INX ; Next FCB slot | |
| B5B8 | CPX #&10 ; Past end of table? |
| B5BA | BNE done_test_empty_slot ; No: continue |
| B5BC | LDX #0 ; Wrap around to slot 0 |
| B5BE | .done_test_empty_slot←1← B5BA BNE |
| LDA fcb_status,x ; Load FCB status flags | |
| B5C1 | ROL ; Shift bit 7 into carry |
| B5C2 | BPL loop_scan_empty_fcb ; Not in use: continue scanning |
| B5C4 | SEC ; Set carry for ROR restore |
| B5C5 | ROR ; Restore original flags |
| B5C6 | STA fcb_status,x ; Save flags back (mark as found) |
| B5C9 | LDX cur_fcb_index ; Restore original FCB index |
| B5CC | RTS ; Return with found slot in X |
| B5CD | .skip_if_modified_fcb←1← B5B4 BNE |
| BVS loop_find_fcb ; V set (first pass): skip modified | |
| B5CF | LDA fcb_status,x ; Load FCB status flags |
| B5D2 | AND #&20 ; Test bit 5 (offset pending) |
| B5D4 | BNE loop_find_fcb ; Bit 5 set: skip this slot |
| B5D6 | 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.
|
||||||
| B5D8 | .init_wipe_counters←2← B61B JSR← B6C6 JSR | |||||
| LDY #1 ; Initial pass count = 1 | ||||||
| B5DA | STY xfer_pass_count ; Store pass counter | |||||
| B5DD | DEY ; Y=0 Y=&00 | |||||
| B5DE | STY xfer_count_lo ; Clear byte counter low | |||||
| B5E1 | STY xfer_offset ; Clear offset counter | |||||
| B5E4 | STY xfer_flag ; Clear transfer flag | |||||
| B5E7 | TYA ; A=0 A=&00 | |||||
| B5E8 | LDX #2 ; Clear 3 counter bytes | |||||
| B5EA | .loop_clear_counters←1← B5EE BPL | |||||
| STA xfer_counter,x ; Clear counter byte | ||||||
| B5ED | DEX ; Next byte | |||||
| B5EE | BPL loop_clear_counters ; Loop for indices 2, 1, 0 | |||||
| B5F0 | STX xfer_sentinel_1 ; Store &FF as sentinel in l10cd | |||||
| B5F3 | STX xfer_sentinel_2 ; Store &FF as sentinel in l10ce | |||||
| B5F6 | LDX #&ca ; X=&CA: workspace offset | |||||
| B5F8 | LDY #&10 ; Y=&10: page &10 | |||||
| B5FA | 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.
|
||||
| B5FB | .start_wipe_pass←2← B68B JSR← B7B7 JSR | |||
| JSR verify_ws_checksum ; Verify workspace checksum integrity | ||||
| B5FE | STX cur_fcb_index ; Save current FCB index | |||
| B601 | LDA fcb_status,x ; Load FCB status flags | |||
| B604 | ROR ; Shift bit 0 (active) into carry | |||
| B605 | BCC done_clear_fcb_active ; Not active: clear status and return | |||
| B607 | LDA work_stn_lo ; Save current station low to stack | |||
| B60A | PHA ; Push station low | |||
| B60B | LDA work_stn_hi ; Save current station high | |||
| B60E | PHA ; Push station high | |||
| B60F | LDA fcb_stn_lo,x ; Load FCB station low | |||
| B612 | STA work_stn_lo ; Set as working station low | |||
| B615 | LDA fcb_stn_hi,x ; Load FCB station high | |||
| B618 | STA work_stn_hi ; Set as working station high | |||
| B61B | JSR init_wipe_counters ; Reset transfer counters | |||
| B61E | DEC xfer_offset ; Set offset to &FF (no data yet) | |||
| B621 | DEC xfer_pass_count ; Set pass counter to 0 (flush mode) | |||
| B624 | LDX cur_fcb_index ; Reload FCB index | |||
| B627 | TXA ; Transfer to A | |||
| B628 | CLC ; Prepare addition | |||
| B629 | ADC #&11 ; Add &11 for buffer page offset | |||
| B62B | STA fcb_buf_page ; Store buffer address high byte | |||
| B62E | LDA fcb_status,x ; Load FCB status flags | |||
| B631 | AND #&20 ; Test bit 5 (has saved offset) | |||
| B633 | BEQ done_restore_offset ; No offset: skip restore | |||
| B635 | LDA fcb_buf_offset,x ; Load saved byte offset | |||
| B638 | STA xfer_offset ; Restore offset counter | |||
| B63B | .done_restore_offset←1← B633 BEQ | |||
| LDA fcb_attr_ref,x ; Load FCB attribute reference | ||||
| B63E | STA cur_attr_ref ; Store as current reference | |||
| B641 | TAX ; Transfer to X | |||
| B642 | JSR read_rx_attribute ; Read saved receive attribute | |||
| B645 | PHA ; Push to stack | |||
| B646 | TXA ; Restore attribute to A | |||
| B647 | STA (net_rx_ptr),y ; Set attribute in receive buffer | |||
| B649 | LDX #&ca ; X=&CA: workspace offset | |||
| B64B | LDY #&10 ; Y=&10: page &10 | |||
| B64D | LDA #0 ; A=0: standard transfer mode | |||
| B64F | JSR send_and_receive ; Send data and receive response | |||
| B652 | LDX cur_fcb_index ; Reload FCB index | |||
| B655 | PLA ; Restore saved receive attribute | |||
| B656 | JSR store_rx_attribute ; Restore receive attribute | |||
| B659 | PLA ; Restore station high | |||
| B65A | STA work_stn_hi ; Store station high | |||
| B65D | PLA ; Restore station low | |||
| B65E | STA work_stn_lo ; Store station low | |||
| B661 | .done_clear_fcb_active←1← B605 BCC | |||
| LDA #&dc ; Mask &DC: clear bits 0, 1, 5 | ||||
| B663 | AND fcb_status,x ; Clear active and offset flags | |||
| B666 | STA fcb_status,x ; Update FCB status | |||
| B669 | 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.
|
||||
| B66A | .save_fcb_context←2← B72D JSR← B8EF JSR | |||
| LDX #&0c ; Copy 13 bytes (indices 0 to &0C) | ||||
| B66C | .loop_save_tx_context←1← B676 BPL | |||
| LDA txcb_reply_port,x ; Load TX buffer byte | ||||
| B66F | STA fcb_ctx_save,x ; Save to context buffer at &10D9 | |||
| B672 | LDA fs_load_addr,x ; Load workspace byte from fs_load_addr | |||
| B674 | PHA ; Save to stack | |||
| B675 | DEX ; Next byte down | |||
| B676 | BPL loop_save_tx_context ; Loop for all 13 bytes | |||
| B678 | CPY #0 ; Y=0? (no FCB to process) | |||
| B67A | BNE done_save_context ; Non-zero: scan and process FCBs | |||
| B67C | JMP loop_restore_workspace ; Y=0: skip to restore workspace | |||
| B67F | .done_save_context←1← B67A BNE | |||
| PHP ; Save flags | ||||
| B680 | LDX #&ff ; X=&FF: start scanning from -1 | |||
| B682 | .loop_find_pending_fcb←2← B686 BPL← B689 BPL | |||
| INX ; Next FCB slot | ||||
| B683 | LDA fcb_status,x ; Load FCB status flags | |||
| B686 | BPL loop_find_pending_fcb ; Bit 7 clear: not pending, skip | |||
| B688 | ASL ; Shift bit 6 to bit 7 | |||
| B689 | BPL loop_find_pending_fcb ; Bit 6 clear: skip | |||
| B68B | JSR start_wipe_pass ; Flush this FCB's pending data | |||
| B68E | LDA #&40 ; Pending marker &40 | |||
| B690 | STA fcb_status,x ; Mark FCB as pending-only | |||
| B693 | PHP ; Save flags | |||
| B694 | JSR find_open_fcb ; Find next available FCB slot | |||
| B697 | PLP ; Restore flags | |||
| B698 | LDA cur_chan_attr ; Load current channel attribute | |||
| B69B | STA cur_attr_ref ; Store as current reference | |||
| B69E | PHA ; Save attribute | |||
| B69F | SEC ; Prepare attribute-to-channel conversion | |||
| B6A0 | SBC #&20 ; Convert attribute (&20+) to channel index | |||
| B6A2 | TAY ; Y = attribute index | |||
| B6A3 | LDA fcb_net_or_port,y ; Load station for this attribute | |||
| B6A6 | STA fs_cmd_data ; Store station in TX buffer | |||
| B6A9 | PLA ; Restore attribute | |||
| B6AA | STA fcb_attr_ref,x ; Store attribute in FCB slot | |||
| B6AD | LDA work_stn_lo ; Load working station low | |||
| B6B0 | STA fcb_stn_lo,x ; Store in TX buffer Store station low in FCB | |||
| B6B3 | LDA work_stn_hi ; Load working station high | |||
| B6B6 | STA fcb_stn_hi,x ; Store in TX buffer Store station high in FCB | |||
| B6B9 | TXA ; Get FCB slot index | |||
| B6BA | CLC ; Prepare addition | |||
| B6BB | ADC #&11 ; Add &11 for buffer page offset | |||
| B6BD | STA fcb_buf_page ; Store buffer address high byte | |||
| B6C0 | PLP ; Restore flags | |||
| B6C1 | BVC done_init_wipe ; V clear: skip directory request | |||
| B6C3 | JSR flush_fcb_if_station_known ; Command byte = 0 Send directory request to server | |||
| B6C6 | .done_init_wipe←1← B6C1 BVC | |||
| JSR init_wipe_counters ; Reset transfer counters | ||||
| B6C9 | JSR read_rx_attribute ; Read saved receive attribute | |||
| B6CC | PHA ; Function code &0D Push to stack | |||
| B6CD | LDA cur_attr_ref ; Load current reference | |||
| B6D0 | STA (net_rx_ptr),y ; Set in receive buffer | |||
| B6D2 | LDY #&10 ; Y=&10: page &10 | |||
| B6D4 | LDA #2 ; A=2: transfer mode 2 | |||
| B6D6 | JSR send_and_receive ; Send and receive data | |||
| B6D9 | PLA ; Restore receive attribute | |||
| B6DA | JSR store_rx_attribute ; Restore receive attribute | |||
| B6DD | LDX cur_fcb_index ; Reload FCB index | |||
| B6E0 | LDA xfer_pass_count ; Load pass counter | |||
| B6E3 | BNE done_calc_offset ; Non-zero: data received, calc offset | |||
| B6E5 | LDA xfer_offset ; Load offset counter | |||
| B6E8 | BEQ done_set_fcb_active ; Zero: no data received at all | |||
| B6EA | .done_calc_offset←1← B6E3 BNE | |||
| LDA xfer_offset ; Load offset counter | ||||
| B6ED | EOR #&ff ; Negate (ones complement) | |||
| B6EF | CLC ; Clear carry for add | |||
| B6F0 | ADC #1 ; Complete twos complement negation | |||
| B6F2 | STA fcb_buf_offset,x ; Store negated offset in FCB | |||
| B6F5 | LDA #&20 ; Set bit 5 (has saved offset) | |||
| B6F7 | ORA fcb_status,x ; Add to FCB flags | |||
| B6FA | STA fcb_status,x ; Update FCB status | |||
| B6FD | LDA fcb_buf_page ; Load buffer address high byte | |||
| B700 | STA fs_load_addr_3 ; Set pointer high byte | |||
| B702 | LDA #0 ; A=0: pointer low byte and clear val | |||
| B704 | STA fs_load_addr_2 ; Set pointer low byte | |||
| B706 | LDY fcb_buf_offset,x ; Load negated offset (start of clear) | |||
| B709 | .loop_clear_buffer←1← B70C BNE | |||
| STA (fs_load_addr_2),y ; Clear buffer byte | ||||
| B70B | INY ; Next byte | |||
| B70C | BNE loop_clear_buffer ; Loop until page boundary | |||
| B70E | .done_set_fcb_active←1← B6E8 BEQ | |||
| LDA #2 ; Set bit 1 (active flag) | ||||
| B710 | ORA fcb_status,x ; Add active flag to status | |||
| B713 | STA fcb_status,x ; Update FCB status | |||
| B716 | LDY #0 ; Y=0: start restoring workspace | |||
| B718 | .loop_restore_workspace←2← B67C JMP← B71F BNE | |||
| PLA ; Restore workspace byte from stack | ||||
| B719 | STA fs_load_addr,y ; Store to fs_load_addr workspace | |||
| B71C | INY ; Next byte | |||
| B71D | CPY #&0d ; Restored all 13 bytes? | |||
| B71F | BNE loop_restore_workspace ; No: continue restoring | |||
| fall through ↓ | ||||
| B721 | .restore_catalog_entry←1← B922 JSR |
| LDY #&0c ; Copy 13 bytes (indices 0 to &0C) | |
| B723 | .loop_restore_tx_buf←1← B72A BPL |
| LDA fcb_ctx_save,y ; Load saved catalog byte from &10D9 | |
| B726 | STA txcb_reply_port,y ; Restore to TX buffer |
| B729 | DEY ; Next byte down |
| B72A | BPL loop_restore_tx_buf ; Loop for all bytes |
| B72C | RTS ; Return |
| B72D | .loop_save_before_match←1← B74C 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).
|
||||||
| B730 | .find_matching_fcb←3← 9DF8 JSR← B7EE JSR← B88B JSR | |||||
| LDX #&ff ; X=&FF: start scanning from -1 | ||||||
| B732 | .loop_reload_attr←2← B76E BNE← B776 BNE | |||||
| LDY cur_chan_attr ; Load channel attribute to match | ||||||
| B735 | .loop_next_fcb_slot←2← B754 BEQ← B75A BNE | |||||
| INX ; Next FCB slot | ||||||
| B736 | CPX #&10 ; Past end of table (&10)? | |||||
| B738 | BNE done_test_fcb_active ; No: check this slot | |||||
| B73A | LDA cur_chan_attr ; Load channel attribute | |||||
| B73D | JSR attr_to_chan_index ; Convert to channel index | |||||
| B740 | LDA fcb_station_or_count_hi,x ; Load station for this channel | |||||
| B743 | STA work_stn_hi ; Store as match target station high | |||||
| B746 | LDA fcb_attr_or_count_mid,x ; Load port for this channel | |||||
| B749 | STA work_stn_lo ; Store as match target station low | |||||
| B74C | JMP loop_save_before_match ; Save context and rescan from start | |||||
| B74F | .done_test_fcb_active←1← B738 BNE | |||||
| LDA fcb_status,x ; Load FCB status flags | ||||||
| B752 | AND #2 ; Test active flag (bit 1) | |||||
| B754 | BEQ loop_next_fcb_slot ; Not active: skip to next | |||||
| B756 | TYA ; Get attribute to match | |||||
| B757 | CMP fcb_attr_ref,x ; Compare with FCB attribute ref | |||||
| B75A | BNE loop_next_fcb_slot ; No attribute match: skip | |||||
| B75C | STX cur_fcb_index ; Save matching FCB index | |||||
| B75F | PHP ; Save flags from attribute compare | |||||
| B760 | SEC ; Prepare subtraction | |||||
| B761 | SBC #&20 ; Convert attribute to channel index | |||||
| B763 | PLP ; Restore flags from attribute compare | |||||
| B764 | TAY ; Y = channel index | |||||
| B765 | LDX cur_fcb_index ; Reload FCB index | |||||
| B768 | LDA fcb_attr_or_count_mid,y ; Load channel station byte | |||||
| B76B | CMP fcb_stn_lo,x ; Compare with FCB station | |||||
| B76E | BNE loop_reload_attr ; Station mismatch: try next | |||||
| B770 | LDA fcb_station_or_count_hi,y ; Load channel network byte | |||||
| B773 | CMP fcb_stn_hi,x ; Compare with FCB network | |||||
| B776 | BNE loop_reload_attr ; Network mismatch: try next | |||||
| B778 | LDA fcb_status,x ; Load FCB flags | |||||
| B77B | BPL return_test_offset ; Bit 7 clear: no pending flush | |||||
| B77D | AND #&7f ; Clear pending flag (bit 7) | |||||
| B77F | STA fcb_status,x ; Update FCB status | |||||
| B782 | JSR find_open_fcb ; Find new open FCB slot | |||||
| B785 | LDA fcb_status,x ; Reload FCB flags | |||||
| B788 | .return_test_offset←1← B77B BPL | |||||
| AND #&20 ; Test bit 5 (has offset data) | ||||||
| B78A | 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).
|
||||
| B78B | .inc_fcb_byte_count←2← B833 JSR← B8CB JSR | |||
| INC fcb_count_lo,x ; Increment byte count low | ||||
| B78E | BNE return_from_inc_fcb_count ; No overflow: done | |||
| B790 | INC fcb_attr_or_count_mid,x ; Increment byte count mid | |||
| B793 | BNE return_from_inc_fcb_count ; No overflow: done | |||
| B795 | INC fcb_station_or_count_hi,x ; Increment byte count high | |||
| B798 | .return_from_inc_fcb_count←2← B78E BNE← B793 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.
|
||||
| B799 | .process_all_fcbs←9← 8D87 JSR← 8FA0 JSR← 94A0 JSR← 9BDC JSR← 9C21 JSR← 9CC4 JSR← 9D8A JSR← 9E58 JSR← A67A JSR | |||
| TXA ; Save X | ||||
| B79A | PHA ; Push X to stack | |||
| B79B | TYA ; Save Y | |||
| B79C | PHA ; Push Y to stack | |||
| B79D | LDX #&f7 ; X=&F7: save 9 workspace bytes (&F7..&FF) | |||
| B79F | .loop_cb79f←1← B7A4 BMI | |||
| LDA lffbd,x ; Load workspace byte | ||||
| B7A2 | PHA ; Push fs_options | |||
| B7A3 | INX ; Next byte | |||
| B7A4 | BMI loop_cb79f ; X<0: more bytes to save | |||
| B7A6 | LDX #&0f ; Start from FCB slot &0F | |||
| B7A8 | STX cur_fcb_index ; Store as current FCB index | |||
| B7AB | .loop_process_fcb←1← B7BF BPL | |||
| LDX cur_fcb_index ; Load current FCB index | ||||
| B7AE | TYA ; Get filter attribute | |||
| B7AF | BEQ done_flush_fcb ; Zero: process all FCBs | |||
| B7B1 | CMP fcb_attr_ref,x ; Compare with FCB attribute ref | |||
| B7B4 | BNE done_advance_fcb ; No match: skip this FCB | |||
| B7B6 | .done_flush_fcb←1← B7AF BEQ | |||
| PHA ; Save filter attribute | ||||
| B7B7 | JSR start_wipe_pass ; Flush pending data for this FCB | |||
| B7BA | PLA ; Restore filter | |||
| B7BB | TAY ; Y = filter attribute | |||
| B7BC | .done_advance_fcb←1← B7B4 BNE | |||
| DEC cur_fcb_index ; Previous FCB index | ||||
| B7BF | BPL loop_process_fcb ; More slots: continue loop | |||
| B7C1 | LDX #8 ; X=8: restore 9 workspace bytes | |||
| B7C3 | .loop_cb7c3←1← B7C7 BPL | |||
| PLA ; Restore fs_block_offset | ||||
| B7C4 | STA fs_work_4,x ; Restore workspace byte | |||
| B7C6 | DEX ; Next byte down | |||
| B7C7 | BPL loop_cb7c3 ; More bytes: continue restoring | |||
| B7C9 | PLA ; Restore Y | |||
| B7CA | TAY ; Y restored | |||
| B7CB | PLA ; Restore X | |||
| B7CC | TAX ; X restored | |||
| B7CD | RTS ; Return | |||
| B7CE | STY cur_chan_attr ; Save channel attribute | |||
| B7D1 | TXA ; Save caller's X | |||
| B7D2 | PHA ; Push X | |||
| B7D3 | JSR store_result_check_dir ; Store result and check not directory | |||
| B7D6 | LDA chan_status,x ; Load channel flags | |||
| B7D9 | AND #&20 ; Test write-only flag (bit 5) | |||
| B7DB | BEQ done_read_fcb_byte ; Not write-only: proceed with read | |||
| B7DD | LDA #&d4 ; Error code &D4 | |||
| B7DF | JSR error_inline_log ; Generate 'Write only' error | |||
| B7E2 | EQUS "Write only." | |||
| B7ED | .done_read_fcb_byte←1← B7DB BEQ | |||
| CLV ; Clear V (first-pass matching) | ||||
| B7EE | JSR find_matching_fcb ; Find FCB matching this channel | |||
| B7F1 | BEQ done_load_from_buf ; No offset: read byte from buffer | |||
| B7F3 | LDA fcb_count_lo,y ; Load byte count for matching FCB | |||
| B7F6 | CMP fcb_buf_offset,x ; Compare with buffer offset limit | |||
| B7F9 | BCC done_load_from_buf ; Below offset: data available | |||
| B7FB | LDA chan_status,y ; Load channel flags for FCB | |||
| B7FE | TAX ; Transfer to X for testing | |||
| B7FF | AND #&40 ; Test bit 6 (EOF already signalled) | |||
| B801 | BNE error_end_of_file ; EOF already set: raise error | |||
| B803 | TXA ; Restore flags | |||
| B804 | ORA #&40 ; Set EOF flag (bit 6) | |||
| B806 | STA chan_status,y ; Update channel flags with EOF | |||
| B809 | LDA #0 ; A=0: clear receive attribute | |||
| B80B | JSR store_rx_attribute ; Clear receive attribute (A=0) | |||
| B80E | PLA ; Restore caller's X | |||
| B80F | TAX ; X restored | |||
| B810 | LDA #&fe ; A=&FE: EOF marker byte | |||
| B812 | LDY cur_chan_attr ; Restore channel attribute | |||
| B815 | SEC ; C=1: end of file | |||
| B816 | RTS ; Return | |||
| B817 | .error_end_of_file←1← B801 BNE | |||
| LDA #&df ; Error code &DF | ||||
| B819 | JSR error_inline_log ; Generate 'End of file' error | |||
| B81C | EQUS "End of file." | |||
| B828 | .done_load_from_buf←2← B7F1 BEQ← B7F9 BCC | |||
| LDA fcb_count_lo,y ; Load current byte count (= offset) | ||||
| B82B | PHA ; Save byte count | |||
| B82C | TYA ; Get FCB slot index | |||
| B82D | TAX ; X = FCB slot for byte count inc | |||
| B82E | LDA #0 ; A=0: clear receive attribute | |||
| B830 | JSR store_rx_attribute ; Clear receive attribute (A=0) | |||
| B833 | JSR inc_fcb_byte_count ; Increment byte count for this FCB | |||
| B836 | PLA ; Restore byte count (= buffer offset) | |||
| B837 | TAY ; Y = offset into data buffer | |||
| B838 | LDA cur_fcb_index ; Load current FCB index | |||
| B83B | CLC ; Prepare addition | |||
| B83C | ADC #&11 ; Add &11 for buffer page offset | |||
| B83E | STA fs_load_addr_3 ; Set pointer high byte | |||
| B840 | LDA #0 ; A=0: pointer low byte | |||
| B842 | STA fs_load_addr_2 ; Set pointer low byte | |||
| B844 | PLA ; Restore caller's X | |||
| B845 | TAX ; X restored | |||
| B846 | LDA (fs_load_addr_2),y ; Read data byte from buffer | |||
| B848 | LDY cur_chan_attr ; Restore channel attribute | |||
| B84B | CLC ; C=0: byte read successfully | |||
| B84C | RTS ; Return; A = data byte | |||
| B84D | STY cur_chan_attr ; Save channel attribute | |||
| B850 | PHA ; Save data byte | |||
| B851 | TAY ; Y = data byte | |||
| B852 | TXA ; Save caller's X | |||
| B853 | PHA ; Push X | |||
| B854 | TYA ; Restore data byte to A | |||
| B855 | PHA ; Push data byte for later | |||
| B856 | STA osbput_saved_byte ; Save data byte in workspace | |||
| B859 | JSR store_result_check_dir ; Store result and check not directory | |||
| B85C | LDA chan_status,x ; Load channel flags | |||
| B85F | BMI done_test_write_flag ; Bit 7 set: channel open, proceed | |||
| B861 | LDA #&c1 ; Error &C1: Not open for update | |||
| B863 | JSR error_inline_log ; Raise error with inline string | |||
| B866 | EQUS "Not open for update." | |||
| B87A | .done_test_write_flag←1← B85F BMI | |||
| AND #&20 ; Test write flag (bit 5) | ||||
| B87C | BEQ done_find_write_fcb ; Not write-capable: use buffer path | |||
| B87E | LDY fcb_net_or_port,x ; Load reply port for this channel | |||
| B881 | PLA ; Restore data byte | |||
| B882 | JSR send_wipe_request ; Send byte directly to server | |||
| B885 | JMP done_inc_byte_count ; Update byte count and return | |||
| B888 | .done_find_write_fcb←1← B87C BEQ | |||
| BIT bit_test_ff ; Set V flag (alternate match mode) | ||||
| B88B | JSR find_matching_fcb ; Find matching FCB for channel | |||
| B88E | LDA fcb_count_lo,y ; Load byte count for FCB | |||
| B891 | CMP #&ff ; Buffer full (&FF bytes)? | |||
| B893 | BNE done_check_buf_offset ; No: store byte in buffer | |||
| B895 | JSR flush_fcb_with_init ; Save X Save context and flush FCB data | |||
| B898 | .done_check_buf_offset←1← B893 BNE | |||
| CMP fcb_buf_offset,x ; Push Y Compare count with buffer offset | ||||
| B89B | BCC done_set_dirty_flag ; Below offset: skip offset update | |||
| B89D | ADC #0 ; Carry set from BCS/BCC above Add carry (count + 1) | |||
| B89F | STA fcb_buf_offset,x ; Update buffer offset in FCB | |||
| B8A2 | BNE done_set_dirty_flag ; Non-zero: keep offset flag | |||
| B8A4 | LDA #&df ; Mask &DF: clear bit 5 | |||
| B8A6 | AND fcb_status,x ; Clear offset flag | |||
| B8A9 | STA fcb_status,x ; Update FCB status | |||
| B8AC | .done_set_dirty_flag←2← B89B BCC← B8A2 BNE | |||
| LDA #1 ; Set bit 0 (dirty/active) | ||||
| B8AE | ORA fcb_status,x ; Add to FCB flags | |||
| B8B1 | STA fcb_status,x ; Update FCB status | |||
| B8B4 | LDA fcb_count_lo,y ; Load byte count (= write position) | |||
| B8B7 | PHA ; Save count | |||
| B8B8 | TYA ; Get FCB slot index | |||
| B8B9 | TAX ; X = FCB slot | |||
| B8BA | PLA ; Restore byte count | |||
| B8BB | TAY ; Y = buffer write offset | |||
| B8BC | LDA cur_fcb_index ; Load current FCB index | |||
| B8BF | CLC ; Prepare addition | |||
| B8C0 | ADC #&11 ; Add &11 for buffer page offset | |||
| B8C2 | STA fs_load_addr_3 ; Set pointer high byte | |||
| B8C4 | LDA #0 ; A=0: pointer low byte | |||
| B8C6 | STA fs_load_addr_2 ; Set pointer low byte | |||
| B8C8 | PLA ; Restore data byte | |||
| B8C9 | STA (fs_load_addr_2),y ; Write data byte to buffer | |||
| B8CB | .done_inc_byte_count←1← B885 JMP | |||
| JSR inc_fcb_byte_count ; Increment byte count for this FCB | ||||
| B8CE | LDA #0 ; A=0: clear receive attribute | |||
| B8D0 | JSR store_rx_attribute ; Clear receive attribute (A=0) | |||
| B8D3 | PLA ; Restore caller's X | |||
| B8D4 | TAX ; X restored | |||
| B8D5 | PLA ; Discard saved data byte | |||
| B8D6 | LDY cur_chan_attr ; Restore channel attribute | |||
| B8D9 | RTS ; Return | |||
Flush FCB byte count to server if station is setSaves all registers, checks if the FCB has a known station. If yes, sends the accumulated byte count as a flush request to the file server. If no station is set, falls through to flush_fcb_with_init which saves FCB context first.
|
|||||||||||
| B8DA | .flush_fcb_if_station_known←1← B6C3 JSR | ||||||||||
| PHA ; Save A | |||||||||||
| B8DB | TXA ; Transfer X to A | ||||||||||
| B8DC | PHA ; Save X | ||||||||||
| B8DD | TYA ; Transfer Y to A | ||||||||||
| B8DE | PHA ; Save Y (channel index) | ||||||||||
| B8DF | LDA fcb_net_or_port,y ; Load station for this channel | ||||||||||
| B8E2 | BNE store_station_and_flush ; Non-zero: station known, skip init | ||||||||||
| fall through ↓ | |||||||||||
Save FCB context and flush byte count to serverSaves all registers and the current FCB context, copies the FCB byte count into the TX command buffer, and sends a flush/close request to the file server. Restores the catalog entry and all registers on return.
|
|||||||||||
| B8E4 | .flush_fcb_with_init←1← B895 JSR | ||||||||||
| PHA ; Save A | |||||||||||
| B8E5 | TXA ; Transfer X to A | ||||||||||
| B8E6 | PHA ; Save X | ||||||||||
| B8E7 | TYA ; Transfer Y to A | ||||||||||
| B8E8 | PHA ; Save Y (channel index) | ||||||||||
| B8E9 | LDA fcb_net_or_port,y ; Load station for this channel | ||||||||||
| B8EC | PHA ; Save station on stack | ||||||||||
| B8ED | LDY #0 ; Y=0: reset index | ||||||||||
| B8EF | JSR save_fcb_context ; Save current FCB context | ||||||||||
| B8F2 | PLA ; Restore station from stack | ||||||||||
| B8F3 | .store_station_and_flush←1← B8E2 BNE | ||||||||||
| STA fs_cmd_data ; Store station in command buffer | |||||||||||
| B8F6 | TAX ; X=station number | ||||||||||
| B8F7 | PLA ; Restore Y from stack | ||||||||||
| B8F8 | TAY ; Y=channel index | ||||||||||
| B8F9 | PHA ; Re-save Y on stack | ||||||||||
| B8FA | TXA ; A=station number | ||||||||||
| B8FB | PHA ; Save station for later restore | ||||||||||
| B8FC | LDX #0 ; X=0 | ||||||||||
| B8FE | STX fs_func_code ; Clear function code | ||||||||||
| B901 | LDA fcb_count_lo,y ; Load byte count lo from FCB | ||||||||||
| B904 | STA fs_data_count ; Store as data byte count | ||||||||||
| B907 | LDA fcb_attr_or_count_mid,y ; Load byte count mid from FCB | ||||||||||
| B90A | STA fs_reply_cmd ; Store as reply command byte | ||||||||||
| B90D | LDA fcb_station_or_count_hi,y ; Load byte count hi from FCB | ||||||||||
| B910 | STA fs_load_vector ; Store as load vector field | ||||||||||
| B913 | LDY #&0d ; Y=&0D: TX command byte offset | ||||||||||
| B915 | LDX #5 ; X=5: send 5 bytes | ||||||||||
| B917 | JSR save_net_tx_cb ; Send flush request to server | ||||||||||
| B91A | PLA ; Restore station from stack | ||||||||||
| B91B | TAY ; Y=station for wipe request | ||||||||||
| B91C | LDA osbput_saved_byte ; Load saved data byte | ||||||||||
| B91F | JSR send_wipe_request ; Send close/wipe request to server | ||||||||||
| B922 | JSR restore_catalog_entry ; Restore catalog state after flush | ||||||||||
| B925 | PLA ; Restore Y | ||||||||||
| B926 | TAY ; Y restored | ||||||||||
| B927 | PLA ; Restore X | ||||||||||
| B928 | TAX ; X restored | ||||||||||
| B929 | PLA ; Restore A | ||||||||||
| B92A | 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.
|
||||||
| B92B | .send_wipe_request←2← B882 JSR← B91F JSR | |||||
| STY fs_handle_mask ; Store reply port | ||||||
| B92E | STA fs_error_flags ; Store data byte | |||||
| B931 | TYA ; Save Y | |||||
| B932 | PHA ; Push Y to stack | |||||
| B933 | TXA ; Save X | |||||
| B934 | PHA ; Push X to stack | |||||
| B935 | LDA #&90 ; Function code &90 | |||||
| B937 | STA fs_putb_buf ; Store in send buffer | |||||
| B93A | JSR init_txcb ; Initialise TX control block | |||||
| B93D | LDA #&dc ; TX start address low = &DC | |||||
| B93F | STA txcb_start ; Set TX start in control block | |||||
| B941 | LDA #&e0 ; TX end address low = &E0 | |||||
| B943 | STA txcb_end ; Set TX end in control block | |||||
| B945 | LDA #9 ; Expected reply port = 9 | |||||
| B947 | STA fs_getb_buf ; Store reply port in buffer | |||||
| B94A | LDX #&c0 ; TX control = &C0 | |||||
| B94C | LDY #0 ; Y=0: no timeout | |||||
| B94E | LDA fs_handle_mask ; Load reply port for addressing | |||||
| B951 | JSR send_disconnect_reply ; Send packet to server | |||||
| B954 | LDA fs_getb_buf ; Load reply status | |||||
| B957 | BEQ done_toggle_station ; Zero: success | |||||
| B959 | STA fs_last_error ; Store error code | |||||
| B95C | LDX #0 ; X=0: copy index | |||||
| B95E | .loop_copy_wipe_err_msg←1← B969 BNE | |||||
| LDA fs_putb_buf,x ; Load error message byte | ||||||
| B961 | STA error_block,x ; Copy to error block | |||||
| B964 | CMP #&0d ; Is it CR (end of message)? | |||||
| B966 | BEQ done_terminate_wipe_err ; Yes: terminate string | |||||
| B968 | INX ; Next byte | |||||
| B969 | BNE loop_copy_wipe_err_msg ; Continue copying error message | |||||
| B96B | .done_terminate_wipe_err←1← B966 BEQ | |||||
| LDA #0 ; NUL terminator | ||||||
| B96D | STA error_block,x ; Terminate error string in block | |||||
| B970 | DEX ; Back up position for error check | |||||
| B971 | JMP check_net_error_code ; Process and raise network error | |||||
| B974 | .done_toggle_station←1← B957 BEQ | |||||
| LDX cur_chan_attr ; Load channel attribute index | ||||||
| B977 | LDA fcb_flags,x ; Load station number for channel | |||||
| B97A | EOR #1 ; Toggle bit 0 (alternate station) | |||||
| B97C | STA fcb_flags,x ; Update station number | |||||
| B97F | PLA ; Restore X | |||||
| B980 | TAX ; X restored | |||||
| B981 | PLA ; Restore Y | |||||
| B982 | TAY ; Y restored | |||||
| B983 | 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.
|
||||||||
| B984 | .send_and_receive←2← B64F JSR← B6D6 JSR | |||||||
| JSR set_options_ptr ; Set up FS options pointer | ||||||||
| B987 | JMP setup_transfer_workspace ; Set up transfer workspace and return | |||||||
Read receive attribute byte from RX bufferReads byte at offset &0A in the network receive control block, used to track which channel owns the current receive buffer.
|
||||||
| B98A | .read_rx_attribute←4← 96F0 JSR← 9703 JSR← B642 JSR← B6C9 JSR | |||||
| LDY #&0a ; Y=&0A: receive attribute offset | ||||||
| B98C | LDA (net_rx_ptr),y ; Read byte from receive buffer | |||||
| B98E | RTS ; Return | |||||
Store receive attribute byte to RX bufferWrites A to offset &0A in the network receive control block, marking which channel owns the current receive buffer.
|
|||||||
| B98F | .store_rx_attribute←10← 8B6C JSR← 9C0C JSR← 9CCC JSR← 9D1C JSR← B4EF JSR← B656 JSR← B6DA JSR← B80B JSR← B830 JSR← B8D0 JSR | ||||||
| LDY #&0a ; Y=&0A: receive attribute offset | |||||||
| B991 | STA (net_rx_ptr),y ; Store byte to receive buffer | ||||||
| B993 | RTS ; 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. |
|
| B994 | .cmd_close |
| LDA #osfind_close ; A=0: close all open files | |
| B996 | TAY ; Y=&00 |
| B997 | 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.
|
||||
| B99A | .cmd_type | |||
| CLV ; Clear V for unconditional BVC | ||||
| B99B | 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.
|
||||
| B99D | .cmd_print | |||
| BIT bit_test_ff ; Set V flag (= print mode) | ||||
| B9A0 | .open_and_read_file←1← B99B BVC | |||
| JSR open_file_for_read ; Open file for reading | ||||
| B9A3 | LDY ws_page ; Y=file handle | |||
| B9A5 | LDA #0 ; A = 0 | |||
| B9A7 | STA table_idx ; Clear previous-character tracker | |||
| B9A9 | PHP ; Save V flag (print/type mode) | |||
| B9AA | .loop_read_print_byte←3← B9CA JMP← B9EA JMP← B9FD BEQ | |||
| JSR osbget ; Read a single byte from an open file Y | ||||
| B9AD | BCC done_print_escape ; Branch if not end of file | |||
| B9AF | PLP ; EOF: restore processor status | |||
| B9B0 | JSR close_ws_file ; Close the file | |||
| B9B3 | JMP osnewl ; Write newline (characters 10 and 13) | |||
| B9B6 | .done_print_escape←1← B9AD BCC | |||
| JSR abort_if_escape ; Check for escape key pressed | ||||
| B9B9 | PLP ; Restore V (print/type mode) | |||
| B9BA | PHP ; Re-save for next iteration | |||
| B9BB | BVS done_store_prev_char ; Print mode: skip CR/LF handling | |||
| B9BD | CMP #&0d ; Is it a carriage return? | |||
| B9BF | BEQ done_handle_line_end ; Yes: handle line ending | |||
| B9C1 | CMP #&0a ; Is it a line feed? | |||
| B9C3 | BEQ done_handle_line_end ; Yes: handle line ending | |||
| B9C5 | .done_store_prev_char←1← B9BB BVS | |||
| STA table_idx ; Save as previous character | ||||
| B9C7 | .loop_write_char←1← B9D8 BNE | |||
| JSR oswrch ; Write character | ||||
| B9CA | JMP loop_read_print_byte ; Loop for next byte | |||
| B9CD | .done_handle_line_end←2← B9BF BEQ← B9C3 BEQ | |||
| PHA ; Save the CR or LF character | ||||
| B9CE | LDX vdu_queue_count ; Check output destination flag | |||
| B9D1 | BEQ done_normalise_crlf ; Zero: normalise line endings | |||
| B9D3 | LDA #0 ; Non-zero: output raw | |||
| B9D5 | STA table_idx ; Clear previous-character tracker | |||
| B9D7 | PLA ; Retrieve CR/LF | |||
| B9D8 | BNE loop_write_char ; Output it directly; ALWAYS branch | |||
| B9DA | .done_normalise_crlf←1← B9D1 BEQ | |||
| LDA table_idx ; Get previous character | ||||
| B9DC | CMP #&0d ; Was previous a CR? | |||
| B9DE | BEQ done_check_cr_lf ; Yes: check for CR+LF pair | |||
| B9E0 | CMP #&0a ; Was previous a LF? | |||
| B9E2 | BEQ done_check_lf_cr ; Yes: check for LF+CR pair | |||
| B9E4 | PLA ; Retrieve CR/LF from stack | |||
| B9E5 | STA table_idx ; Save as previous character | |||
| B9E7 | .done_write_newline←2← B9F2 BNE← B9F7 BNE | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| B9EA | JMP loop_read_print_byte ; Loop for next byte | |||
| B9ED | .done_check_cr_lf←1← B9DE BEQ | |||
| PLA ; Retrieve current character | ||||
| B9EE | CMP #&0a ; Is it LF? (CR+LF pair) | |||
| B9F0 | BEQ done_consume_pair ; Yes: consume LF, no extra newline | |||
| B9F2 | BNE done_write_newline ; No: output extra newline ALWAYS branch | |||
| B9F4 | .done_check_lf_cr←1← B9E2 BEQ | |||
| PLA ; Retrieve current character | ||||
| B9F5 | CMP #&0d ; Is it CR? (LF+CR pair) | |||
| B9F7 | BNE done_write_newline ; No: output extra newline | |||
| B9F9 | .done_consume_pair←1← B9F0 BEQ | |||
| LDA #0 ; Pair consumed: A = 0 | ||||
| B9FB | STA table_idx ; Clear previous-character tracker | |||
| B9FD | 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. |
|
| B9FF | .abort_if_escape←2← B9B6 JSR← BA33 JSR |
| BIT escape_flag ; Test bit 7 of escape flag | |
| BA01 | BMI error_escape_pressed ; Escape pressed: handle abort |
| BA03 | RTS ; No escape: return |
| BA04 | .error_escape_pressed←1← BA01 BMI |
| JSR close_ws_file ; Close the open file | |
| BA07 | JSR osnewl ; Write newline (characters 10 and 13) |
| BA0A | LDA #osbyte_acknowledge_escape ; Acknowledge escape condition |
| BA0C | JSR osbyte ; Clear escape condition and perform escape effects |
| BA0F | LDA #&11 ; Error number &11 |
| BA11 | JSR error_inline ; Generate 'Escape' BRK error |
| BA14 | 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.
|
||||
| BA1B | .cmd_dump | |||
| JSR open_file_for_read ; Open file for reading, set ws_page | ||||
| BA1E | LDX #&14 ; 21 bytes to push (0-&14) | |||
| BA20 | LDA #0 ; Zero fill value | |||
| BA22 | .loop_push_zero_buf←1← BA24 BPL | |||
| PHA ; Push zero onto stack | ||||
| BA23 | DEX ; Count down | |||
| BA24 | BPL loop_push_zero_buf ; Loop until all 21 bytes pushed | |||
| BA26 | TSX ; X = stack pointer (buffer base - 1) | |||
| BA27 | JSR init_dump_buffer ; Set up buffer pointer and parse args | |||
| BA2A | LDA (work_ae),y ; Load display address low byte | |||
| BA2C | AND #&f0 ; Test high nibble | |||
| BA2E | BEQ loop_dump_line ; Skip header if 16-byte aligned | |||
| BA30 | JSR print_dump_header ; Print column header for offset start | |||
| BA33 | .loop_dump_line←2← BA2E BEQ← BAD5 JMP | |||
| JSR abort_if_escape ; Check for Escape key | ||||
| BA36 | LDA #&ff ; Start byte counter at -1 | |||
| BA38 | STA osword_flag ; Reset counter | |||
| BA3A | .loop_read_dump_byte←1← BA49 BNE | |||
| LDY ws_page ; Y=file handle | ||||
| BA3C | JSR osbget ; Read a single byte from an open file Y | |||
| BA3F | BCS done_check_dump_eof ; C=1 from OSBGET: end of file | |||
| BA41 | INC osword_flag ; Increment byte counter (0-15) | |||
| BA43 | LDY osword_flag ; Use counter as buffer index | |||
| BA45 | STA (work_ae),y ; Store byte in data buffer | |||
| BA47 | CPY #&0f ; Read 16 bytes? (index 0-15) | |||
| BA49 | BNE loop_read_dump_byte ; No: read next byte | |||
| BA4B | CLC ; C=0: not EOF, full line read | |||
| BA4C | .done_check_dump_eof←1← BA3F BCS | |||
| PHP ; Save C: EOF status | ||||
| BA4D | LDA osword_flag ; Check byte counter | |||
| BA4F | BPL done_check_boundary ; Counter >= 0: have data to display | |||
| BA51 | LDX #&15 ; 22 bytes to pop (21 buffer + PHP) | |||
| BA53 | .loop_pop_stack_buf←2← BA55 BPL← BADA JMP | |||
| PLA ; Pop one byte from stack | ||||
| BA54 | DEX ; Count down | |||
| BA55 | BPL loop_pop_stack_buf ; Loop until stack cleaned up | |||
| BA57 | JMP close_ws_file ; Close file and return | |||
| BA5A | .done_check_boundary←1← BA4F BPL | |||
| LDY #&10 ; Point to display address low byte | ||||
| BA5C | LDA (work_ae),y ; Load display address low byte | |||
| BA5E | AND #&f0 ; Test high nibble | |||
| BA60 | BNE done_start_dump_addr ; Non-zero: header already current | |||
| BA62 | JSR print_dump_header ; Crossed 256-byte boundary: new header | |||
| BA65 | .done_start_dump_addr←1← BA60 BNE | |||
| LDY #&13 ; Start from highest address byte | ||||
| BA67 | .loop_print_addr_byte←1← BA71 BNE | |||
| LDA (work_ae),y ; Load address byte | ||||
| BA69 | PHA ; Save for address increment later | |||
| BA6A | JSR print_hex_byte ; Print as two hex digits | |||
| BA6D | PLA ; Restore address byte | |||
| BA6E | DEY ; Next byte down | |||
| BA6F | CPY #&0f ; Printed all 4 address bytes? | |||
| BA71 | BNE loop_print_addr_byte ; No: print next address byte | |||
| BA73 | INY ; Y=&10: point to address byte 0 | |||
| BA74 | CLC ; Prepare for 16-byte add | |||
| BA75 | ADC #&10 ; Add 16 to lowest address byte | |||
| BA77 | PHP ; Save carry for propagation | |||
| BA78 | .loop_inc_dump_addr←1← BA83 BNE | |||
| PLP ; Restore carry from previous byte | ||||
| BA79 | STA (work_ae),y ; Store updated address byte | |||
| BA7B | INY ; Next address byte up | |||
| BA7C | LDA (work_ae),y ; Load next address byte | |||
| BA7E | ADC #0 ; Add carry | |||
| BA80 | PHP ; Save carry for next byte | |||
| BA81 | CPY #&14 ; Past all 4 address bytes? | |||
| BA83 | BNE loop_inc_dump_addr ; No: continue propagation | |||
| BA85 | PLP ; Discard final carry | |||
| BA86 | JSR print_inline ; Print address/data separator | |||
| BA89 | EQUS " : " | |||
| BA8C | LDY #0 ; Start from first data byte | |||
| BA8E | LDX osword_flag ; X = bytes read (counter for display) | |||
| BA90 | .loop_print_dump_hex←1← BA9B BPL | |||
| LDA (work_ae),y ; Load data byte from buffer | ||||
| BA92 | JSR print_hex_and_space ; Print as two hex digits | |||
| BA95 | .loop_next_dump_col←1← BAA8 JMP | |||
| INY ; Space separator Next column | ||||
| BA96 | CPY #&10 ; All 16 columns done? | |||
| BA98 | BEQ done_print_separator ; Yes: go to ASCII separator | |||
| BA9A | DEX ; Decrement remaining data bytes | |||
| BA9B | BPL loop_print_dump_hex ; More data: print next hex byte | |||
| BA9D | TYA ; Save column position | |||
| BA9E | PHA ; Preserve Y across print | |||
| BA9F | JSR print_inline ; Print 3-space padding | |||
| BAA2 | EQUS " " | |||
| BAA5 | NOP ; Inline string terminator (NOP) | |||
| BAA6 | PLA ; Restore column position | |||
| BAA7 | TAY ; Back to Y | |||
| BAA8 | JMP loop_next_dump_col ; Check next column | |||
| BAAB | .done_print_separator←1← BA98 BEQ | |||
| DEX ; Adjust X for advance_x_by_8 | ||||
| BAAC | JSR print_inline ; Print hex/ASCII separator | |||
| BAAF | EQUS ": " | |||
| BAB1 | LDY #0 ; Y=0: start ASCII section from byte 0 | |||
| BAB3 | JSR advance_x_by_8 ; Advance X to ASCII display column | |||
| BAB6 | .loop_print_dump_ascii←1← BACD BPL | |||
| LDA (work_ae),y ; Load data byte | ||||
| BAB8 | AND #&7f ; Strip high bit | |||
| BABA | CMP #&20 ; Printable? (>= space) | |||
| BABC | BCS done_test_del ; Yes: check for DEL | |||
| BABE | .skip_non_printable←1← BAC2 BEQ | |||
| LDA #&2e ; Non-printable: substitute '.' | ||||
| BAC0 | .done_test_del←1← BABC BCS | |||
| CMP #&7f ; Is it DEL (&7F)? | ||||
| BAC2 | BEQ skip_non_printable ; Yes: substitute '.' | |||
| BAC4 | JSR osasci ; Print ASCII character Write character | |||
| BAC7 | INY ; Next column | |||
| BAC8 | CPY #&10 ; All 16 columns done? | |||
| BACA | BEQ done_end_dump_line ; Yes: end of line | |||
| BACC | DEX ; Decrement remaining data bytes | |||
| BACD | BPL loop_print_dump_ascii ; More data: print next ASCII char | |||
| BACF | .done_end_dump_line←1← BACA BEQ | |||
| JSR osnewl ; Print newline Write newline (characters 10 and 13) | ||||
| BAD2 | PLP ; Restore EOF status from &BA4C | |||
| BAD3 | BCS done_dump_eof ; C=1: EOF reached, clean up | |||
| BAD5 | JMP loop_dump_line ; Not EOF: continue with next line | |||
| BAD8 | .done_dump_eof←1← BAD3 BCS | |||
| LDX #&14 ; 21 bytes to pop (buffer only, PHP done) | ||||
| BADA | 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. |
|
| BADD | .print_dump_header←2← BA30 JSR← BA62 JSR |
| LDA (work_ae),y ; Load display address low byte | |
| BADF | PHA ; Save as starting column number |
| BAE0 | JSR print_inline ; Print header label with leading CR |
| BAE3 | EQUS ".Address : " |
| BAEF | LDX #&0f ; X=&0F: 16 column numbers to print |
| BAF1 | PLA ; Restore starting column number |
| BAF2 | .loop_cbaf2←1← BAFB BPL |
| JSR print_hex_and_space ; Print as two hex digits | |
| BAF5 | SEC ; Space separator SEC for +1 via ADC |
| BAF6 | ADC #0 ; Increment column number (SEC+ADC 0=+1) |
| BAF8 | AND #&0f ; Wrap to low nibble (0-F) |
| BAFA | DEX ; Restore column number Count down |
| BAFB | BPL loop_cbaf2 ; Loop for all 16 columns |
| BAFD | JSR osnewl ; Print trailer with ASCII label Write newline (characters 10 and 13) |
| BB00 | JMP osnewl ; Write newline (characters 10 and 13) |
Print hex byte followed by spaceSaves A, prints it as a 2-digit hex value via print_hex_byte, outputs a space character, then restores A from the stack. Used by cmd_dump and print_dump_header for column-aligned hex output.
|
||||
| BB03 | .print_hex_and_space←2← BA92 JSR← BAF2 JSR | |||
| PHA ; Save byte value on stack | ||||
| BB04 | JSR print_hex_byte | |||
| BB07 | LDA #&20 ; A=&20: space character | |||
| BB09 | .sub_cbb09 | |||
| JSR osasci ; Print space character Write character 32 | ||||
| BB0C | .done_print_hex_space | |||
| PLA ; Restore byte value from stack | ||||
| BB0D | 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. |
|
| BB0E | .parse_dump_range←2← BB7E JSR← BC0A JSR |
| TYA ; Save command line offset to X | |
| BB0F | TAX ; X tracks current position |
| BB10 | LDA #0 ; Zero for clearing accumulator |
| BB12 | TAY ; Y=0 for buffer indexing Y=&00 |
| BB13 | .loop_clear_hex_accum←1← BB18 BNE |
| STA (work_ae),y ; Clear accumulator byte | |
| BB15 | INY ; Next byte |
| BB16 | CPY #4 ; All 4 bytes cleared? |
| BB18 | BNE loop_clear_hex_accum ; No: clear next |
| BB1A | .loop_parse_hex_digit←1← BB61 JMP |
| TXA ; Restore pre-increment offset to A | |
| BB1B | INX ; Advance X to next char position |
| BB1C | TAY ; Y = pre-increment offset for indexing |
| BB1D | LDA (os_text_ptr),y ; Load character from command line |
| BB1F | CMP #&0d ; CR: end of input |
| BB21 | BEQ done_test_hex_space ; Done: skip trailing spaces |
| BB23 | CMP #&20 ; Space: end of this parameter |
| BB25 | BEQ done_test_hex_space ; Done: skip trailing spaces |
| BB27 | CMP #&30 ; Below '0'? |
| BB29 | BCC error_bad_hex_value ; Yes: not a hex digit, error |
| BB2B | CMP #&3a ; Below ':'? (i.e. '0'-'9') |
| BB2D | BCC done_mask_hex_digit ; Yes: is a decimal digit |
| BB2F | AND #&5f ; Force uppercase for A-F |
| BB31 | ADC #&b8 ; Map 'A'-'F' → &FA-&FF (C=0 here) |
| BB33 | BCS error_bad_hex_value ; Carry set: char > 'F', error |
| BB35 | CMP #&fa ; Below &FA? (i.e. was < 'A') |
| BB37 | BCC error_bad_hex_value ; Yes: gap between '9' and 'A', error |
| BB39 | .done_mask_hex_digit←1← BB2D BCC |
| AND #&0f ; Mask to low nibble (0-15) | |
| BB3B | PHA ; Save hex digit value |
| BB3C | TXA ; Save current offset |
| BB3D | PHA ; Preserve on stack |
| BB3E | LDX #4 ; 4 bits to shift in |
| BB40 | .loop_shift_nibble←1← BB56 BNE |
| LDY #0 ; Start from byte 0 (LSB) | |
| BB42 | TYA ; Clear A; C from PHA/PLP below A=&00 |
| BB43 | .loop_rotate_hex_accum←1← BB4F BNE |
| PHA ; Transfer carry bit to flags via stack | |
| BB44 | PLP ; PLP: C = bit shifted out of prev iter |
| BB45 | LDA (work_ae),y ; Load accumulator byte |
| BB47 | ROL ; Rotate left through carry |
| BB48 | STA (work_ae),y ; Store shifted byte |
| BB4A | PHP ; Save carry for next byte |
| BB4B | PLA ; Transfer to A for PHA/PLP trick |
| BB4C | INY ; Next accumulator byte |
| BB4D | CPY #4 ; All 4 bytes rotated? |
| BB4F | BNE loop_rotate_hex_accum ; No: rotate next byte |
| BB51 | PHA ; Transfer carry to flags |
| BB52 | PLP ; C = overflow bit |
| BB53 | BCS error_hex_overflow ; Overflow: address too large |
| BB55 | DEX ; Count bits shifted |
| BB56 | BNE loop_shift_nibble ; 4 bits shifted? No: shift again |
| BB58 | PLA ; Restore command line offset |
| BB59 | TAX ; Back to X |
| BB5A | PLA ; Restore hex digit value |
| BB5B | LDY #0 ; Point to LSB of accumulator |
| BB5D | ORA (work_ae),y ; OR digit into low nibble |
| BB5F | STA (work_ae),y ; Store updated LSB |
| BB61 | JMP loop_parse_hex_digit ; Parse next character |
| BB64 | .error_hex_overflow←1← BB53 BCS |
| PLA ; Discard saved offset | |
| BB65 | PLA ; Discard saved digit |
| BB66 | SEC ; C=1: overflow |
| BB67 | RTS ; Return with C=1 |
| BB68 | .error_bad_hex_value←3← BB29 BCC← BB33 BCS← BB37 BCC |
| JSR close_ws_file ; Close open file before error | |
| BB6B | JMP err_bad_hex ; Generate 'Bad hex' error |
| BB6E | .loop_skip_hex_spaces←1← BB73 BEQ |
| INY ; Advance past space | |
| BB6F | .done_test_hex_space←2← BB21 BEQ← BB25 BEQ |
| LDA (os_text_ptr),y ; Load next char | |
| BB71 | CMP #&20 ; Space? |
| BB73 | BEQ loop_skip_hex_spaces ; Yes: skip it |
| BB75 | CLC ; C=0: valid parse (no overflow) |
| BB76 | 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. |
|
| BB77 | .init_dump_buffer←1← BA27 JSR |
| INX ; X+1: first byte of buffer | |
| BB78 | STX work_ae ; Set buffer pointer low byte |
| BB7A | LDX #1 ; Buffer is on stack in page 1 |
| BB7C | STX addr_work ; Set buffer pointer high byte |
| BB7E | JSR parse_dump_range ; Parse start offset from command line |
| BB81 | BCS error_outside_file ; Overflow: 'Outside file' error |
| BB83 | TYA ; A = command line offset after parse |
| BB84 | PHA ; Save for later (past start addr) |
| BB85 | LDY ws_page ; Y=file handle |
| BB87 | LDX #&aa ; X=zero page address for result |
| BB89 | LDA #2 ; A=2: read file extent (length) |
| BB8B | JSR osargs ; Get length of file into zero page address X (A=2) |
| BB8E | LDY #3 ; Check from MSB down |
| BB90 | .loop_cmp_file_length←1← BB98 BPL |
| LDA osword_flag,y ; Load file length byte | |
| BB93 | CMP (work_ae),y ; Compare with start offset byte |
| BB95 | BNE done_check_outside ; Mismatch: check which is larger |
| BB97 | DEY ; Next byte down |
| BB98 | BPL loop_cmp_file_length ; More bytes to compare |
| BB9A | BMI done_advance_start ; All equal: start = length, within file ALWAYS branch |
| BB9C | .done_check_outside←1← BB95 BNE |
| BCC error_outside_file ; Length < start: outside file | |
| BB9E | LDY #&ff ; Y=&FF: length > start, flag for later |
| BBA0 | BNE done_advance_start ; Continue to copy start address ALWAYS branch |
| BBA2 | .error_outside_file←2← BB81 BCS← BB9C BCC |
| JSR close_ws_file ; Close file before error | |
| BBA5 | LDA #&b7 ; Error number &B7 |
| BBA7 | JSR error_inline ; Generate 'Outside file' error |
| BBAA | EQUS "Outside file." |
| BBB7 | .loop_copy_osword_data←1← BBBF BNE |
| .loop_copy_start_addr←1← BBBF BNE | |
| LDA (work_ae),y ; Load start address byte from buffer | |
| BBB9 | STA osword_flag,y ; Store to osword_flag (&AA-&AD) |
| BBBC | .done_advance_start←2← BB9A BMI← BBA0 BNE |
| INY ; Next byte | |
| BBBD | CPY #4 ; All 4 bytes copied? |
| BBBF | BNE loop_copy_osword_data ; No: copy next byte |
| BBC1 | LDX #&aa ; X=zero page address to write from |
| BBC3 | LDY ws_page ; Y=file handle |
| BBC5 | LDA #1 ; A=1: write file pointer |
| BBC7 | JSR osargs ; OSARGS: set file pointer Write sequential file pointer from zero page address X (A=1) |
| BBCA | PLA ; Restore saved command line offset |
| BBCB | TAY ; Back to Y for command line indexing |
| BBCC | LDA (os_text_ptr),y ; Load next char from command line |
| BBCE | CMP #&0d ; End of command? (CR) |
| BBD0 | BNE done_parse_disp_base ; No: parse display base address |
| BBD2 | LDY #1 ; Copy 2 bytes: os_text_ptr to buffer |
| BBD4 | .loop_copy_osfile_ptr←1← BBDA BPL |
| LDA os_text_ptr,y ; Load os_text_ptr byte | |
| BBD7 | STA (work_ae),y ; Store as filename pointer in OSFILE CB |
| BBD9 | DEY ; Next byte |
| BBDA | BPL loop_copy_osfile_ptr ; Copy both low and high bytes |
| BBDC | LDA #osfile_read_catalogue_info ; Read catalogue information |
| BBDE | LDX work_ae ; X = control block low |
| BBE0 | LDY addr_work ; Y = control block high |
| BBE2 | JSR osfile ; OSFILE: read file info Read catalogue information (A=5) |
| BBE5 | LDY #2 ; Start at OSFILE +2 (load addr byte 0) |
| BBE7 | .loop_shift_osfile_data←1← BBF2 BNE |
| LDA (work_ae),y ; Load from OSFILE result offset | |
| BBE9 | DEY ; Y-2: destination is 2 bytes earlier |
| BBEA | DEY ; Continue decrement |
| BBEB | STA (work_ae),y ; Store to buf[Y-2] |
| BBED | INY ; Y += 3: advance source index |
| BBEE | INY ; (continued) |
| BBEF | INY ; (continued) |
| BBF0 | CPY #6 ; Copied all 4 load address bytes? |
| BBF2 | BNE loop_shift_osfile_data ; No: copy next byte |
| BBF4 | DEY ; Y=6 after loop exit |
| BBF5 | DEY ; Y=4: check from buf[4] downward |
| BBF6 | .loop_check_ff_addr←1← BBFD BNE |
| LDA (work_ae),y ; Load address byte | |
| BBF8 | CMP #&ff ; Is it &FF? |
| BBFA | BNE done_add_disp_base ; No: valid load address, use it |
| BBFC | DEY ; Check next byte down |
| BBFD | BNE loop_check_ff_addr ; More bytes to check for &FF |
| BBFF | LDY #3 ; Clear all 4 bytes |
| BC01 | LDA #0 ; Zero value |
| BC03 | .loop_zero_load_addr←1← BC06 BPL |
| STA (work_ae),y ; Clear byte | |
| BC05 | DEY ; Next byte down |
| BC06 | BPL loop_zero_load_addr ; Loop for all 4 bytes |
| BC08 | BMI done_add_disp_base ; Continue to compute display address ALWAYS branch |
| BC0A | .done_parse_disp_base←1← BBD0 BNE |
| JSR parse_dump_range ; Parse second hex parameter | |
| BC0D | BCC done_add_disp_base ; Valid: use as display base |
| BC0F | JSR close_ws_file ; Invalid: close file before error |
| BC12 | LDA #&fc ; Error number &FC |
| BC14 | JSR error_bad_inline ; Generate 'Bad address' error |
| BC17 | EQUS "address." |
| BC1F | .done_add_disp_base←3← BBFA BNE← BC08 BMI← BC0D BCC |
| LDY #0 ; Start from LSB | |
| BC21 | LDX #4 ; 4 bytes to add |
| BC23 | CLC ; Clear carry for addition |
| BC24 | .loop_add_disp_bytes←1← BC2E BNE |
| LDA (work_ae),y ; Load display base byte | |
| BC26 | ADC osword_flag,y ; Add start offset byte |
| BC29 | STA osword_flag,y ; Store result in osword_flag |
| BC2C | INY ; Next byte |
| BC2D | DEX ; Count down |
| BC2E | BNE loop_add_disp_bytes ; Loop for all 4 bytes |
| BC30 | LDY #&14 ; Point past end of address area |
| BC32 | LDX #3 ; Start from MSB (byte 3) |
| BC34 | .loop_store_disp_addr←1← BC3A BPL |
| DEY ; Pre-decrement Y | |
| BC35 | LDA osword_flag,x ; Load computed display address byte |
| BC37 | STA (work_ae),y ; Store to buf[&10-&13] |
| BC39 | DEX ; Next byte down |
| BC3A | BPL loop_store_disp_addr ; Loop for all 4 bytes |
| BC3C | RTS ; Return; Y=&10 (address low byte) |
Close file handle stored in workspaceLoads the file handle from ws_page and closes it via OSFIND with A=0. |
|
| BC3D | .close_ws_file←6← B9B0 JSR← BA04 JSR← BA57 JMP← BB68 JSR← BBA2 JSR← BC0F JSR |
| LDY ws_page ; Load file handle from workspace | |
| BC3F | LDA #osfind_close ; A=0: close file |
| BC41 | JMP osfind ; 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. |
|
| BC44 | .open_file_for_read←2← B9A0 JSR← BA1B JSR |
| PHP ; Save caller flags | |
| BC45 | TYA ; A=filename offset from Y |
| BC46 | CLC ; Clear carry for 16-bit addition |
| BC47 | ADC os_text_ptr ; Add text pointer low byte |
| BC49 | PHA ; Save filename address low |
| BC4A | TAX ; X=filename address low (for OSFIND) |
| BC4B | LDA #0 ; A=0: carry propagation only |
| BC4D | ADC os_text_ptr_hi ; Add text pointer high byte + carry |
| BC4F | PHA ; Save filename address high |
| BC50 | TAY ; Y=filename address high (for OSFIND) |
| BC51 | LDA #osfind_open_input ; A=&40: open for reading |
| BC53 | JSR osfind ; Open file for input (A=64) |
| BC56 | TAY ; A=file handle (or zero on failure) |
| BC57 | STA ws_page ; Store file handle in workspace |
| BC59 | BNE restore_text_ptr ; Non-zero: file opened successfully |
| BC5B | LDA #&d6 ; Error &D6 |
| BC5D | JSR error_inline ; Raise 'Not found' error |
| BC60 | EQUS "Not found." |
| BC6A | .restore_text_ptr←1← BC59 BNE |
| PLA ; Restore text pointer high from stack | |
| BC6B | STA os_text_ptr_hi ; Set OS text pointer high |
| BC6D | PLA ; Restore text pointer low from stack |
| BC6E | STA os_text_ptr ; Set OS text pointer low |
| BC70 | LDY #0 ; Y=0: start from beginning |
| BC72 | .loop_skip_filename←1← BC7B BNE |
| INY ; Advance to next character | |
| BC73 | LDA (os_text_ptr),y ; Load character from command text |
| BC75 | CMP #&0d ; CR (end of line)? |
| BC77 | BEQ done_skip_filename ; Yes: finished parsing filename |
| BC79 | CMP #&20 ; Space (word separator)? |
| BC7B | BNE loop_skip_filename ; No: still within filename |
| BC7D | .loop_skip_fn_spaces←1← BC82 BEQ |
| INY ; Advance past space | |
| BC7E | LDA (os_text_ptr),y ; Load next character |
| BC80 | CMP #&20 ; Another space? |
| BC82 | BEQ loop_skip_fn_spaces ; Yes: skip consecutive spaces |
| BC84 | .done_skip_filename←1← BC77 BEQ |
| PLP ; Restore caller flags | |
| BC85 | RTS ; Return; Y=offset past filename |
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. |
|
| BC86 | .advance_x_by_8←3← 9BED JSR← A8B0 JSR← BAB3 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. |
|
| BC89 | .advance_x_by_4←1← BC86 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 | |
|---|---|---|
| BC8C | .inx4←1← BC89 JSR | |
| INX ; X += 4 | ||
| BC8D | INX ; (continued) | |
| BC8E | INX ; (continued) | |
| BC8F | INX ; (continued) | |
| BC90 | RTS ; Return | |
| BC91 | EQUB &FF, &FF | |
| BC93 | EQUB &FF ; Padding; next byte is reloc_p5_src | |
| BC94 | 0500 | .tube_r2_dispatch_table←2← 0050 JMP← BEA7 STA |
| EQUW tube_osrdch ; R2 cmd 0: OSRDCH | ||
| BC96 | 0502 | EQUW tube_oscli ; R2 cmd 1: OSCLI |
| BC98 | 0504 | EQUW tube_osbyte_2param ; R2 cmd 2: OSBYTE (2-param) |
| BC9A | 0506 | EQUW tube_osbyte_long ; R2 cmd 3: OSBYTE (3-param) |
| BC9C | 0508 | EQUW tube_osword ; R2 cmd 4: OSWORD |
| BC9E | 050A | EQUW tube_osword_rdln ; R2 cmd 5: OSWORD 0 (read line) |
| BCA0 | 050C | EQUW tube_osargs ; R2 cmd 6: OSARGS |
| BCA2 | 050E | EQUW tube_osbget ; R2 cmd 7: OSBGET |
| BCA4 | 0510 | EQUW tube_osbput ; R2 cmd 8: OSBPUT |
| BCA6 | 0512 | EQUW tube_osfind ; R2 cmd 9: OSFIND |
| BCA8 | 0514 | EQUW tube_osfile ; R2 cmd 10: OSFILE |
| BCAA | 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. | ||
| BCAC | 0518 | .tube_ctrl_values←1← 0453 LDA |
| EQUB &86 ; Type 0: set I+J (1-byte R3, parasite to host) | ||
| BCAD | 0519 | EQUB &88 ; Type 1: set M (1-byte R3, host to parasite) |
| BCAE | 051A | EQUB &96 ; Type 2: set V+I+J (2-byte R3, parasite to host) |
| BCAF | 051B | EQUB &98 ; Type 3: set V+M (2-byte R3, host to parasite) |
| BCB0 | 051C | EQUB &18 ; Type 4: clear V+M (execute code at address) |
| BCB1 | 051D | EQUB &18 ; Type 5: clear V+M (release address claim) |
| BCB2 | 051E | EQUB &82 ; Type 6: set I (define event handler) |
| BCB3 | 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. | ||
| BCB4 | 0520 | .tube_osbput |
| JSR tube_read_r2 ; Read channel handle from R2 for BPUT | ||
| BCB7 | 0523 | TAY ; Y=channel handle for OSBPUT |
| BCB8 | 0524 | JSR tube_read_r2 ; Read data byte from R2 for OSBPUT |
| BCBB | 0527 | .tube_poll_r1_wrch |
| JSR osbput ; Write a single byte A to an open file Y | ||
| BCBE | 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. | ||
| BCC1 | 052D | .tube_osbget |
| JSR tube_read_r2 ; Read channel handle from R2 for BGET | ||
| BCC4 | 0530 | TAY ; Y=file handle |
| BCC5 | 0531 | JSR osbget ; Read a single byte from an open file Y |
| BCC8 | 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. | ||
| BCCB | 0537 | .tube_osrdch |
| JSR osrdch ; Read a character from the current input stream | ||
| BCCE | 053A | .tube_rdch_reply←2← 0534 JMP← 05EF JMP |
| ROR ; ROR A: encode carry (error flag) into bit 7 | ||
| BCCF | 053B | JSR tube_send_r2 ; Send carry+data byte to Tube R2 |
| BCD2 | 053E | ROL ; ROL A: restore carry flag |
| BCD3 | 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. | ||
| BCD6 | 0542 | .tube_osfind |
| JSR tube_read_r2 ; Read open mode from R2 for OSFIND | ||
| BCD9 | 0545 | BEQ tube_osfind_close ; Mode=0: close file(s) |
| BCDB | 0547 | PHA ; Save open mode on stack |
| BCDC | 0548 | JSR tube_read_string ; Read filename string from R2 |
| BCDF | 054B | PLA ; Restore open mode |
| BCE0 | 054C | JSR osfind ; Open or close file(s) |
| BCE3 | 054F | JMP tube_reply_byte ; Reply with file handle via R2 |
| BCE6 | 0552 | .tube_osfind_close←1← 0545 BEQ |
| JSR tube_read_r2 ; OSFIND close: read handle from R2 | ||
| BCE9 | 0555 | TAY ; Transfer handle to Y |
| BCEA | 0556 | LDA #osfind_close ; A=0: close file |
| BCEC | 0558 | JSR osfind ; Close one or all files |
| BCEF | 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. | ||
| BCF2 | 055E | .tube_osargs |
| JSR tube_read_r2 ; Read file handle from R2 for OSARGS | ||
| BCF5 | 0561 | TAY ; Y=file handle for OSARGS |
| BCF6 | 0562 | .tube_read_params |
| LDX #4 ; Read 4-byte arg + reason from R2 into ZP | ||
| BCF8 | 0564 | .read_osargs_params←1← 056A BNE |
| JSR tube_read_r2 ; Read next param byte from R2 | ||
| BCFB | 0567 | STA escape_flag,x ; Store param at ZP+X (escape_flag downward) |
| BCFD | 0569 | DEX ; Decrement index |
| BCFE | 056A | BNE read_osargs_params ; More params: continue reading |
| BD00 | 056C | JSR tube_read_r2 ; Read OSARGS reason code from R2 |
| BD03 | 056F | JSR osargs ; Read or write a file's attributes |
| BD06 | 0572 | JSR tube_send_r2 ; Send result A via R2 |
| BD09 | 0575 | LDX #3 ; X=3: send 4 result bytes |
| BD0B | 0577 | .send_osargs_result←1← 057D BPL |
| LDA zp_ptr_lo,x ; Load result byte from zero page | ||
| BD0D | 0579 | JSR tube_send_r2 ; Send result byte via R2 |
| BD10 | 057C | DEX ; Decrement byte counter |
| BD11 | 057D | BPL send_osargs_result ; More bytes: continue sending |
| BD13 | 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) | ||
| BD16 | 0582 | .tube_read_string←3← 0548 JSR← 0596 JSR← 05B3 JSR |
| LDX #0 ; X=0: initialise string buffer index | ||
| BD18 | 0584 | LDY #0 ; Y=0: initialise string offset |
| BD1A | 0586 | .strnh←1← 0591 BNE |
| JSR tube_read_r2 ; Read next string byte from R2 | ||
| BD1D | 0589 | STA string_buf,y ; Store in string buffer at &0700+Y |
| BD20 | 058C | INY ; Advance string index |
| BD21 | 058D | BEQ string_buf_done ; Buffer full (256 bytes): done |
| BD23 | 058F | CMP #&0d ; Check for CR terminator |
| BD25 | 0591 | BNE strnh ; Not CR: continue reading |
| BD27 | 0593 | .string_buf_done←1← 058D BEQ |
| LDY #7 ; Y=7: set XY=&0700 for OSCLI/OSFIND | ||
| BD29 | 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. | ||
| BD2A | 0596 | .tube_oscli |
| JSR tube_read_string ; Read command string from R2 into &0700 | ||
| BD2D | 0599 | JSR oscli ; Execute command string via OSCLI |
| BD30 | 059C | .tube_reply_ack←3← 0489 JMP← 052A JMP← 055B JMP |
| LDA #&7f ; &7F = success acknowledgement | ||
| BD32 | 059E | .tube_reply_byte←4← 053F JMP← 054F JMP← 05A1 BVC← 067D JMP |
| BIT tube_status_register_2 ; Poll R2 status until ready | ||
| BD35 | 05A1 | BVC tube_reply_byte ; Bit 6 clear: not ready, loop |
| BD37 | 05A3 | STA tube_data_register_2 ; Write byte to R2 data register |
| BD3A | 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. | ||
| BD3D | 05A9 | .tube_osfile |
| LDX #&10 ; Read 16-byte OSFILE control block from R2 | ||
| BD3F | 05AB | .argsw←1← 05B1 BNE |
| JSR tube_read_r2 ; Read next control block byte from R2 | ||
| BD42 | 05AE | STA zp_ptr_hi,x ; Store at ZP+X (control block) |
| BD44 | 05B0 | DEX ; Decrement index |
| BD45 | 05B1 | BNE argsw ; More bytes: continue reading |
| BD47 | 05B3 | JSR tube_read_string ; Read filename string from R2 |
| BD4A | 05B6 | STX zp_ptr_lo ; Set filename ptr low = 0 |
| BD4C | 05B8 | STY zp_ptr_hi ; Set filename ptr high = &07 |
| BD4E | 05BA | LDY #0 ; Y=0: OSFILE reason code index |
| BD50 | 05BC | JSR tube_read_r2 ; Read OSFILE reason code from R2 |
| BD53 | 05BF | JSR osfile ; Execute OSFILE |
| BD56 | 05C2 | JSR tube_send_r2 ; Send result A via R2 |
| BD59 | 05C5 | LDX #&10 ; X=&10: send 16 result bytes |
| BD5B | 05C7 | .send_osfile_ctrl_blk←1← 05CD BNE |
| LDA zp_ptr_hi,x ; Load control block byte | ||
| BD5D | 05C9 | JSR tube_send_r2 ; Send control block byte via R2 |
| BD60 | 05CC | DEX ; Decrement byte counter |
| BD61 | 05CD | BNE send_osfile_ctrl_blk ; More bytes: continue sending |
| BD63 | 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. | ||
| BD65 | 05D1 | .tube_osgbpb |
| LDX #&0d ; X=&0D: read 13-byte OSGBPB ctrl block | ||
| BD67 | 05D3 | .read_osgbpb_ctrl_blk←1← 05D9 BNE |
| JSR tube_read_r2 ; Read next control block byte from R2 | ||
| BD6A | 05D6 | STA escape_flag,x ; Store at ZP+X (escape_flag downward) |
| BD6C | 05D8 | DEX ; Decrement index |
| BD6D | 05D9 | BNE read_osgbpb_ctrl_blk ; More bytes: continue reading |
| BD6F | 05DB | JSR tube_read_r2 ; Read OSGBPB reason code from R2 |
| BD72 | 05DE | LDY #0 ; Y=0: OSGBPB direction/count |
| BD74 | 05E0 | JSR osgbpb ; Read or write multiple bytes to an open file |
| BD77 | 05E3 | PHA ; Save result A on stack |
| BD78 | 05E4 | LDX #&0c ; X=&0C: send 12 result bytes |
| BD7A | 05E6 | .send_osgbpb_result←1← 05EC BPL |
| LDA zp_ptr_lo,x ; Load result byte from zero page | ||
| BD7C | 05E8 | JSR tube_send_r2 ; Send result byte via R2 |
| BD7F | 05EB | DEX ; Decrement byte counter |
| BD80 | 05EC | BPL send_osgbpb_result ; More bytes: continue sending |
| BD82 | 05EE | PLA ; Recover completion status from stack |
| BD83 | 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. | ||
| BD86 | 05F2 | .tube_osbyte_2param |
| JSR tube_read_r2 ; Read X parameter from R2 | ||
| BD89 | 05F5 | TAX ; Transfer to X register |
| BD8A | 05F6 | JSR tube_read_r2 ; Read A (OSBYTE function code) from R2 |
| BD8D | 05F9 | JSR osbyte ; Execute OSBYTE A,X |
| BD90 | 05FC | .tube_poll_r2_result←2← 05FF ref← 0625 BVS |
| BIT tube_status_register_2 ; Poll R2 status for result send | ||
| BD93 | 05FF | EQUB &50 ; BVC: page 5/6 boundary straddle |
| BD94 | 0600 | .tube_osbyte_reply_block←1← BEAD STA |
| EQUB &FB ; Send carry+status to co-processor via R2 | ||
| BD95 | 0601 | STX tube_data_register_2 ; Send X result for 2-param OSBYTE |
| BD98 | 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. | ||
| BD9B | 0607 | .tube_osbyte_long |
| JSR tube_read_r2 ; Read X, Y, A from R2 for 3-param OSBYTE | ||
| BD9E | 060A | TAX ; Save in X |
| BD9F | 060B | JSR tube_read_r2 ; Read Y parameter from co-processor |
| BDA2 | 060E | TAY ; Save in Y |
| BDA3 | 060F | JSR tube_read_r2 ; Read A (OSBYTE function code) |
| BDA6 | 0612 | JSR osbyte ; Execute OSBYTE A,X,Y |
| BDA9 | 0615 | EOR #&9d ; Test for OSBYTE &9D (fast Tube BPUT) |
| BDAB | 0617 | BEQ bytex ; OSBYTE &9D (fast Tube BPUT): no result needed |
| BDAD | 0619 | ROR ; Encode carry (error flag) into bit 7 |
| BDAE | 061A | JSR tube_send_r2 ; Send carry+status byte via R2 |
| BDB1 | 061D | .tube_osbyte_send_y←1← 0620 BVC |
| BIT tube_status_register_2 ; Poll R2 status for ready | ||
| BDB4 | 0620 | BVC tube_osbyte_send_y ; Not ready: keep polling |
| BDB6 | 0622 | STY tube_data_register_2 ; Send Y result, then fall through to send X |
| BDB9 | 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. | ||
| BDBB | 0627 | .tube_osword |
| JSR tube_read_r2 ; Overlapping entry: &20 = JSR c06c5 (OSWORD) | ||
| BDBE | 062A | TAY ; Save OSWORD number in Y |
| BDBF | 062B | .tube_osword_read←1← 062E BPL |
| BIT tube_status_register_2 ; Poll R2 status for data ready | ||
| BDC2 | 062E | BPL tube_osword_read ; Not ready: keep polling |
| BDC4 | 0630 | .tube_osbyte_send_x |
| LDX tube_data_register_2 ; Read param block length from R2 | ||
| BDC7 | 0633 | DEX ; DEX: length 0 means no params to read |
| BDC8 | 0634 | BMI skip_param_read ; No params (length=0): skip read loop |
| BDCA | 0636 | .tube_osword_read_lp←2← 0639 BPL← 0642 BPL |
| BIT tube_status_register_2 ; Poll R2 status for data ready | ||
| BDCD | 0639 | BPL tube_osword_read_lp ; Not ready: keep polling |
| BDCF | 063B | LDA tube_data_register_2 ; Read param byte from R2 |
| BDD2 | 063E | STA tube_osword_pb,x ; Store param bytes into block at &0128 |
| BDD5 | 0641 | DEX ; Next param byte (descending) |
| BDD6 | 0642 | BPL tube_osword_read_lp ; Loop until all params read |
| BDD8 | 0644 | TYA ; Restore OSWORD number from Y |
| BDD9 | 0645 | .skip_param_read←1← 0634 BMI |
| LDX #<(tube_osword_pb) ; XY=&0128: param block address for OSWORD | ||
| BDDB | 0647 | LDY #>(tube_osword_pb) ; Y=&01: param block at &0128 |
| BDDD | 0649 | JSR osword ; Execute OSWORD with XY=&0128 |
| BDE0 | 064C | .poll_r2_osword_result←1← 064F BPL |
| BIT tube_status_register_2 ; Poll R2 status for ready | ||
| BDE3 | 064F | BPL poll_r2_osword_result ; Not ready: keep polling |
| BDE5 | 0651 | LDX tube_data_register_2 ; Read result block length from R2 |
| BDE8 | 0654 | DEX ; Decrement result byte counter |
| BDE9 | 0655 | BMI tube_return_main ; No results to send: return to main loop |
| BDEB | 0657 | .tube_osword_write←1← 0663 BPL |
| LDY tube_osword_pb,x ; Send result block bytes from &0128 via R2 | ||
| BDEE | 065A | .tube_osword_write_lp←1← 065D BVC |
| BIT tube_status_register_2 ; Poll R2 status for ready | ||
| BDF1 | 065D | BVC tube_osword_write_lp ; Not ready: keep polling |
| BDF3 | 065F | STY tube_data_register_2 ; Send result byte via R2 |
| BDF6 | 0662 | DEX ; Next result byte (descending) |
| BDF7 | 0663 | BPL tube_osword_write ; Loop until all results sent |
| BDF9 | 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). | ||
| BDFC | 0668 | .tube_osword_rdln |
| LDX #4 ; Read 5-byte OSWORD 0 control block from R2 | ||
| BDFE | 066A | .read_rdln_ctrl_block←1← 0670 BPL |
| JSR tube_read_r2 ; Read control block byte from R2 | ||
| BE01 | 066D | STA zp_ptr_lo,x ; Store in zero page params |
| BE03 | 066F | DEX ; Next byte (descending) |
| BE04 | 0670 | BPL read_rdln_ctrl_block ; Loop until all 5 bytes read |
| BE06 | 0672 | INX ; X=0 after loop, A=0 for OSWORD 0 (read line) |
| BE07 | 0673 | LDY #0 ; Y=0 for OSWORD 0 |
| BE09 | 0675 | TXA ; A=0: OSWORD 0 (read line) |
| BE0A | 0676 | JSR osword ; Read input line from keyboard |
| BE0D | 0679 | BCC tube_rdln_send_line ; C=0: line read OK; C=1: escape pressed |
| BE0F | 067B | LDA #&ff ; &FF = escape/error signal to co-processor |
| BE11 | 067D | JMP tube_reply_byte ; Escape: send &FF error to co-processor |
| BE14 | 0680 | .tube_rdln_send_line←1← 0679 BCC |
| LDX #0 ; X=0: start of input buffer at &0700 | ||
| BE16 | 0682 | LDA #&7f ; &7F = line read successfully |
| BE18 | 0684 | JSR tube_send_r2 ; Send &7F (success) to co-processor |
| BE1B | 0687 | .tube_rdln_send_loop←1← 0690 BNE |
| LDA string_buf,x ; Load char from input buffer | ||
| BE1E | 068A | .tube_rdln_send_byte |
| JSR tube_send_r2 ; Send char to co-processor | ||
| BE21 | 068D | INX ; Next character |
| BE22 | 068E | CMP #&0d ; Check for CR terminator |
| BE24 | 0690 | BNE tube_rdln_send_loop ; Loop until CR terminator sent |
| BE26 | 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) | ||
| BE29 | 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) | ||
| BE2C | 0698 | BVC tube_send_r2 ; Not ready: keep polling |
| BE2E | 069A | STA tube_data_register_2 ; Write A to Tube R2 data register |
| BE31 | 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) | ||
| BE32 | 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) | ||
| BE35 | 06A1 | BVC tube_send_r4 ; Not ready: keep polling |
| BE37 | 06A3 | STA tube_data_register_4 ; Write A to Tube R4 data register |
| BE3A | 06A6 | RTS ; Return to caller |
| BE3B | 06A7 | .tube_escape_check←1← 0403 JMP |
| LDA escape_flag ; Check OS escape flag at &FF | ||
| BE3D | 06A9 | SEC ; SEC+ROR: put bit 7 of &FF into carry+bit 7 |
| BE3E | 06AA | ROR ; ROR: shift escape bit 7 to carry |
| BE3F | 06AB | BMI tube_send_r1 ; Escape set: forward to co-processor via R1 |
| BE41 | 06AD | .tube_event_handler |
| PHA ; EVNTV: forward event A, Y, X to co-processor | ||
| BE42 | 06AE | LDA #0 ; Send &00 prefix (event notification) |
| BE44 | 06B0 | JSR tube_send_r1 ; Send zero prefix via R1 |
| BE47 | 06B3 | TYA ; Y value for event |
| BE48 | 06B4 | JSR tube_send_r1 ; Send Y via R1 |
| BE4B | 06B7 | TXA ; X value for event |
| BE4C | 06B8 | JSR tube_send_r1 ; Send X via R1 |
| BE4F | 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) | ||
| BE50 | 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) | ||
| BE53 | 06BF | BVC tube_send_r1 ; Not ready: keep polling |
| BE55 | 06C1 | STA tube_data_register_1 ; Write A to Tube R1 data register |
| BE58 | 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 | ||
| BE59 | 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) | ||
| BE5C | 06C8 | BPL tube_read_r2 ; Not ready: keep polling |
| BE5E | 06CA | LDA tube_data_register_2 ; Read data byte from R2 |
| BE61 | 06CD | RTS ; Return with byte in A |
| BE62 | 06CE | CMP #&fe ; Is byte &FE (VDU stream start)? |
| BE64 | 06D0 | BCC tube_vdu_normal_byte ; Below &FE: normal byte |
| BE66 | 06D2 | BNE setup_tube_vectors ; &FF: set up event/break vectors |
| BE68 | 06D4 | CPY #0 ; &FE: check Y parameter |
| BE6A | 06D6 | BEQ tube_vdu_normal_byte ; Y=0: treat as normal byte |
| BE6C | 06D8 | LDX #6 ; X=6: six extra pages |
| BE6E | 06DA | LDA #osbyte_explode_chars ; OSBYTE &14: explode char defs |
| BE70 | 06DC | JSR osbyte ; Explode character definition RAM (six extra pages), can redefine all characters 32-255 (X=6) |
| BE73 | 06DF | .loop_poll_r1_vdu←1← 06E2 BPL |
| BIT tube_status_1_and_tube_control ; Poll R1 status (bit 6 = ready) | ||
| BE76 | 06E2 | BPL loop_poll_r1_vdu ; Not ready: keep polling |
| BE78 | 06E4 | LDA tube_data_register_1 ; Read byte from Tube R1 |
| BE7B | 06E7 | BEQ tube_vdu_stream_end ; Zero: end of VDU stream |
| BE7D | 06E9 | JSR oswrch ; Write character |
| BE80 | 06EC | .svc_11_nmi_claim |
| JMP loop_poll_r1_vdu_rom ; Loop back to read next R1 byte | ||
| BE83 | 06EF | .setup_tube_vectors←1← 06D2 BNE |
| LDA #&ad ; EVNTV low byte (&AD) | ||
| BE85 | 06F1 | STA evntv ; Store in EVNTV vector low |
| BE88 | 06F4 | LDA #6 ; EVNTV high byte (page 6) |
| BE8A | 06F6 | STA evntv+1 ; Store in EVNTV vector high |
| BE8D | 06F9 | LDA #&16 ; BRKV low byte (&16) |
| BE8F | 06FB | STA brkv ; Store in BRKV vector |
| BE92 | 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. | ||
| BE94 | STA brkv+1 ; Store BRK vector high byte | |
| BE97 | LDA #&8e ; A=&8E: Tube control register value | |
| BE99 | STA tube_status_1_and_tube_control ; Write Tube control register | |
| BE9C | LDY #0 ; Y=0: copy 256 bytes per page | |
| BE9E | .loop_copy_reloc_pages←1← BEB1 BNE | |
| LDA reloc_p4_src,y ; Load page 4 source byte | ||
| BEA1 | STA tube_page4_vectors,y ; Store to page 4 destination | |
| BEA4 | LDA reloc_p5_src,y ; Load page 5 source byte | |
| BEA7 | STA tube_r2_dispatch_table,y ; Store to page 5 destination | |
| BEAA | LDA reloc_p6_src,y ; Load page 6 source byte | |
| BEAD | STA tube_osbyte_reply_block,y ; Store to page 6 destination | |
| BEB0 | DEY ; Decrement byte counter | |
| BEB1 | BNE loop_copy_reloc_pages ; Non-zero: continue copying | |
| BEB3 | JSR clear_tube_claim ; Clear tube claim state | |
| BEB6 | LDX #&41 ; X=&41: copy 66 bytes of ZP workspace | |
| BEB8 | .loop_copy_zp_workspace←1← BEBE BPL | |
| LDA reloc_zp_src,x ; Load ZP source byte from ROM | ||
| BEBB | STA nmi_workspace_start,x ; Store to NMI workspace at &16+X | |
| BEBD | DEX ; Decrement byte counter | |
| BEBE | BPL loop_copy_zp_workspace ; More bytes: continue copying | |
| BEC0 | LDA #0 ; A=0: return success | |
| BEC2 | RTS ; Return to caller | |
| BEC3 | 0016 | .nmi_workspace_start←1← BEBB STA |
| LDA #&ff ; A=&FF: signal error to co-processor via R4 | ||
| BEC5 | 0018 | JSR tube_send_r4 ; Send &FF error signal to Tube R4 |
| BEC8 | 001B | LDA tube_data_register_2 ; Flush any pending R2 byte |
| BECB | 001E | LDA #0 ; A=0: send zero prefix to R2 |
| BECD | 0020 | .tube_send_zero_r2 |
| JSR tube_send_r2 ; Send zero prefix byte via R2 | ||
| BED0 | 0023 | TAY ; Y=0: start of error block at (&FD) |
| BED1 | 0024 | LDA (brk_ptr),y ; Load error number from (&FD),0 |
| BED3 | 0026 | .tube_send_error_num |
| JSR tube_send_r2 ; Send error number via R2 | ||
| BED6 | 0029 | .tube_brk_send_loop←1← 0030 BNE |
| INY ; Advance to next error string byte | ||
| BED7 | 002A | .tube_send_error_byte |
| LDA (brk_ptr),y ; Load next error string byte | ||
| BED9 | 002C | JSR tube_send_r2 ; Send error string byte via R2 |
| BEDC | 002F | TAX ; Zero byte = end of error string |
| BEDD | 0030 | BNE tube_brk_send_loop ; Loop until zero terminator sent |
| BEDF | 0032 | .tube_reset_stack←1← 0477 JMP |
| LDX #&ff ; Reset stack pointer to top | ||
| BEE1 | 0034 | TXS ; TXS: set stack pointer from X |
| BEE2 | 0035 | CLI ; Enable interrupts for main loop |
| BEE3 | 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 | ||
| BEE6 | 0039 | BPL tube_poll_r2 ; R1 not ready: check R2 instead |
| BEE8 | 003B | .tube_handle_wrch←1← 0049 BMI |
| LDA tube_data_register_1 ; Read character from Tube R1 data | ||
| BEEB | 003E | JSR oswrch ; Write character |
| BEEE | 0041 | .tube_poll_r2←1← 0039 BPL |
| BIT tube_status_register_2 ; BIT R2 status: check command byte | ||
| BEF1 | 0044 | BPL tube_main_loop ; R2 not ready: loop back to R1 check |
| BEF3 | 0046 | BIT tube_status_1_and_tube_control ; Re-check R1: WRCH has priority over R2 |
| BEF6 | 0049 | BMI tube_handle_wrch ; R1 ready: handle WRCH first |
| BEF8 | 004B | LDX tube_data_register_2 ; Read command byte from Tube R2 data |
| BEFB | 004E | STX tube_cmd_lo ; Self-modify JMP low byte for dispatch |
| BEFD | 0050 | .tube_dispatch_cmd |
| JMP (tube_r2_dispatch_table) ; Dispatch to handler via indirect JMP | ||
| BF00 | 0053 | .tube_transfer_addr←2← 04DA STY← 04EA STA |
| EQUB &00 ; Tube transfer address low byte | ||
| BF01 | 0054 | .tube_xfer_page←3← 04B2 INC← 04D0 STA← 04EF STA |
| EQUB &80 ; Tube transfer page (default &80) | ||
| BF02 | 0055 | .tube_xfer_addr_2←2← 04B6 INC← 04F9 STY |
| EQUB &00 ; Tube transfer address byte 2 | ||
| BF03 | 0056 | .tube_xfer_addr_3←2← 04BA INC← 04F7 STA |
| EQUB &00 ; Tube transfer address byte 3 | ||
| BF04 | 0400 | .tube_page4_vectors←1← BEA1 STA |
| JMP tube_begin ; JMP to BEGIN startup entry | ||
| BF07 | 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) | ||
| BF0A | 0406 | .tube_addr_data_dispatch←10← 0493 JSR← 04CB JMP← 837C JSR← 844F JSR← 8933 JSR← 893B JSR← 9FF2 JSR← A009 JSR← A075 JSR← A2EC JMP |
| CMP #&80 ; A>=&80: address claim; A<&80: data transfer | ||
| BF0C | 0408 | BCC tube_transfer_setup ; A<&80: data transfer setup (SENDW) |
| BF0E | 040A | CMP #&c0 ; A>=&C0: new address claim from another host |
| BF10 | 040C | BCS addr_claim_external ; C=1: external claim, check ownership |
| BF12 | 040E | ORA #&40 ; Map &80-&BF range to &C0-&FF for comparison |
| BF14 | 0410 | CMP tube_claimed_id ; Is this for our currently-claimed address? |
| BF16 | 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. | ||
| BF18 | 0414 | .tube_release_claim←1← 0471 JSR |
| PHP ; PHP: save interrupt state for release | ||
| BF19 | 0415 | SEI ; SEI: disable interrupts during R4 protocol |
| BF1A | 0416 | LDA #5 ; R4 cmd 5: release our address claim |
| BF1C | 0418 | JSR tube_send_r4 ; Send release command to co-processor |
| BF1F | 041B | LDA tube_claimed_id ; Load our currently-claimed address |
| BF21 | 041D | JSR tube_send_r4 ; Send our address as release parameter |
| BF24 | 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. | ||
| BF25 | 0421 | .clear_tube_claim←1← BEB3 JSR |
| LDA #&80 ; &80 sentinel: clear address claim | ||
| BF27 | 0423 | STA tube_claimed_id ; &80 sentinel = no address currently claimed |
| BF29 | 0425 | STA tube_claim_flag ; Store to claim-in-progress flag |
| BF2B | 0427 | RTS ; Return from tube_post_init |
| BF2C | 0428 | .addr_claim_external←1← 040C BCS |
| ASL tube_claim_flag ; Another host claiming; check if we're owner | ||
| BF2E | 042A | BCS accept_new_claim ; C=1: we have an active claim |
| BF30 | 042C | CMP tube_claimed_id ; Compare with our claimed address |
| BF32 | 042E | BEQ return_tube_init ; Match: return (we already have it) |
| BF34 | 0430 | CLC ; Not ours: CLC = we don't own this address |
| BF35 | 0431 | RTS ; Return with C=0 (claim denied) |
| BF36 | 0432 | .accept_new_claim←1← 042A BCS |
| STA tube_claimed_id ; Accept new claim: update our address | ||
| BF38 | 0434 | .return_tube_init←2← 0412 BNE← 042E BEQ |
| RTS ; Return with address updated | ||
| BF39 | 0435 | .tube_transfer_setup←1← 0408 BCC |
| PHP ; PHP: save interrupt state | ||
| BF3A | 0436 | SEI ; SEI: disable interrupts for R4 protocol |
| BF3B | 0437 | .setup_data_transfer |
| STY tube_data_ptr_hi ; Save 16-bit transfer address from (X,Y) | ||
| BF3D | 0439 | STX tube_data_ptr ; Store address pointer low byte |
| BF3F | 043B | JSR tube_send_r4 ; Send transfer type byte to co-processor |
| BF42 | 043E | TAX ; X = transfer type for table lookup |
| BF43 | 043F | LDY #3 ; Y=3: send 4 bytes (address + claimed addr) |
| BF45 | 0441 | LDA tube_claimed_id ; Send our claimed address + 4-byte xfer addr |
| BF47 | 0443 | JSR tube_send_r4 ; Send transfer address byte |
| BF4A | 0446 | .send_xfer_addr_bytes←1← 044C BPL |
| LDA (tube_data_ptr),y ; Load transfer address byte from (X,Y) | ||
| BF4C | 0448 | JSR tube_send_r4 ; Send address byte to co-processor via R4 |
| BF4F | 044B | DEY ; Previous byte (big-endian: 3,2,1,0) |
| BF50 | 044C | BPL send_xfer_addr_bytes ; Loop for all 4 address bytes |
| BF52 | 044E | LDY #&18 ; Y=&18: enable Tube control register |
| BF54 | 0450 | STY tube_status_1_and_tube_control ; Enable Tube interrupt generation |
| BF57 | 0453 | LDA tube_ctrl_values,x ; Look up Tube control bits for this xfer type |
| BF5A | 0456 | STA tube_status_1_and_tube_control ; Apply transfer- specific control bits |
| BF5D | 0459 | LSR ; LSR: check bit 2 (2-byte flush needed?) |
| BF5E | 045A | LSR ; LSR: shift bit 2 to carry |
| BF5F | 045B | BCC skip_r3_flush ; C=0: no flush needed, skip R3 reads |
| BF61 | 045D | BIT tube_data_register_3 ; Dummy R3 reads: flush for 2-byte transfers |
| BF64 | 0460 | BIT tube_data_register_3 ; Second dummy read to flush R3 FIFO |
| BF67 | 0463 | .skip_r3_flush←1← 045B BCC |
| JSR tube_send_r4 ; Trigger co-processor ack via R4 | ||
| BF6A | 0466 | .poll_r4_copro_ack←1← 0469 BVC |
| BIT tube_status_register_4_and_cpu_control ; Poll R4 status for co- processo r response | ||
| BF6D | 0469 | BVC poll_r4_copro_ack ; Bit 6 clear: not ready, keep polling |
| BF6F | 046B | BCS copro_ack_nmi_check ; R4 bit 7: co-processor acknowledged transfer |
| BF71 | 046D | CPX #4 ; Type 4 = SENDW (host-to-parasite word xfer) |
| BF73 | 046F | BNE skip_nmi_release ; Not SENDW type: skip release path |
| BF75 | 0471 | .tube_sendw_complete←1← 048F BEQ |
| JSR tube_release_claim ; SENDW complete: release, sync, restart | ||
| BF78 | 0474 | JSR tube_send_r2 ; Sync via R2 send |
| BF7B | 0477 | JMP tube_reset_stack ; Restart Tube main loop |
| BF7E | 047A | .copro_ack_nmi_check←1← 046B BCS |
| LSR ; LSR: check bit 0 (NMI used?) | ||
| BF7F | 047B | BCC skip_nmi_release ; C=0: NMI not used, skip NMI release |
| BF81 | 047D | LDY #&88 ; Release Tube NMI (transfer used interrupts) |
| BF83 | 047F | STY tube_status_1_and_tube_control ; Write &88 to Tube control to release NMI |
| BF86 | 0482 | .skip_nmi_release←2← 046F BNE← 047B BCC |
| PLP ; Restore interrupt state | ||
| BF87 | 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. | ||
| BF88 | 0484 | .tube_begin←1← 0400 JMP |
| CLI ; BEGIN: enable interrupts for Tube host code | ||
| BF89 | 0485 | BCS claim_addr_ff ; C=1: hard break, claim addr &FF |
| BF8B | 0487 | BNE check_break_type ; C=0, A!=0: re-init path |
| BF8D | 0489 | JMP tube_reply_ack ; Z=1 from C=0 path: just acknowledge |
| BF90 | 048C | .check_break_type←1← 0487 BNE |
| LDX last_break_type ; Read last break type from OS workspace | ||
| BF93 | 048F | BEQ tube_sendw_complete ; Soft break (X=0): re-init Tube and restart |
| BF95 | 0491 | .claim_addr_ff←2← 0485 BCS← 0496 BCC |
| LDA #&ff ; Claim address &FF (startup = highest prio) | ||
| BF97 | 0493 | JSR tube_addr_data_dispatch ; Request address claim from Tube system |
| BF9A | 0496 | BCC claim_addr_ff ; C=0: claim failed, retry |
| BF9C | 0498 | JSR tube_init_reloc ; Init reloc pointers from ROM header |
| BF9F | 049B | .send_next_rom_page←1← 04C0 BVC |
| PHP ; Save interrupt state | ||
| BFA0 | 049C | SEI ; Disable interrupts during ROM transfer |
| BFA1 | 049D | .next_rom_page |
| LDA #7 ; R4 cmd 7: SENDW to send ROM to parasite | ||
| BFA3 | 049F | JSR tube_claim_default ; Set up Tube for SENDW transfer |
| BFA6 | 04A2 | LDY #0 ; Y=0: start at beginning of page |
| BFA8 | 04A4 | STY zp_ptr_lo ; Store to zero page pointer low byte |
| BFAA | 04A6 | .send_rom_page_bytes←1← 04AF BNE |
| LDA (zp_ptr_lo),y ; Send 256-byte page via R3, byte at a time | ||
| BFAC | 04A8 | STA tube_data_register_3 ; Write byte to Tube R3 data register |
| BFAF | 04AB | NOP ; Timing delay: Tube data register needs NOPs |
| BFB0 | 04AC | NOP ; NOP delay (2) |
| BFB1 | 04AD | NOP ; NOP delay (3) |
| BFB2 | 04AE | INY ; Next byte in page |
| BFB3 | 04AF | BNE send_rom_page_bytes ; Loop for all 256 bytes |
| BFB5 | 04B1 | PLP ; Restore interrupt state after page sent |
| BFB6 | 04B2 | INC tube_xfer_page ; Increment 24-bit destination addr |
| BFB8 | 04B4 | BNE skip_addr_carry ; No carry: skip higher bytes |
| BFBA | 04B6 | INC tube_xfer_addr_2 ; Carry into second byte |
| BFBC | 04B8 | BNE skip_addr_carry ; No carry: skip third byte |
| BFBE | 04BA | INC tube_xfer_addr_3 ; Carry into third byte |
| BFC0 | 04BC | .skip_addr_carry←2← 04B4 BNE← 04B8 BNE |
| INC zp_ptr_hi ; Increment page counter | ||
| BFC2 | 04BE | BIT zp_ptr_hi ; Bit 6 set = all pages transferred |
| BFC4 | 04C0 | BVC send_next_rom_page ; More pages: loop back to SENDW |
| BFC6 | 04C2 | JSR tube_init_reloc ; Re-init reloc pointers for final claim |
| BFC9 | 04C5 | 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 | 04C7 | .tube_claim_default←1← 049F JSR |
| LDY #0 ; Y=0: transfer address low byte | ||
| BFCD | 04C9 | LDX #&53 ; X=&53: transfer address high byte (&0053) |
| BFCF | 04CB | 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 | 04CE | .tube_init_reloc←2← 0498 JSR← 04C2 JSR |
| LDA #&80 ; Init: start sending from &8000 | ||
| BFD4 | 04D0 | STA tube_xfer_page ; Store &80 as source page high byte |
| BFD6 | 04D2 | STA zp_ptr_hi ; Store &80 as page counter initial value |
| BFD8 | 04D4 | LDA #&20 ; A=&20: bit 5 mask for ROM type check |
| BFDA | 04D6 | AND rom_type ; ROM type bit 5: reloc address in header? |
| BFDD | 04D9 | TAY ; Y = 0 or &20 (reloc flag) |
| BFDE | 04DA | STY tube_transfer_addr ; Store as transfer address selector |
| BFE0 | 04DC | BEQ store_xfer_end_addr ; No reloc addr: use defaults |
| BFE2 | 04DE | LDX copyright_offset ; Skip past copyright string to find reloc addr |
| BFE5 | 04E1 | .scan_copyright_end←1← 04E5 BNE |
| INX ; Skip past null-terminated copyright string | ||
| BFE6 | 04E2 | LDA rom_header,x ; Load next byte from ROM header |
| BFE9 | 04E5 | BNE scan_copyright_end ; Loop until null terminator found |
| BFEB | 04E7 | LDA rom_header_byte1,x ; Read 4-byte reloc address from ROM header |
| BFEE | 04EA | STA tube_transfer_addr ; Store reloc addr byte 1 as transfer addr |
| BFF0 | 04EC | LDA rom_header_byte2,x ; Load reloc addr byte 2 |
| BFF3 | 04EF | STA tube_xfer_page ; Store as source page start |
| BFF5 | 04F1 | LDY service_entry,x ; Load reloc addr byte 3 |
| BFF8 | 04F4 | LDA service_handler_lo,x ; Load reloc addr byte 4 (highest) |
| BFFB | 04F7 | .store_xfer_end_addr←1← 04DC BEQ |
| STA tube_xfer_addr_3 ; Store high byte of end address | ||
| BFFD | 04F9 | STY tube_xfer_addr_2 ; Store byte 3 of end address |
| BFFF | 04FB | RTS ; Return with pointers initialised |
