Acorn ANFS 4.21 (variant 1)
Updated 3 May 2026
← All Acorn NFS and Acorn ANFS versions
- Disassembly source on GitHub
- Acorn ANFS 4.21 (variant 1) in The BBC Micro ROM Library
- Discuss this disassembly on the Stardot Forums thread: Acorn ANFS - A new annotated disassembly
- Changes from ANFS 4.18
- Memory map
- Found a mistake or a comment that could be clearer? Report an issue.
| ; Sideways ROM header | |
| ; ANFS ROM 4.21 (variant 1) disassembly (Acorn Advanced Network ; Filing System) | |
| ; ============================================================== ; ============== | |
| 8000 | .rom_header |
| .language_entry | |
| .pydis_start | |
| EQUB &00, &42, &43 | |
| 8003 | .service_entry |
| JMP service_handler | |
| 8006 | .rom_type |
| EQUB &82 ; ROM type: service + language | |
| 8007 | .copyright_offset |
| EQUB copyright - rom_header | |
| 8008 | .binary_version |
| EQUB &04 | |
| 8009 | .title |
| EQUS "Acorn ANFS 4.21" | |
| 8018 | .version |
| EQUB &00 | |
| 8019 | .copyright |
| EQUB &00 ; Null terminator before copyright | |
| 801A | .copyright_string |
| EQUS "(C)1986 Acorn." | |
Service 5: unrecognised interrupt (Master 128 dispatch)Reads the deferred-work flag at
|
||||||||||||||
| 8028 | .svc5_irq_check | |||||||||||||
| PHX ; Save X (the ROM slot we're being called on behalf of) | ||||||||||||||
| 8029 | PHY ; Save Y (the dispatch-path selector via its high bit) | |||||||||||||
| 802A | LDY tx_op_type ; Read deferred-work flag at &0D65 (set by NMI when work queued) | |||||||||||||
| 802D | BNE irq_check_dispatch ; Non-zero: there's work to dispatch | |||||||||||||
| 802F | PLY ; Zero: no work; restore Y | |||||||||||||
| 8030 | PLX ; Restore X | |||||||||||||
| 8031 | RTS ; Return to MOS (service unclaimed) | |||||||||||||
| 8032 | .irq_check_dispatch←1← 802D BNE | |||||||||||||
| LDA #&80 ; A=&80: bit 7 -- the bit to clear in ACCCON | ||||||||||||||
| 8034 | TRB acccon ; Clear ACCCON bit 7 (release IRR mask) | |||||||||||||
| 8037 | STZ tx_op_type ; Zero the deferred-work flag (we're handling it now) | |||||||||||||
| 803A | TYA ; Copy to A for sign test Bring saved Y back into A so BMI can test bit 7 | |||||||||||||
| 803B | BMI dispatch_svc5 ; Bit 7 set: dispatch via table Bit 7 of caller's Y set: dispatch via PHA/PHA/RTS table | |||||||||||||
| 803D | LDA #&fe ; A=&FE: Econet receive event | |||||||||||||
| 803F | JSR generate_event ; Call event vector handler | |||||||||||||
| 8042 | JMP tx_done_exit ; Fire event (enable: *FX52,150) Tail-jump to tx_done_exit which restores X/Y and claims the service | |||||||||||||
Generate event via EVNTVSingle-instruction
|
|||||||||||
| 8045 | .generate_event←1← 803F JSR | ||||||||||
| JMP (evntv) ; Dispatch through event vector | |||||||||||
Service-5 PHA/PHA/RTS dispatch tailBuilds an
|
||||
| 8048 | .dispatch_svc5←1← 803B BMI | |||
| LDA #&85 ; Push return addr high (&85) | ||||
| 804A | PHA ; High byte on stack for RTS | |||
| 804B | LDA tx_done_dispatch_lo-&83,y ; Load dispatch target low byte | |||
| 804E | PHA ; Low byte on stack for RTS | |||
| fall through ↓ | ||||
Service-5 unknown-IRQ tail (PHA/PHA/RTS landing)Bare |
|
| 804F | .svc_5_unknown_irq |
| RTS ; RTS = dispatch to PHA'd address | |
ADLC initialisationInitialise ADLC hardware and Econet workspace. Disables NMIs via
|
|
| 8050 | .adlc_init←1← 903C JSR |
| BIT master_intoff ; INTOFF: read station ID, disable NMIs | |
| 8053 | JSR adlc_full_reset ; Full ADLC hardware reset |
| 8056 | LDA #&ea ; OSBYTE &EA: check Tube co-processor |
| 8058 | LDX #0 ; X=0 for OSBYTE |
| 805A | STX econet_init_flag ; Clear Econet init flag before setup |
| 805D | JSR osbyte_x0 ; Check Tube presence via OSBYTE &EA |
| 8060 | STX tube_present ; Store Tube presence flag from OSBYTE &EA |
| 8063 | LDA #&8f ; OSBYTE &8F: issue service request |
| 8065 | LDX #&0c ; X=&0C: NMI claim service |
| 8067 | JSR osbyte_yff ; Issue NMI claim service request |
| 806A | LDY #5 ; Y=5: NMI claim service number |
| 806C | .econet_restore |
| CPY #5 ; Check if NMI service was claimed (Y changed) | |
| 806E | 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 ( The shim includes the INTOFF/INTON pair ( Workspace fields written:
Finally re-enables NMIs via INTON ( |
||||||||||||||||||||||
| 8070 | .init_nmi_workspace | |||||||||||||||||||||
| LDY #&20 ; Copy NMI shim from ROM to &0D0C area | ||||||||||||||||||||||
| 8072 | .copy_nmi_shim←1← 8079 BNE | |||||||||||||||||||||
| LDA nmi_shim_source,y ; Read byte from NMI shim ROM source | ||||||||||||||||||||||
| 8075 | STA nmi_code_base,y ; Write to NMI shim RAM (start of NFS workspace) | |||||||||||||||||||||
| 8078 | DEY ; Next byte (descending) | |||||||||||||||||||||
| 8079 | BNE copy_nmi_shim ; Loop until all 32 bytes copied | |||||||||||||||||||||
| 807B | LDA romsel_copy ; Patch current ROM bank into NMI shim | |||||||||||||||||||||
| 807D | STA nmi_romsel ; Self-modifying code: ROM bank at &0D07 | |||||||||||||||||||||
| 8080 | STY tx_src_net ; Clear source network (Y=0 from copy loop) | |||||||||||||||||||||
| 8083 | STY prot_flags ; Clear Tube release flag | |||||||||||||||||||||
| 8085 | STY tx_op_type ; Clear TX operation type | |||||||||||||||||||||
| 8088 | LDY #1 ; Y=1: tx_src_stn offset in NMI block | |||||||||||||||||||||
| 808A | LDA (net_rx_ptr),y ; Read TX source station from (net_rx_ptr)+1 | |||||||||||||||||||||
| 808C | STA tx_src_stn ; Store as tx_src_stn | |||||||||||||||||||||
| 808F | LDA #&80 ; &80 = Econet initialised | |||||||||||||||||||||
| 8091 | STA tx_complete_flag ; Mark TX as complete (ready) | |||||||||||||||||||||
| 8094 | STA econet_init_flag ; Mark Econet as initialised | |||||||||||||||||||||
| 8097 | BIT master_inton ; INTON: re-enable NMIs (&FE20 read side effect) | |||||||||||||||||||||
| 809A | .adlc_init_done←1← 806E BNE | |||||||||||||||||||||
| RTS ; Return | ||||||||||||||||||||||
NMI RX scout handler (initial byte)Default NMI handler for incoming scout frames. Checks whether the frame is addressed to us or is a broadcast. Installed as the NMI target during idle RX listen mode. Tests |
|
| 809B | .nmi_rx_scout←1← 89D5 JMP |
| LDA #1 ; A=&01: mask for SR2 bit0 (AP = Address Present) | |
| 809D | BIT adlc_cr2 ; Z = A AND SR2 -- tests if AP is set |
| 80A0 | BEQ scout_error ; AP not set, no incoming data -- check for errors |
| 80A2 | LDA adlc_tx ; Read first RX byte (destination station address) |
| 80A5 | CMP tx_src_stn ; Compare to our station ID (&FE18 read = INTOFF, disables NMIs) |
| 80A8 | BEQ accept_frame ; Match -- accept frame |
| 80AA | CMP #&ff ; Check for broadcast address (&FF) |
| 80AC | BNE scout_reject ; Neither our address nor broadcast -- reject frame |
| 80AE | LDA #&40 ; Flag &40 = broadcast frame |
| 80B0 | STA rx_src_net ; Store broadcast flag in rx_src_net |
| 80B3 | .accept_frame←1← 80A8 BEQ |
| LDA #&b8 ; Install nmi_rx_scout_net NMI handler | |
| 80B5 | JMP install_nmi_handler ; Install next handler |
RX scout second byte handlerReads the second byte of an incoming scout (destination network).
Installs |
|||||||||||||
| 80B8 | .nmi_rx_scout_net | ||||||||||||
| BIT adlc_cr2 ; Test SR2 for RDA (bit7 = data available) | |||||||||||||
| 80BB | BPL scout_error ; No RDA -- check errors | ||||||||||||
| 80BD | LDA adlc_tx ; Read destination network byte | ||||||||||||
| 80C0 | BEQ accept_local_net ; Network = 0 -- local network, accept | ||||||||||||
| 80C2 | EOR #&ff ; Test if network = &FF (broadcast) | ||||||||||||
| 80C4 | BEQ accept_scout_net ; Broadcast network -- accept | ||||||||||||
| 80C6 | .scout_reject←1← 80AC BNE | ||||||||||||
| LDA #&a2 ; Reject: wrong network. CR1=&A2: RIE|RX_DISCONTINUE | |||||||||||||
| 80C8 | STA adlc_cr1 ; Write CR1 to discontinue RX | ||||||||||||
| 80CB | JMP set_nmi_rx_scout ; Return to idle scout listening | ||||||||||||
| 80CE | .accept_local_net←1← 80C0 BEQ | ||||||||||||
| STA rx_src_net ; Network = 0 (local): clear tx_flags | |||||||||||||
| 80D1 | .accept_scout_net←1← 80C4 BEQ | ||||||||||||
| STA port_buf_len ; Store Y offset for scout data buffer | |||||||||||||
| 80D3 | LDA #&e8 ; Install scout data handler | ||||||||||||
| 80D5 | JMP install_nmi_handler ; Install scout data loop | ||||||||||||
Scout error/discard handlerHandles scout reception errors and end-of-frame conditions. Reads
Also serves as the common discard path for address/network
mismatches from |
|
| 80D8 | .scout_error←5← 80A0 BEQ← 80BB BPL← 80ED BPL← 8121 BEQ← 8123 BPL |
| LDA adlc_cr2 ; Read SR2 | |
| 80DB | AND #&81 ; Test AP (b0) | RDA (b7) |
| 80DD | BEQ scout_discard ; Neither set -- clean end, discard frame |
| 80DF | JSR adlc_full_reset ; Unexpected data/status: full ADLC reset |
| 80E2 | JMP set_nmi_rx_scout ; Discard and return to idle |
| 80E5 | .scout_discard←1← 80DD BEQ |
| JMP reset_adlc_rx_listen ; Gentle discard: RX_DISCONTINUE | |
| 80E8 | LDY port_buf_len ; Y = buffer offset |
| 80EA | LDA adlc_cr2 ; Read SR2 |
| 80ED | .scout_loop_rda←1← 810D BNE |
| BPL scout_error ; No RDA -- error handler | |
| 80EF | LDA adlc_tx ; Read data byte from RX FIFO |
| 80F2 | STA scout_buf,y ; Store at &0D3D+Y (scout buffer) |
| 80F5 | INY ; Advance buffer index |
| 80F6 | LDA adlc_cr2 ; Read SR2 again (FV detection point) |
| 80F9 | BMI scout_loop_second ; RDA set -- more data, read second byte |
| 80FB | BNE scout_complete ; SR2 non-zero (FV or other) -- scout completion |
| 80FD | .scout_loop_second←1← 80F9 BMI |
| LDA adlc_tx ; Read second byte of pair | |
| 8100 | STA scout_buf,y ; Store at &0D3D+Y |
| 8103 | INY ; Advance and check buffer limit |
| 8104 | CPY #&0c ; Copied all 12 scout bytes? |
| 8106 | BEQ scout_complete ; Buffer full (Y=12) -- force completion |
| 8108 | STY port_buf_len ; Save final buffer offset |
| 810A | LDA adlc_cr2 ; Read SR2 for next pair |
| 810D | BNE scout_loop_rda ; SR2 non-zero -- loop back for more bytes |
| 810F | JMP nmi_rti ; SR2 = 0 -- wait for next NMI |
Scout completion handlerProcesses a completed scout frame. Writes Matches the port byte (
|
|
| 8112 | .scout_complete←2← 80FB BNE← 8106 BEQ |
| LDA #0 ; Save Y for next iteration | |
| 8114 | STA adlc_cr1 ; Write CR1 |
| 8117 | LDA #&84 ; CR2=&84: disable PSE, enable RDA_SUPPRESS_FV |
| 8119 | STA adlc_cr2 ; Write CR2 |
| 811C | LDA #2 ; A=&02: FV mask for SR2 bit1 |
| 811E | BIT adlc_cr2 ; Test SR2 FV (Z) and RDA (N) |
| 8121 | BEQ scout_error ; No FV -- not a valid frame end, error |
| 8123 | BPL scout_error ; FV set but no RDA -- missing last byte, error |
| 8125 | LDA adlc_tx ; Read last byte from RX FIFO |
| 8128 | STA scout_buf,y ; Store last byte at &0D3D+Y |
| 812B | LDA #&44 ; CR1=&44: RX_RESET | TIE (switch to TX for ACK) |
| 812D | STA adlc_cr1 ; Write CR1: switch to TX mode |
| 8130 | SEC ; Set bit7 of need_release_tube flag |
| 8131 | ROR prot_flags ; Rotate C=1 into bit7: mark Tube release needed |
| 8133 | LDA scout_port ; Check port byte: 0 = immediate op, non-zero = data transfer |
| 8136 | BNE scout_match_port ; Port non-zero -- look for matching receive block |
| 8138 | .scout_no_match |
| JMP immediate_op ; Port = 0 -- immediate operation handler | |
| 813B | .scout_match_port←1← 8136 BNE |
| BIT rx_src_net ; Check if broadcast (bit6 of tx_flags) | |
| 813E | BVC scan_port_list ; Not broadcast -- skip CR2 setup |
| 8140 | LDA #7 ; CR2=&07: broadcast prep |
| 8142 | STA adlc_cr2 ; Write CR2: broadcast frame prep |
| 8145 | .scan_port_list←1← 813E BVC |
| BIT econet_flags ; Check if RX port list active (bit7) | |
| 8148 | BPL try_nfs_port_list ; No active ports -- try NFS workspace |
| 814A | LDA #&c0 ; Start scanning port list at page &C0 |
| 814C | LDY #0 ; Y=0: start offset within each port slot |
| 814E | .scan_nfs_port_list←1← 8193 BNE |
| STA port_ws_offset ; Store page to workspace pointer low | |
| 8150 | STY rx_buf_offset ; Store page high byte for slot scanning |
| 8152 | .check_port_slot←1← 8185 BCC |
| LDY #0 ; Y=0: read control byte from start of slot | |
| 8154 | .scout_ctrl_check |
| LDA (port_ws_offset),y ; Read port control byte from slot | |
| 8156 | BEQ discard_no_match ; Zero = end of port list, no match |
| 8158 | CMP #&7f ; &7F = any-port wildcard |
| 815A | BNE next_port_slot ; Not wildcard -- check specific port match |
| 815C | INY ; Y=1: advance to port byte in slot |
| 815D | LDA (port_ws_offset),y ; Read port number from slot (offset 1) |
| 815F | BEQ check_station_filter ; Zero port in slot = match any port |
| 8161 | CMP scout_port ; Check if port matches this slot |
| 8164 | BNE next_port_slot ; Port mismatch -- try next slot |
| 8166 | .check_station_filter←1← 815F BEQ |
| INY ; Y=2: advance to station byte | |
| 8167 | LDA (port_ws_offset),y ; Read station filter from slot (offset 2) |
| 8169 | BEQ port_match_found ; Zero station = match any station, accept |
| 816B | CMP scout_buf ; Check if source station matches |
| 816E | BNE next_port_slot ; Station mismatch -- try next slot |
| 8170 | .scout_port_match |
| INY ; Y=3: advance to network byte | |
| 8171 | LDA (port_ws_offset),y ; Read network filter from slot (offset 3) |
| 8173 | BEQ port_match_found ; Zero = accept any network |
| 8175 | CMP scout_src_net ; Check if source network matches |
| 8178 | BEQ port_match_found ; Network matches or zero = accept |
| 817A | .next_port_slot←3← 815A BNE← 8164 BNE← 816E BNE |
| LDA rx_buf_offset ; Check if NFS workspace search pending | |
| 817C | BEQ try_nfs_port_list ; No NFS workspace -- try fallback path |
| 817E | LDA port_ws_offset ; Load current slot base address |
| 8180 | CLC ; For 12-byte slot advance |
| 8181 | ADC #&0c ; Advance to next 12-byte port slot |
| 8183 | STA port_ws_offset ; Update workspace pointer to next slot |
| 8185 | BCC check_port_slot ; Always branches (page &C0 won't overflow) |
| 8187 | .discard_no_match←2← 8156 BEQ← 818D BVC |
| JMP nmi_error_dispatch ; No match found -- discard frame | |
| 818A | .try_nfs_port_list←2← 8148 BPL← 817C BEQ |
| BIT econet_flags ; Try NFS workspace if paged list exhausted | |
| 818D | BVC discard_no_match ; No NFS workspace RX (bit6 clear) -- discard |
| 818F | LDA #0 ; NFS workspace starts at offset 0 in page |
| 8191 | LDY nfs_workspace_hi ; NFS workspace high byte for port list |
| 8193 | BNE scan_nfs_port_list ; Scan NFS workspace port list |
| fall through ↓ | |
Scout matched: arm data RX, ACK or discardSets
Four inbound refs (one
|
||||||||||||||||
| 8195 | .port_match_found←4← 8169 BEQ← 8173 BEQ← 8178 BEQ← 84B9 JMP | |||||||||||||||
| LDA #3 ; Match found: set scout_status = 3 | ||||||||||||||||
| 8197 | STA rx_port ; Record match for completion handler | |||||||||||||||
| 819A | JSR tx_calc_transfer ; Calculate transfer parameters | |||||||||||||||
| 819D | BCC nmi_error_dispatch ; C=0: no Tube claimed -- discard | |||||||||||||||
| 819F | BIT rx_src_net ; Check broadcast flag for ACK path | |||||||||||||||
| 81A2 | BVC send_data_rx_ack ; Not broadcast -- normal ACK path | |||||||||||||||
| 81A4 | JMP copy_scout_to_buffer ; Broadcast: different completion path | |||||||||||||||
Send scout ACK and arm data-RX continuationSwitches the ADLC to TX mode for the scout ACK frame: writes
Two callers: the dispatch in
|
||||||
| 81A7 | .send_data_rx_ack←2← 81A2 BVC← 84AE JMP | |||||
| LDA #&44 ; CR1=&44: RX_RESET | TIE | ||||||
| 81A9 | STA adlc_cr1 ; Write CR1: TX mode for ACK | |||||
| 81AC | LDA #&a7 ; CR2=&A7: RTS | CLR_TX_ST | FC_TDRA | PSE | |||||
| 81AE | STA adlc_cr2 ; Write CR2: enable TX with PSE | |||||
| 81B1 | LDA #&b8 ; Install data_rx_setup at &81B8 | |||||
| 81B3 | LDY #&81 ; High byte of data_rx_setup handler | |||||
| 81B5 | JMP ack_tx_write_dest ; Send ACK with data_rx_setup as next NMI | |||||
NMI handler: switch ADLC to RX for the data frameNMI continuation entry installed by
|
|
| 81B8 | .data_rx_setup |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (switch to RX for data frame) | |
| 81BA | STA adlc_cr1 ; Write CR1: switch to RX for data frame |
| 81BD | LDA #&c2 ; Install nmi_data_rx at &81E7 |
| 81BF | 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: this routine (AP + dest-stn check) →
|
|
| 81C2 | .nmi_data_rx |
| LDA #1 ; A=1: AP mask for SR2 bit test | |
| 81C4 | BIT adlc_cr2 ; Test SR2 AP bit |
| 81C7 | BEQ nmi_error_dispatch ; No AP: wrong frame or error |
| 81C9 | LDA adlc_tx ; Read first byte (dest station) |
| 81CC | CMP tx_src_stn ; Compare to our station ID (INTOFF) |
| 81CF | BNE nmi_error_dispatch ; Not for us: error path |
| 81D1 | LDA #&d6 ; Install nmi_data_rx_net check handler |
| 81D3 | JMP install_nmi_handler ; Set NMI vector via RAM shim |
NMI handler: validate dest-net byte of data frameNMI continuation entry installed by
|
||||
| 81D6 | .nmi_data_rx_net | |||
| BIT adlc_cr2 ; Validate source network = 0 | ||||
| 81D9 | BPL nmi_error_dispatch ; SR2 bit7 clear: no data ready -- error | |||
| 81DB | LDA adlc_tx ; Read dest network byte | |||
| 81DE | BNE nmi_error_dispatch ; Network != 0: wrong network -- error | |||
| 81E0 | LDA #&ec ; Install skip handler at &8211 | |||
| 81E2 | LDY #&81 ; High byte of &8211 handler | |||
| 81E4 | BIT adlc_cr1 ; SR1 bit7: IRQ, data already waiting | |||
| 81E7 | BMI nmi_data_rx_skip ; Data ready: skip directly, no return | |||
| 81E9 | JMP set_nmi_vector ; Install handler and return | |||
NMI handler: skip control + port bytesNMI continuation entry that consumes the control and port bytes of
the data frame (already known from the scout) and proceeds to the
bulk-data-read continuation. Polls SR2 for RDA on entry; on no
RDA, branches to |
|
| 81EC | .nmi_data_rx_skip←1← 81E7 BMI |
| BIT adlc_cr2 ; Test SR2 RDA (RX data byte ready) | |
| 81EF | BPL nmi_error_dispatch ; SR2 bit7 clear: error |
| 81F1 | LDA adlc_tx ; Discard control byte |
| 81F4 | LDA adlc_tx ; Discard port byte |
| fall through ↓ | |
Install data RX bulk or Tube handlerSelects between the normal bulk-RX handler at
In the bulk path, after loading the handler address, checks |
|||||||
| 81F7 | .install_data_rx_handler←1← 88D4 JMP | ||||||
| LDA #2 ; A=2: Tube transfer flag mask | |||||||
| 81F9 | BIT rx_src_net ; Check if Tube transfer active | ||||||
| 81FC | BNE install_tube_rx ; Tube active: use Tube RX path | ||||||
| 81FE | LDA #&23 ; A=&23: low byte of nmi_data_rx_bulk (&8223) | ||||||
| 8200 | LDY #&82 ; Y=&82: high byte of nmi_data_rx_bulk | ||||||
| 8202 | BIT adlc_cr1 ; SR1 bit7: more data already waiting? | ||||||
| 8205 | BMI nmi_data_rx_bulk ; Yes: enter bulk read directly | ||||||
| 8207 | JMP set_nmi_vector ; No: install handler | ||||||
| 820A | .install_tube_rx←1← 81FC BNE | ||||||
| LDA #&91 ; A=&91: low byte of nmi_data_rx_tube (&8291) | |||||||
| 820C | LDY #&82 ; Y=&82: high byte of nmi_data_rx_tube | ||||||
| 820E | JMP set_nmi_vector ; Install Tube handler | ||||||
| ; Page-overflow exit from nmi_data_rx_bulk: restores the Master ; 128 ACCCON | |||||||
| ; that was saved at &822A before falling through to the ; RXCB-update path. | |||||||
| 8211 | .page_boundary_restore←1← 823F BEQ | ||||||
| PLA ; Pull saved ACCCON from stack | |||||||
| 8212 | STA acccon ; Restore caller's ACCCON on page-overflow exit | ||||||
| fall through ↓ | |||||||
NMI error handler dispatchCommon error/abort entry used by 11 call sites. The dispatch byte
at
|
|||||||
| 8215 | .nmi_error_dispatch←11← 8187 JMP← 819D BCC← 81C7 BEQ← 81CF BNE← 81D9 BPL← 81DE BNE← 81EF BPL← 8279 BEQ← 827F BEQ← 833C JMP← 8488 JMP | ||||||
| LDA rx_src_net ; Check tx_flags for error path | |||||||
| 8218 | BPL rx_error_reset ; Bit7 clear: RX error path | ||||||
| 821A | JMP tx_result_fail ; Bit7 set: TX result = not listening | ||||||
| 821D | .rx_error_reset←1← 8218 BPL | ||||||
| JSR adlc_full_reset ; Full ADLC reset on RX error | |||||||
| 8220 | 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
|
|
| 8223 | .nmi_data_rx_bulk←1← 8205 BMI |
| LDY port_buf_len ; Y = buffer offset, resume from last position | |
| 8225 | LDA adlc_cr2 ; Read SR2 for next pair |
| 8228 | .data_rx_loop←1← 825F BNE |
| BPL data_rx_complete ; SR2 bit7 clear: frame complete (FV) | |
| ; 4.21 Master 128: save/restore ACCCON across the ; (open_port_buf),Y stores | |
| ; in this bulk-read loop. Same idiom as in copy_scout_to_buffer; ; workspace | |
| ; &97 holds the desired ACCCON value pre-loaded by the caller. | |
| 822A | LDA acccon ; Save current ACCCON on stack (Master 128) |
| 822D | PHA ; Push ACCCON snapshot |
| 822E | LDA escapable ; Load desired ACCCON from workspace &97 |
| 8230 | STA acccon ; Set ACCCON for the upcoming buffer stores |
| 8233 | LDA adlc_tx ; Read first byte of pair from RX FIFO |
| 8236 | STA (open_port_buf),y ; Store byte to buffer |
| 8238 | INY ; Advance buffer offset |
| 8239 | BNE read_sr2_between_pairs ; Y != 0: no page boundary crossing |
| 823B | INC open_port_buf_hi ; Crossed page: increment buffer high byte |
| 823D | DEC port_buf_len_hi ; Decrement remaining page count |
| 823F | BEQ page_boundary_restore ; No pages left: handle as complete |
| 8241 | .read_sr2_between_pairs←1← 8239 BNE |
| LDA adlc_cr2 ; Read SR2 between byte pairs | |
| 8244 | BMI read_second_rx_byte ; SR2 bit7 set: more data available |
| 8246 | BNE frame_complete_restore ; SR2 non-zero, bit7 clear: frame done |
| 8248 | .read_second_rx_byte←1← 8244 BMI |
| LDA adlc_tx ; Read second byte of pair from RX FIFO | |
| 824B | STA (open_port_buf),y ; Store byte to buffer |
| 824D | INY ; Advance buffer offset |
| 824E | STY port_buf_len ; Save updated buffer position |
| 8250 | BNE byte_pair_restore ; Y != 0: no page boundary crossing |
| 8252 | INC open_port_buf_hi ; Crossed page: increment buffer high byte |
| 8254 | DEC port_buf_len_hi ; Decrement remaining page count |
| 8256 | BEQ frame_complete_restore ; No pages left: frame complete |
| 8258 | .byte_pair_restore←1← 8250 BNE |
| PLA ; Pull saved ACCCON from stack | |
| 8259 | STA acccon ; Restore caller's ACCCON between byte pairs |
| 825C | .check_sr2_loop_again |
| LDA adlc_cr2 ; Re-poll ADLC SR2 for next byte pair | |
| 825F | BNE data_rx_loop ; More data: loop back to data_rx_loop |
| 8261 | JMP nmi_rti ; No more data: return from NMI |
| 8264 | .frame_complete_restore←2← 8246 BNE← 8256 BEQ |
| PLA ; Pull saved ACCCON (frame-complete path) | |
| 8265 | STA acccon ; Restore caller's ACCCON before completion |
| fall through ↓ | |
Data frame completionReached when |
|
| 8268 | .data_rx_complete←1← 8228 BPL |
| LDA #&84 ; A=&84: CR2 value (disable PSE) | |
| 826A | STA adlc_cr2 ; Write CR2 = &84 to disable PSE for bit testing |
| 826D | LDA #0 ; A=0: CR1 value (disable all interrupts) |
| 826F | STA adlc_cr1 ; Write CR1 = 0 to disable all interrupts |
| 8272 | STY port_buf_len ; Save Y (byte count from data RX loop) |
| 8274 | LDA #2 ; A=&02: FV mask |
| 8276 | BIT adlc_cr2 ; Test SR2 FV (Z) and RDA (N) |
| 8279 | BEQ nmi_error_dispatch ; No FV -- error |
| 827B | BPL send_ack ; FV set, no RDA -- proceed to ACK |
| 827D | LDA port_buf_len_hi ; Check if buffer space remains |
| 827F | .read_last_rx_byte←3← 829C BEQ← 82C3 BEQ← 82CF BEQ |
| BEQ nmi_error_dispatch ; No buffer space: error/discard frame | |
| 8281 | LDA adlc_tx ; FV+RDA: read and store last data byte |
| 8284 | LDY port_buf_len ; Y = current buffer write offset |
| 8286 | STA (open_port_buf),y ; Store last byte in port receive buffer |
| 8288 | INC port_buf_len ; Advance buffer write offset |
| 828A | BNE send_ack ; No page wrap: proceed to send ACK |
| 828C | INC open_port_buf_hi ; Page boundary: advance buffer page |
| 828E | .send_ack←2← 827B BPL← 828A BNE |
| JMP ack_tx ; Send ACK frame to complete handshake | |
NMI handler: data-frame RX into Tube bufferNMI continuation entry for the Tube data-RX path. Polls SR2 for
RDA, reads the next data byte from the ADLC RX FIFO, and writes it
to the Tube data register, advancing the Tube transfer pointer
each iteration. Tests for end-of-frame via FV and either continues
the tight inner loop or returns via |
|
| 8291 | .nmi_data_rx_tube |
| LDA adlc_cr2 ; Read SR2 for Tube data receive path | |
| 8294 | .rx_tube_data←1← 82AF BNE |
| BPL data_rx_tube_complete ; RDA clear: no more data, frame complete | |
| 8296 | LDA adlc_tx ; Read data byte from ADLC RX FIFO |
| 8299 | JSR advance_buffer_ptr ; Check buffer limits and transfer size |
| 829C | BEQ read_last_rx_byte ; Zero: buffer full, handle as error |
| 829E | STA tube_data_register_3 ; Send byte to Tube data register 3 |
| 82A1 | LDA adlc_tx ; Read second data byte (paired transfer) |
| 82A4 | STA tube_data_register_3 ; Send second byte to Tube |
| 82A7 | JSR advance_buffer_ptr ; Check limits after byte pair |
| 82AA | BEQ data_rx_tube_complete ; Zero: Tube transfer complete |
| 82AC | LDA adlc_cr2 ; Re-read SR2 for next byte pair |
| 82AF | BNE rx_tube_data ; More data available: continue loop |
| 82B1 | .data_rx_tube_error |
| JMP nmi_rti ; Unexpected end: return from NMI | |
| 82B4 | .data_rx_tube_complete←2← 8294 BPL← 82AA BEQ |
| LDA #0 ; CR1=&00: disable all interrupts | |
| 82B6 | STA adlc_cr1 ; Write CR1 for individual bit testing |
| 82B9 | LDA #&84 ; CR2=&84: disable PSE |
| 82BB | STA adlc_cr2 ; Write CR2: same pattern as main path |
| 82BE | LDA #2 ; A=&02: FV mask for Tube completion |
| 82C0 | BIT adlc_cr2 ; Test SR2 FV (Z) and RDA (N) |
| 82C3 | BEQ read_last_rx_byte ; No FV: incomplete frame, error |
| 82C5 | BPL ack_tx ; FV set, no RDA: proceed to ACK |
| 82C7 | LDA port_buf_len ; Check if any buffer was allocated |
| 82C9 | ORA port_buf_len_hi ; OR all 4 buffer pointer bytes together |
| 82CB | ORA open_port_buf ; Check buffer low byte |
| 82CD | ORA open_port_buf_hi ; Check buffer high byte |
| 82CF | BEQ read_last_rx_byte ; All zero (null buffer): error |
| 82D1 | LDA adlc_tx ; Read extra trailing byte from FIFO |
| 82D4 | STA rx_extra_byte ; Save extra byte in workspace for later use |
| 82D7 | LDA #&20 ; Bit5 = extra data byte available flag |
| 82D9 | ORA rx_src_net ; Set extra byte flag in tx_flags |
| 82DC | 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. Tests bit 7 of After writing the address bytes to the TX FIFO, installs the next
NMI handler from |
|
| 82DF | .ack_tx←2← 828E JMP← 82C5 BPL |
| LDA rx_src_net ; Load TX flags to check ACK type | |
| 82E2 | BPL ack_tx_configure ; Bit7 clear: normal scout ACK |
| 82E4 | JSR advance_rx_buffer_ptr ; Final ACK: call completion handler |
| 82E7 | JMP tx_result_ok ; Jump to TX success result |
| 82EA | .ack_tx_configure←1← 82E2 BPL |
| LDA #&44 ; CR1=&44: RX_RESET | TIE (switch to TX mode) | |
| 82EC | STA adlc_cr1 ; Write CR1: switch to TX mode |
| 82EF | LDA #&a7 ; CR2=&A7: RTS|CLR_TX_ST|FC_TDRA|2_1_BYTE|PSE |
| 82F1 | STA adlc_cr2 ; Write CR2: enable TX with status clear |
| 82F4 | LDA #&86 ; Install saved next handler (scout ACK path) |
| 82F6 | LDY #&83 ; High byte of post-ACK handler |
| fall through ↓ | |
Begin ACK transmit: write destination address to ADLCFirst step of the four-byte ACK frame transmission. Saves the
caller-supplied When Two callers:
|
||||||
| 82F8 | .ack_tx_write_dest←2← 81B5 JMP← 84F6 JMP | |||||
| STA saved_nmi_lo ; Store next handler low byte | ||||||
| 82FB | STY saved_nmi_hi ; Store next handler high byte | |||||
| 82FE | LDA scout_buf ; Load dest station from RX scout buffer | |||||
| 8301 | BIT adlc_cr1 ; Test SR1 TDRA (V=bit6) | |||||
| 8304 | BVC dispatch_nmi_error ; TDRA not ready -- error | |||||
| 8306 | STA adlc_tx ; Write dest station to TX FIFO | |||||
| 8309 | LDA scout_src_net ; Load dest network from RX scout buffer | |||||
| 830C | STA adlc_tx ; Write dest net byte to FIFO | |||||
| 830F | LDA #&16 ; A=&16: low byte of nmi_ack_tx_src (&8316) | |||||
| 8311 | LDY #&83 ; High byte of nmi_ack_tx_src | |||||
| 8313 | JMP set_nmi_vector ; Set NMI vector to ack_tx_src handler | |||||
ACK TX continuationContinuation of ACK frame transmission, reached via NMI after
Then dispatches on
|
|||||||
| 8316 | .nmi_ack_tx_src | ||||||
| LDA tx_src_stn ; Load our station ID from workspace copy | |||||||
| 8319 | BIT adlc_cr1 ; Test SR1 TDRA | ||||||
| 831C | BVC dispatch_nmi_error ; TDRA not ready -- error | ||||||
| 831E | STA adlc_tx ; Write our station to TX FIFO | ||||||
| 8321 | LDA #0 ; Write network=0 to TX FIFO | ||||||
| 8323 | STA adlc_tx ; Write network=0 (local) to TX FIFO | ||||||
| 8326 | LDA rx_src_net ; Check tx_flags for data phase | ||||||
| 8329 | BMI start_data_tx ; bit7 set: start data TX phase | ||||||
| 832B | 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 starting at
|
|
| 832D | .post_ack_scout |
| STA adlc_cr2 ; Write CR2 to clear status after ACK TX | |
| 8330 | LDA saved_nmi_lo ; Install saved handler from &0D4B/&0D4C |
| 8333 | LDY saved_nmi_hi ; Load saved next handler high byte |
| 8336 | JMP set_nmi_vector ; Install next NMI handler |
| 8339 | .start_data_tx←1← 8329 BMI |
| JMP data_tx_begin ; Jump to start data TX phase | |
| 833C | .dispatch_nmi_error←2← 8304 BVC← 831C 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. Reads:
Updates the RXCB in place. Clobbers
|
||||
| 833F | .advance_rx_buffer_ptr←2← 82E4 JSR← 8395 JSR | |||
| LDA #2 ; A=2: test bit1 of tx_flags | ||||
| 8341 | BIT rx_src_net ; Check tx_flags data-transfer bit | |||
| 8344 | BEQ return_rx_complete ; Bit1 clear: no transfer -- return | |||
| 8346 | CLC ; Init carry for 4-byte add | |||
| 8347 | PHP ; Save carry on stack for loop | |||
| 8348 | LDY #8 ; Y=8: start at byte 0 of the 4-byte RXCB pointer | |||
| 834A | .add_rxcb_ptr←1← 8356 BCC | |||
| LDA (port_ws_offset),y ; Load RXCB[Y] (buffer pointer byte) | ||||
| 834C | PLP ; Restore carry from stack | |||
| 834D | ADC net_tx_ptr,y ; Add transfer count byte | |||
| 8350 | STA (port_ws_offset),y ; Store updated pointer back to RXCB | |||
| 8352 | INY ; Next byte | |||
| 8353 | PHP ; Save carry for next iteration | |||
| 8354 | CPY #&0c ; Done 4 bytes? (Y reaches &0C) | |||
| 8356 | BCC add_rxcb_ptr ; No: continue adding | |||
| 8358 | PLP ; Discard final carry | |||
| 8359 | LDA #&20 ; A=&20: test bit5 of tx_flags | |||
| 835B | BIT rx_src_net ; Check tx_flags Tube bit | |||
| 835E | BEQ skip_tube_update ; No Tube: skip Tube update | |||
| 8360 | TXA ; Save X on stack | |||
| 8361 | PHA ; Push X | |||
| 8362 | LDA #8 ; A=8: offset for Tube address | |||
| 8364 | CLC ; For address calculation | |||
| 8365 | ADC port_ws_offset ; Add workspace base offset | |||
| 8367 | TAX ; X = address low for Tube claim | |||
| 8368 | LDY rx_buf_offset ; Y = address high for Tube claim | |||
| 836A | LDA #1 ; A=1: Tube claim type (read) | |||
| 836C | JSR tube_addr_data_dispatch ; Claim Tube address for transfer | |||
| 836F | LDA rx_extra_byte ; Load extra RX data byte | |||
| 8372 | STA tube_data_register_3 ; Send to Tube via R3 | |||
| 8375 | SEC ; Init carry for increment | |||
| 8376 | LDY #8 ; Y=8: start at byte 0 of the 4-byte RXCB pointer | |||
| 8378 | .inc_rxcb_ptr←1← 837F BCS | |||
| LDA #0 ; A=0: add carry only (increment) | ||||
| 837A | ADC (port_ws_offset),y ; Add carry to pointer byte | |||
| 837C | STA (port_ws_offset),y ; Store back to RXCB | |||
| 837E | INY ; Next byte | |||
| 837F | BCS inc_rxcb_ptr ; Keep going while carry propagates | |||
| 8381 | PLA ; Restore X from stack | |||
| 8382 | TAX ; Transfer to X register | |||
| 8383 | .skip_tube_update←1← 835E BEQ | |||
| LDA #&ff ; A=&FF: return value (transfer done) | ||||
| 8385 | .return_rx_complete←1← 8344 BEQ | |||
| RTS ; Return | ||||
Post-ACK frame-complete NMI handlerInstalled by
|
|||||||||||||
| 8386 | .nmi_post_ack_dispatch | ||||||||||||
| LDA scout_port ; Load received port byte | |||||||||||||
| 8389 | BNE rx_complete_update_rxcb ; Port != 0: data transfer frame | ||||||||||||
| 838B | LDY scout_ctrl ; Port=0: load control byte | ||||||||||||
| 838E | CPY #&82 ; Ctrl = &82 (POKE)? | ||||||||||||
| 8390 | BEQ rx_complete_update_rxcb ; Yes: POKE also needs data transfer | ||||||||||||
| 8392 | JMP imm_op_build_reply ; Other port-0 ops: immediate dispatch | ||||||||||||
Complete RX and update RXCBCalled from
This is the NMI-to-foreground synchronisation point:
Falls through to |
|
| 8395 | .rx_complete_update_rxcb←3← 8389 BNE← 8390 BEQ← 8431 JMP |
| JSR advance_rx_buffer_ptr ; Update buffer pointer and check for Tube | |
| 8398 | BNE skip_buf_ptr_update ; Transfer not done: skip buffer update |
| 839A | .add_buf_to_base |
| LDA port_buf_len ; Load buffer bytes remaining | |
| 839C | CLC ; For address add |
| 839D | ADC open_port_buf ; Add to buffer base address |
| 839F | BCC store_buf_ptr_lo ; No carry: skip high byte increment |
| 83A1 | .inc_rxcb_buf_hi |
| INC open_port_buf_hi ; Carry: increment buffer high byte | |
| 83A3 | .store_buf_ptr_lo←1← 839F BCC |
| LDY #8 ; Y=8: store updated buffer position | |
| 83A5 | .store_rxcb_buf_ptr |
| STA (port_ws_offset),y ; Store updated low byte to RXCB | |
| 83A7 | INY ; Y=9: buffer high byte offset |
| 83A8 | LDA open_port_buf_hi ; Load updated buffer high byte |
| 83AA | .store_rxcb_buf_hi |
| STA (port_ws_offset),y ; Store high byte to RXCB | |
| 83AC | .skip_buf_ptr_update←1← 8398 BNE |
| LDA scout_port ; Check port byte again | |
| 83AF | BEQ discard_reset_rx ; Port=0: immediate op, discard+listen |
| 83B1 | LDA scout_src_net ; Load source network from scout buffer |
| 83B4 | LDY #3 ; Y=3: RXCB source network offset |
| 83B6 | STA (port_ws_offset),y ; Store source network to RXCB |
| 83B8 | DEY ; Y=2: source station offset |
| 83B9 | LDA scout_buf ; Load source station from scout buffer |
| 83BC | STA (port_ws_offset),y ; Store source station to RXCB |
| 83BE | DEY ; Y=1: port byte offset |
| 83BF | LDA scout_port ; Load port byte |
| 83C2 | STA (port_ws_offset),y ; Store port to RXCB |
| 83C4 | DEY ; Y=0: control/flag byte offset |
| 83C5 | LDA scout_ctrl ; Load control byte from scout |
| 83C8 | ORA #&80 ; Set bit7: signals wait_net_tx_ack that reply arrived |
| 83CA | STA (port_ws_offset),y ; Store to RXCB byte 0 (bit 7 set = complete) |
| 83CC | LDA fs_flags ; Load callback event flags |
| 83CF | ROR ; Shift bit 0 into carry |
| 83D0 | BCC discard_reset_rx ; Bit 0 clear: no callback, skip to reset |
| 83D2 | LDA port_ws_offset ; Load RXCB workspace pointer low byte (carry set on entry) |
| 83D4 | .loop_count_rxcb_slot←1← 83D7 BCS |
| INY ; Count slots | |
| 83D5 | SBC #&0c ; Subtract 12 bytes per RXCB slot |
| 83D7 | BCS loop_count_rxcb_slot ; Loop until pointer exhausted |
| 83D9 | DEY ; Adjust for off-by-one |
| 83DA | CPY #3 ; Check slot index >= 3 |
| 83DC | BCC discard_reset_rx ; Slot < 3: no callback, skip to reset |
| 83DE | JSR discard_reset_listen ; Discard scout and reset listen state |
| 83E1 | TYA ; Pass slot index as callback parameter |
| 83E2 | JMP setup_sr_tx ; Jump to TX completion with slot index |
Discard scout, reset ADLC, install RX-scout NMIThree-stage idle-restore chain:
Used as the standard "something went wrong, get back to listening" exit. |
|
| 83E5 | .discard_reset_rx←6← 8220 JMP← 83AF BEQ← 83D0 BCC← 83DC BCC← 8883 JMP← 88ED JMP |
| JSR discard_reset_listen ; Discard scout and reset RX listen | |
| fall through ↓ | |
Reset ADLC and install RX-scout NMITail of the Two inbound |
|
| 83E8 | .reset_adlc_rx_listen←3← 80E5 JMP← 8434 BCS← 8529 JMP |
| JSR adlc_rx_listen ; Reset ADLC and return to RX listen | |
| fall through ↓ | |
Install nmi_rx_scout as NMI handlerLoads Two callers: |
|
| 83EB | .set_nmi_rx_scout←2← 80CB JMP← 80E2 JMP |
| LDA #&9b ; A=&9B: low byte of nmi_rx_scout | |
| 83ED | LDY #&80 ; Y=&80: high byte of nmi_rx_scout |
| 83EF | 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
Used as the clean-up path after RXCB completion and after ADLC reset to ensure no stale Tube claims persist. |
|
| 83F2 | .discard_reset_listen←2← 83DE JSR← 83E5 JSR |
| LDA #2 ; Tube flag bit 1 AND tx_flags bit 1 | |
| 83F4 | AND tube_present ; Check if Tube transfer active |
| 83F7 | BIT rx_src_net ; Test tx_flags for Tube transfer |
| 83FA | BEQ return_from_discard_reset ; No Tube transfer active -- skip release |
| 83FC | JSR release_tube ; Release Tube claim before discarding |
| 83FF | .return_from_discard_reset←1← 83FA BEQ |
| RTS ; Return | |
Copy scout data to port buffer (entry point)Five-instruction prologue that prepares to copy scout-payload
bytes (offsets
Both paths walk the four-byte buffer pointer and end via
|
|||||||
| 8400 | .copy_scout_to_buffer←1← 81A4 JMP | ||||||
| TXA ; Save X on stack | |||||||
| 8401 | PHA ; Push X | ||||||
| 8402 | LDX #4 ; X=4: start at scout byte offset 4 | ||||||
| 8404 | LDA #2 ; A=2: Tube transfer check mask | ||||||
| 8406 | .copy_scout_select | ||||||
| BIT rx_src_net ; Check tx_flags Tube bit | |||||||
| fall through ↓ | |||||||
Save ACCCON across scout-buffer accessSaves the current |
|
| 8409 | .save_acccon_for_shadow_ram |
| BNE copy_scout_via_tube ; Tube active: use R3 write path | |
| ; 4.21 Master 128: save/restore ACCCON across the ; (open_port_buf),Y stores. | |
| ; The destination port buffer may live in shadow RAM; bit 0 of ; ACCCON (D) | |
| ; controls whether (zp),Y addressing hits shadow vs main RAM. ; Workspace &97 | |
| ; holds the desired ACCCON value pre-loaded by the caller. | |
| 840B | LDA acccon ; Save current ACCCON on stack (4.21 Master 128) |
| 840E | PHA ; Push ACCCON snapshot |
| 840F | LDA escapable ; Load desired ACCCON from workspace &97 |
| 8411 | STA acccon ; Set ACCCON for the upcoming (open_port_buf),Y stores |
| 8414 | LDY port_buf_len ; Y = current buffer position |
| 8416 | .copy_scout_bytes←1← 8429 BNE |
| LDA scout_buf,x ; Load scout data byte | |
| 8419 | STA (open_port_buf),y ; Store to port buffer |
| 841B | INY ; Advance buffer pointer |
| 841C | BNE next_scout_byte ; No page crossing |
| 841E | INC open_port_buf_hi ; Page crossing: inc buffer high byte |
| 8420 | DEC port_buf_len_hi ; Decrement remaining page count |
| 8422 | BEQ scout_page_overflow ; No pages left: overflow |
| 8424 | .next_scout_byte←1← 841C BNE |
| INX ; Next scout data byte | |
| 8425 | STY port_buf_len ; Save updated buffer position |
| 8427 | CPX #&0c ; Done all scout data? (X reaches &0C) |
| 8429 | BNE copy_scout_bytes ; No: continue copying |
| 842B | .scout_copy_done |
| PLA ; Pull saved ACCCON from stack | |
| 842C | STA acccon ; Restore caller's ACCCON before continuing |
| 842F | .scout_done_restore_x←2← 8446 BEQ← 8484 BEQ |
| PLA ; Pull saved X from stack | |
| 8430 | TAX |
| 8431 | JMP rx_complete_update_rxcb ; Tail-jump to rx_complete_update_rxcb |
| 8434 | .dispatch_imm_op_fail←1← 846F BCS |
| BCS reset_adlc_rx_listen ; Reset ADLC if carry set | |
| 8436 | .copy_scout_via_tube←2← 8409 BNE← 8444 BNE |
| LDA scout_buf,x ; Tube path: load scout data byte | |
| 8439 | STA tube_data_register_3 ; Send byte to Tube via R3 |
| 843C | JSR advance_buffer_ptr ; Increment buffer position counters |
| 843F | BEQ tube_overflow_restore ; Counter overflow: handle end of buffer |
| 8441 | INX ; Next scout data byte |
| 8442 | CPX #&0c ; Done all scout data? |
| 8444 | BNE copy_scout_via_tube ; No: continue Tube writes |
| 8446 | BEQ scout_done_restore_x ; ALWAYS branch |
Release Tube co-processor claimTests bit 7 of
Both paths end at Called after completed RX transfers and during discard paths to ensure no stale Tube claims persist. Idempotent: safe to call when the Tube has already been
released. Clobbers
|
|||||||||||||
| 8448 | .release_tube←2← 83FC JSR← 8961 JSR | ||||||||||||
| BIT prot_flags ; Check if Tube needs releasing | |||||||||||||
| 844A | BMI clear_release_flag ; Bit7 set: already released | ||||||||||||
| 844C | LDA #&82 ; A=&82: Tube release claim type | ||||||||||||
| 844E | JSR tube_addr_data_dispatch ; Release Tube address claim | ||||||||||||
| 8451 | .clear_release_flag←1← 844A BMI | ||||||||||||
| LSR prot_flags ; Clear release flag (LSR clears bit7) | |||||||||||||
| 8453 | RTS ; Return | ||||||||||||
Immediate operation handler (port = 0)Checks the control byte at
For Builds the reply by storing data length, station / network, and control byte into the RX buffer header. |
|||||||||||||
| 8454 | .immediate_op←1← 8138 JMP | ||||||||||||
| LDY scout_ctrl ; Control byte &81-&88 range check | |||||||||||||
| 8457 | CPY #&81 ; Below &81: not an immediate op | ||||||||||||
| 8459 | BCC imm_op_out_of_range ; Out of range low: jump to discard | ||||||||||||
| 845B | CPY #&89 ; Above &88: not an immediate op | ||||||||||||
| 845D | BCS imm_op_out_of_range ; Out of range high: jump to discard | ||||||||||||
| 845F | CPY #&87 ; HALT(&87)/CONTINUE(&88) skip protection | ||||||||||||
| 8461 | BCS dispatch_imm_op ; Ctrl >= &87: dispatch without mask check | ||||||||||||
| 8463 | TYA ; Convert ctrl byte to 0-based index for mask | ||||||||||||
| 8464 | SEC ; For subtract | ||||||||||||
| 8465 | SBC #&81 ; A = ctrl - &81 (0-based operation index) | ||||||||||||
| 8467 | TAY ; Y = index for mask rotation count | ||||||||||||
| 8468 | LDA ws_0d68 ; Load protection mask from LSTAT | ||||||||||||
| 846B | .rotate_prot_mask←1← 846D BPL | ||||||||||||
| ROR ; Rotate mask right by control byte index | |||||||||||||
| 846C | DEY ; Decrement rotation counter | ||||||||||||
| 846D | BPL rotate_prot_mask ; Loop until bit aligned | ||||||||||||
| 846F | BCS dispatch_imm_op_fail ; Bit set = operation disabled, discard | ||||||||||||
| 8471 | .dispatch_imm_op←1← 8461 BCS | ||||||||||||
| LDY scout_ctrl ; Reload ctrl byte for dispatch table | |||||||||||||
| 8474 | LDA #&84 ; Hi byte: all handlers are in page &84 | ||||||||||||
| 8476 | PHA ; Push hi byte for PHA/PHA/RTS dispatch | ||||||||||||
| 8477 | LDA imm_op_handler_lo_table,y ; Load handler low byte from jump table | ||||||||||||
| 847A | PHA ; Push handler low byte | ||||||||||||
| 847B | RTS ; RTS dispatches to handler | ||||||||||||
| 847C | .scout_page_overflow←1← 8422 BEQ | ||||||||||||
| INC port_buf_len ; Increment port buffer length | |||||||||||||
| ; Tube-path overflow exit from copy_scout_to_buffer: restores ; the Master 128 | |||||||||||||
| ; ACCCON that was saved at &840B before re-joining the ; scout-done path. | |||||||||||||
| 847E | .tube_overflow_restore←1← 843F BEQ | ||||||||||||
| PLA ; Pull saved ACCCON from stack | |||||||||||||
| 847F | STA acccon ; Restore caller's ACCCON on Tube-overflow exit | ||||||||||||
| 8482 | .check_scout_done | ||||||||||||
| CPX #&0b ; Check if scout data index reached 11 | |||||||||||||
| 8484 | BEQ scout_done_restore_x ; Yes: loop back to continue reading | ||||||||||||
| 8486 | PLA ; Restore A from stack | ||||||||||||
| 8487 | TAX ; Transfer to X | ||||||||||||
| 8488 | .imm_op_out_of_range←2← 8459 BCC← 845D BCS | ||||||||||||
| JMP nmi_error_dispatch ; Jump to discard handler | |||||||||||||
Immediate-op dispatch lo-byte table (8 entries)Eight low-byte entries at |
|
| 848B | .imm_op_dispatch_lo |
| EQUB <(rx_imm_peek-1) ; ctrl &81: PEEK | |
| 848C | EQUB <(rx_imm_poke-1) ; ctrl &82: POKE |
| 848D | EQUB <(rx_imm_exec-1) ; ctrl &83: JSR |
| 848E | EQUB <(rx_imm_exec-1) ; ctrl &84: UserProc |
| 848F | EQUB <(rx_imm_exec-1) ; ctrl &85: OSProc |
| 8490 | EQUB <(rx_imm_halt_cont-1) ; ctrl &86: HALT |
| 8491 | EQUB <(rx_imm_halt_cont-1) ; ctrl &87: CONTINUE |
| 8492 | EQUB <(rx_imm_machine_type-1) ; ctrl &88: machine-type |
RX immediate: JSR / UserProc / OSProc setupSets up the port buffer to receive remote-procedure data. Copies
the 2-byte remote address from Used for operation types |
|
| 8493 | .rx_imm_exec |
| LDA #0 ; A=0: port buffer lo at page boundary | |
| 8495 | STA open_port_buf ; Set port buffer lo |
| 8497 | LDA #&82 ; Buffer length lo = &82 |
| 8499 | STA port_buf_len ; Set buffer length lo |
| 849B | LDA #1 ; Buffer length hi = 1 |
| 849D | STA port_buf_len_hi ; Set buffer length hi |
| 849F | LDA net_rx_ptr_hi ; Load RX page hi for buffer |
| 84A1 | STA open_port_buf_hi ; Set port buffer hi |
| 84A3 | LDY #1 ; Y=1: copy 2 bytes (1 down to 0) |
| 84A5 | .copy_addr_loop←1← 84AC BPL |
| LDA scout_data,y ; Load remote address byte | |
| 84A8 | STA exec_addr_lo,y ; Store to exec address workspace |
| 84AB | DEY ; Next byte (descending) |
| 84AC | BPL copy_addr_loop ; Loop until all 4 bytes copied |
| 84AE | .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:
|
|
| 84B1 | .svc5_dispatch_lo |
| .rx_imm_poke | |
| LDA #&2e ; Port workspace offset = &2E | |
| 84B3 | STA port_ws_offset ; Store as port_ws_offset |
| 84B5 | LDA #&0d ; RX buffer page = &0D |
| 84B7 | STA rx_buf_offset ; Store as rx_buf_offset |
| 84B9 | JMP port_match_found ; Enter POKE data-receive path |
RX immediate: machine-type querySets up the response buffer for a machine-type query immediate
operation (4-byte response: machine code + version digits). Falls
through to |
|
| 84BC | .rx_imm_machine_type |
| LDA #1 ; Buffer length hi = 1 | |
| 84BE | .set_rx_buf_len_hi |
| STA port_buf_len_hi ; Set buffer length hi | |
| 84C0 | LDA #&fc ; Buffer length lo = &FC |
| 84C2 | STA port_buf_len ; Set buffer length lo |
| 84C4 | LDA #&ee ; Buffer start lo = &EE |
| 84C6 | STA open_port_buf ; Set port buffer lo |
| 84C8 | LDA #&88 ; Buffer hi = &88 (response goes to &88EE area) |
| 84CA | STA open_port_buf_hi ; Set port buffer hi |
| 84CC | BNE set_tx_reply_flag ; ALWAYS branch |
RX immediate: PEEK setupWrites |
|
| 84CE | .rx_imm_peek |
| LDA #&2e ; Port workspace offset = &3D | |
| 84D0 | STA port_ws_offset ; Store workspace offset lo |
| 84D2 | LDA #&0d ; RX buffer page = &0D |
| 84D4 | STA rx_buf_offset ; Store workspace offset hi |
| 84D6 | LDA #2 ; Scout status = 2 (PEEK response) |
| 84D8 | STA rx_port ; Store scout status |
| 84DB | JSR tx_calc_transfer ; Calculate transfer size for response |
| 84DE | BCC imm_op_discard ; C=0: transfer not set up, discard |
| 84E0 | .set_tx_reply_flag←1← 84CC BNE |
| LDA rx_src_net ; Mark TX flags bit 7 (reply pending) | |
| 84E3 | ORA #&80 ; Set reply pending flag |
| 84E5 | STA rx_src_net ; Store updated TX flags |
| 84E8 | .rx_imm_halt_cont |
| LDA #&44 ; CR1=&44: TIE | TX_LAST_DATA | |
| 84EA | STA adlc_cr1 ; Write CR1: enable TX interrupts |
| 84ED | .tx_cr2_setup |
| LDA #&a7 ; CR2=&A7: RTS|CLR_RX_ST|FC_TDRA|PSE | |
| 84EF | STA adlc_cr2 ; Write CR2 for TX setup |
| 84F2 | .tx_nmi_setup |
| LDA #&0f ; NMI handler lo byte (self-modifying) | |
| 84F4 | LDY #&85 ; Y=&85: NMI handler high byte |
| 84F6 | JMP ack_tx_write_dest ; Acknowledge and write TX dest |
Build immediate-operation reply headerWrites the reply-frame header for a port-0 immediate operation
into the RX buffer at offsets
Then loads the control byte from
|
|||||||||||||
| 84F9 | .imm_op_build_reply←1← 8392 JMP | ||||||||||||
| LDA port_buf_len ; Get buffer position for reply header | |||||||||||||
| 84FB | CLC ; Clear carry for offset addition | ||||||||||||
| 84FC | ADC #&80 ; Data offset = buf_len + &80 (past header) | ||||||||||||
| 84FE | LDY #&7f ; Y=&7F: reply data length slot | ||||||||||||
| 8500 | STA (net_rx_ptr),y ; Store reply data length in RX buffer | ||||||||||||
| 8502 | LDY #&80 ; Y=&80: source station slot | ||||||||||||
| 8504 | LDA scout_buf ; Load requesting station number | ||||||||||||
| 8507 | STA (net_rx_ptr),y ; Store source station in reply header | ||||||||||||
| 8509 | INY ; Y=&81 | ||||||||||||
| 850A | LDA scout_src_net ; Load requesting network number | ||||||||||||
| 850D | STA (net_rx_ptr),y ; Store source network in reply header | ||||||||||||
| 850F | LDA scout_ctrl ; Load control byte from received frame | ||||||||||||
| fall through ↓ | |||||||||||||
Save TX op type and configure shift-register modeStores the TX operation type in
The shadow is flushed to the real VIA
|
||||||||||
| 8512 | .setup_sr_tx←1← 83E2 JMP | |||||||||
| STA tx_op_type ; Save TX operation type for SR dispatch | ||||||||||
| 8515 | CMP #&86 ; Op codes >= &86 (HALT/CONTINUE/machine-type) skip the SR setup | |||||||||
| 8517 | BCS enable_irq_pending ; Skip ahead to the ACCCON IRR set | |||||||||
| 8519 | LDA ws_0d68 ; Load shadow ACR/IER state | |||||||||
| 851C | STA ws_0d69 ; Stash a copy in ws_0d69 for later restore | |||||||||
| 851F | ORA #&1c ; In shift-register mode-2 control bits | |||||||||
| 8521 | STA ws_0d68 ; Write updated VIA ACR shadow back to ws_0d68 | |||||||||
| 8524 | .enable_irq_pending←1← 8517 BCS | |||||||||
| LDA #&80 ; A=&80: ACCCON bit 7 (IRR -- raise interrupt) | ||||||||||
| 8526 | TSB acccon ; Set ACCCON IRR to flag a pending interrupt to MOS | |||||||||
| 8529 | .imm_op_discard←1← 84DE BCC | |||||||||
| JMP reset_adlc_rx_listen ; Return to idle listen mode | ||||||||||
Increment 4-byte receive-buffer pointerAdds 1 to the 4-byte counter at Preserves
|
||||
| 852C | .advance_buffer_ptr←3← 8299 JSR← 82A7 JSR← 843C JSR | |||
| INC port_buf_len ; Increment buffer length low byte | ||||
| 852E | BNE return_from_advance_buf ; No overflow: done | |||
| 8530 | INC port_buf_len_hi ; Increment buffer length high byte | |||
| 8532 | BNE return_from_advance_buf ; No overflow: done | |||
| 8534 | INC open_port_buf ; Increment buffer pointer low byte | |||
| 8536 | BNE return_from_advance_buf ; No overflow: done | |||
| 8538 | INC open_port_buf_hi ; Increment buffer pointer high byte | |||
| 853A | .return_from_advance_buf←3← 852E BNE← 8532 BNE← 8536 BNE | |||
| RTS ; Return | ||||
TX done dispatch lo-byte table (5 entries)Low bytes of PHA/PHA/RTS dispatch targets for TX operation types
|
|
| 853B | .tx_done_dispatch_lo |
| EQUB <(tx_done_jsr-1) ; op &83: remote JSR | |
| 853C | EQUB <(tx_done_econet_event-1) ; op &84: fire Econet event |
| 853D | EQUB <(tx_done_os_proc-1) ; op &85: OSProc call |
| 853E | EQUB <(tx_done_halt-1) ; op &86: HALT |
| 853F | EQUB <(tx_done_continue-1) ; op &87: CONTINUE |
TX done: remote JSR executionPushes ( |
|
| 8540 | .tx_done_jsr |
| LDA #&85 ; A=&85: high byte of tx_done_exit-1 (&8581) | |
| 8542 | PHA ; Push hi byte on stack |
| 8543 | LDA #&81 ; A=&81: low byte of tx_done_exit-1 (&8581) |
| 8545 | PHA ; Push lo byte on stack |
| 8546 | JMP (exec_addr_lo) ; Call remote JSR; RTS to tx_done_exit |
TX done: fire Econet eventHandler for TX operation type Reached only via
|
||||||
| 8549 | .tx_done_econet_event | |||||
| LDX exec_addr_lo ; X = remote address lo from exec_addr_lo | ||||||
| 854C | LDA exec_addr_hi ; A = remote address hi from exec_addr_hi | |||||
| 854F | LDY #event_network_error ; Y = 8: Econet event number | |||||
| 8551 | .tx_done_fire_event | |||||
| JSR oseven ; Generate event Y='Network error' | ||||||
| 8554 | JMP tx_done_exit ; Exit TX done handler | |||||
TX done: OSProc callCalls the ROM service entry point with
Reached only via
|
||||||
| 8557 | .tx_done_os_proc | |||||
| LDX exec_addr_lo ; X = remote address lo | ||||||
| 855A | LDY exec_addr_hi ; Y = remote address hi | |||||
| 855D | JSR dir_op_dispatch ; Call ROM entry point at &8000 | |||||
| 8560 | JMP tx_done_exit ; Exit TX done handler | |||||
TX done: HALTSets bit 2 of Reached only via
|
||||||||
| 8563 | .tx_done_halt | |||||||
| LDA #4 ; A=&04: bit 2 mask (halt flag in econet_flags) | ||||||||
| 8565 | BIT econet_flags ; Test if already halted | |||||||
| 8568 | BNE tx_done_exit ; Already halted: skip to exit | |||||||
| 856A | ORA econet_flags ; Set bit 2 in econet_flags (halt) | |||||||
| 856D | STA econet_flags ; Store halt flag | |||||||
| 8570 | LDA #4 ; A=4: re-load halt bit mask | |||||||
| 8572 | CLI ; Enable interrupts during halt wait | |||||||
| 8573 | .halt_spin_loop←1← 8576 BNE | |||||||
| BIT econet_flags ; Test halt flag | ||||||||
| 8576 | BNE halt_spin_loop ; Still halted: keep spinning | |||||||
| 8578 | BEQ tx_done_exit ; ALWAYS branch | |||||||
TX done: CONTINUEClears bit 2 of Reached either as a fall-through from
|
||||||
| 857A | .tx_done_continue | |||||
| LDA econet_flags ; Load current econet_flags | ||||||
| 857D | AND #&fb ; Clear bit 2: release halted station | |||||
| 857F | STA econet_flags ; Store updated flags | |||||
| fall through ↓ | ||||||
Shared TX-done exit: restore X/Y, return A=0Common cleanup tail used by every entry in the
Five inbound refs: a tail-jump from
|
||||||
| 8582 | .tx_done_exit←5← 8042 JMP← 8554 JMP← 8560 JMP← 8568 BNE← 8578 BEQ | |||||
| PLA ; Restore Y from stack | ||||||
| 8583 | TAY ; Transfer to Y register | |||||
| 8584 | PLA ; Restore X from stack | |||||
| 8585 | TAX ; Transfer to X register | |||||
| 8586 | LDA #0 ; A=0: success status | |||||
| 8588 | RTS ; Return with A=0 (success) | |||||
Begin TX operationMain TX initiation entry point (called via the NETV trampoline).
|
|
| 8589 | .tx_begin←3← 9BC3 JSR← A92A JMP← AC1E JSR |
| TXA ; Save X on stack | |
| 858A | PHA ; Push X |
| 858B | LDY #2 ; Y=2: TXCB offset for dest station |
| 858D | LDA (nmi_tx_block),y ; Load dest station from TX control block |
| 858F | STA tx_dst_stn ; Store to TX scout buffer |
| 8592 | INY ; Y=&03 |
| 8593 | LDA (nmi_tx_block),y ; Load dest network from TX control block |
| 8595 | STA tx_dst_net ; Store to TX scout buffer |
| 8598 | LDY #0 ; Y=0: first byte of TX control block |
| 859A | LDA (nmi_tx_block),y ; Load control/flag byte |
| 859C | BMI tx_imm_op_setup ; Bit7 set: immediate operation ctrl byte |
| 859E | JMP tx_bad_ctrl_error ; Bit7 clear: normal data transfer |
| 85A1 | .tx_imm_op_setup←1← 859C BMI |
| STA tx_ctrl_byte ; Store control byte to TX scout buffer | |
| 85A4 | TAX ; X = control byte for range checks |
| 85A5 | INY ; Y=1: port byte offset |
| 85A6 | LDA (nmi_tx_block),y ; Load port byte from TX control block |
| 85A8 | STA tx_port ; Store port byte to TX scout buffer |
| 85AB | BNE tx_line_idle_check ; Port != 0: skip immediate op setup |
| 85AD | CPX #&83 ; Ctrl < &83: PEEK/POKE need address calc |
| 85AF | BCS tx_ctrl_range_check ; Ctrl >= &83: skip to range check |
| 85B1 | SEC ; Init borrow for 4-byte subtract |
| 85B2 | PHP ; Save carry on stack for loop |
| 85B3 | LDY #8 ; Y=8: high pointer offset in TXCB |
| 85B5 | .calc_peek_poke_size←1← 85C9 BCC |
| LDA (nmi_tx_block),y ; Load TXCB[Y] (end addr byte) | |
| 85B7 | DEY ; Y -= 4: back to start addr offset |
| 85B8 | DEY ; (continued) |
| 85B9 | DEY ; (continued) |
| 85BA | DEY ; (continued) |
| 85BB | PLP ; Restore borrow from stack |
| 85BC | SBC (nmi_tx_block),y ; end - start = transfer size byte |
| 85BE | STA tx_data_start,y ; Store result to tx_data_start |
| 85C1 | INY ; Y += 5: advance to next end byte |
| 85C2 | INY ; (continued) |
| 85C3 | INY ; (continued) |
| 85C4 | INY ; (continued) |
| 85C5 | INY ; (continued) |
| 85C6 | PHP ; Save borrow for next byte |
| 85C7 | CPY #&0c ; Done all 4 bytes? (Y reaches &0C) |
| 85C9 | BCC calc_peek_poke_size ; No: next byte pair |
| 85CB | PLP ; Discard final borrow |
| 85CC | .tx_ctrl_range_check←1← 85AF BCS |
| CPX #&81 ; Ctrl < &81: not an immediate op | |
| 85CE | BCC tx_bad_ctrl_error ; Below range: normal data transfer |
| 85D0 | .check_imm_range |
| CPX #&89 ; Ctrl >= &89: out of immediate range | |
| 85D2 | BCS tx_bad_ctrl_error ; Above range: normal data transfer |
| 85D4 | LDY #&0c ; Y=&0C: start of extra data in TXCB |
| 85D6 | .copy_imm_params←1← 85DE BCC |
| LDA (nmi_tx_block),y ; Load extra parameter byte from TXCB | |
| 85D8 | STA imm_param_base,y ; Copy to NMI shim workspace at &0D1A+Y |
| 85DB | INY ; Next byte |
| 85DC | CPY #&10 ; Done 4 bytes? (Y reaches &10) |
| 85DE | BCC copy_imm_params ; No: continue copying |
| 85E0 | .tx_line_idle_check←1← 85AB BNE |
| LDA #&20 ; A=&20: mask for SR2 INACTIVE bit | |
| 85E2 | BIT adlc_cr2 ; Test SR2 if line is idle |
| 85E5 | BNE tx_no_clock_error ; Line not idle: handle as line jammed |
| 85E7 | LDA #&fd ; A=&FD: high byte of timeout counter |
| 85E9 | PHA ; Push timeout high byte to stack |
| 85EA | LDA #6 ; Scout frame = 6 address+ctrl bytes |
| 85EC | STA rx_ctrl ; Store scout frame length |
| 85EF | LDA #0 ; A=0: init low byte of timeout counter |
| fall through ↓ | |
INACTIVE polling loopEntry point for the Econet line-idle detection loop.
|
||||
| 85F1 | .inactive_poll | |||
| STA rx_remote_addr ; Save TX index | ||||
| 85F4 | PHA ; Push timeout byte 1 on stack | |||
| 85F5 | PHA ; Push timeout byte 2 on stack | |||
| 85F6 | LDY #&e7 ; Y=&E7: CR2 value for TX prep (RTS|CLR_TX_ST|CLR_RX_ST|FC_TDRA|2_1_ BYTE|PSE) | |||
| 85F8 | .reload_inactive_mask←3← 861E BNE← 8623 BNE← 8628 BNE | |||
| LDA #4 ; A=&04: INACTIVE bit mask for SR2 test | ||||
| 85FA | .test_inactive_retry | |||
| PHP ; Save interrupt state | ||||
| 85FB | SEI ; Disable interrupts for ADLC access | |||
| fall through ↓ | ||||
Disable NMIs and test INACTIVEDisables NMIs via two
On timeout, falls through to
|
||||||||||||
| 85FC | .intoff_test_inactive | |||||||||||
| BIT master_intoff ; INTOFF -- disable NMIs | ||||||||||||
| 85FF | BIT master_intoff ; INTOFF again (belt-and-braces) | |||||||||||
| 8602 | .test_line_idle | |||||||||||
| BIT adlc_cr2 ; Z = &04 AND SR2 -- tests INACTIVE | ||||||||||||
| 8605 | BEQ inactive_retry ; INACTIVE not set -- re-enable NMIs and loop | |||||||||||
| 8607 | LDA adlc_cr1 ; Read SR1 (acknowledge pending interrupt) | |||||||||||
| 860A | LDA #&67 ; CR2=&67: CLR_TX_ST|CLR_RX_ST|FC_TDRA|2_1_BYTE| PSE | |||||||||||
| 860C | STA adlc_cr2 ; Write CR2: clear status, prepare TX | |||||||||||
| 860F | LDA #&10 ; A=&10: CTS mask for SR1 bit4 | |||||||||||
| 8611 | BIT adlc_cr1 ; Test SR1 CTS present | |||||||||||
| 8614 | BNE tx_prepare ; CTS set -- clock hardware detected, start TX | |||||||||||
| 8616 | .inactive_retry←1← 8605 BEQ | |||||||||||
| BIT master_inton ; INTON -- re-enable NMIs (&FE20 read) | ||||||||||||
| 8619 | PLP ; Restore interrupt state | |||||||||||
| 861A | TSX ; 3-byte timeout counter on stack | |||||||||||
| 861B | INC error_text,x ; Increment timeout counter byte 1 | |||||||||||
| 861E | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test | |||||||||||
| 8620 | INC stack_page_2,x ; Increment timeout counter byte 2 | |||||||||||
| 8623 | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test | |||||||||||
| 8625 | INC stack_page_3,x ; Increment timeout counter byte 3 | |||||||||||
| 8628 | BNE reload_inactive_mask ; Not overflowed: retry INACTIVE test | |||||||||||
| 862A | BEQ tx_line_jammed ; ALWAYS branch | |||||||||||
Raise TX 'Bad control byte' (&44) errorLoads error code Reached from three early-validation sites in
|
||||
| 862C | .tx_bad_ctrl_error←3← 859E JMP← 85CE BCC← 85D2 BCS | |||
| LDA #&44 ; Error &44: control byte out of valid range | ||||
| 862E | BNE store_tx_error ; ALWAYS branch | |||
TX timeout error handler (Line Jammed)Reached when the
|
|
| 8630 | .tx_line_jammed←1← 862A BEQ |
| LDA #7 ; CR2=&07: FC_TDRA | 2_1_BYTE | PSE (abort TX) | |
| 8632 | STA adlc_cr2 ; Write CR2 to abort TX |
| 8635 | PLA ; Clean 3 bytes of timeout loop state |
| 8636 | PLA ; Pop saved register |
| 8637 | PLA ; Pop saved register |
| 8638 | LDA #&40 ; Error &40 = 'Line Jammed' |
| 863A | BNE store_tx_error ; ALWAYS branch to shared error handler |
| 863C | .tx_no_clock_error←1← 85E5 BNE |
| LDA #&43 ; Error &43 = 'No Clock' | |
| 863E | .store_tx_error←2← 862E BNE← 863A BNE |
| LDY #0 ; Offset 0 = error byte in TX control block | |
| 8640 | STA (nmi_tx_block),y ; Store error code in TX CB byte 0 |
| 8642 | LDA #&80 ; &80 = TX complete flag |
| 8644 | STA tx_complete_flag ; Signal TX operation complete |
| 8647 | PLA ; Restore X saved by caller |
| 8648 | TAX ; Move to X register |
| 8649 | RTS ; Return to TX caller |
TX preparationConfigures the ADLC for frame transmission and dispatches to the control-byte handler.
Then dispatches on
The 4-byte destination-address write to the TX FIFO happens in
the dispatched-to handler (e.g.
|
||||||||||
| 864A | .tx_prepare←1← 8614 BNE | |||||||||
| STY adlc_cr2 ; Write CR2 = Y (&E7: RTS|CLR_TX_ST|CLR_RX_ST|FC_ TDRA|2_1_BYTE|PSE) | ||||||||||
| 864D | LDX #&44 ; CR1=&44: RX_RESET | TIE (TX active, TX interrupts enabled) | |||||||||
| 864F | STX adlc_cr1 ; Write to ADLC CR1 | |||||||||
| 8652 | LDX #&e7 ; X=&E7: low byte of nmi_tx_data (&86E7) | |||||||||
| 8654 | LDY #&86 ; High byte of NMI handler address | |||||||||
| 8656 | STX nmi_jmp_lo ; Write NMI vector low byte directly | |||||||||
| 8659 | STY nmi_jmp_hi ; Write NMI vector high byte directly | |||||||||
| 865C | SEC ; SEC: prepare carry for ROR into bit 7 | |||||||||
| 865D | ROR prot_flags ; Rotate carry into bit 7 of prot_flags (Tube-claimed) | |||||||||
| 865F | BIT master_inton ; INTON -- NMIs now fire for TDRA (&FE20 read) | |||||||||
| 8662 | LDA tx_port ; Load destination port number | |||||||||
| 8665 | BNE setup_data_xfer ; Port != 0: standard data transfer | |||||||||
| 8667 | LDY tx_ctrl_byte ; Port 0: load control byte for table lookup | |||||||||
| 866A | LDA tx_flags_table,y ; Look up tx_flags from table | |||||||||
| 866D | STA rx_src_net ; Store operation flags | |||||||||
| 8670 | LDA tx_length_table,y ; Look up tx_length from table | |||||||||
| 8673 | STA rx_ctrl ; Store expected transfer length | |||||||||
| 8676 | LDA #&86 ; A=&86: high byte of tx_ctrl_* dispatch target | |||||||||
| 8678 | PHA ; Push high byte for PHA/PHA/RTS dispatch | |||||||||
| 8679 | LDA tx_ctrl_dispatch_lo-&81,y ; Look up handler address low from table | |||||||||
| 867C | PHA ; Push low byte for PHA/PHA/RTS dispatch | |||||||||
| 867D | RTS ; RTS dispatches to control-byte handler | |||||||||
TX ctrl dispatch lo-byte table (8 entries)Low bytes of PHA/PHA/RTS dispatch targets for TX control byte
types |
|
| 867E | .tx_ctrl_dispatch_lo |
| EQUB <(tx_ctrl_peek-1) ; ctrl &81: PEEK | |
| 867F | EQUB <(tx_ctrl_poke-1) ; ctrl &82: POKE |
| 8680 | EQUB <(proc_op_status2-1) ; ctrl &83: JSR |
| 8681 | EQUB <(proc_op_status2-1) ; ctrl &84: UserProc |
| 8682 | EQUB <(proc_op_status2-1) ; ctrl &85: OSProc |
| 8683 | EQUB <(tx_ctrl_exit-1) ; ctrl &86: HALT |
| 8684 | EQUB <(tx_ctrl_exit-1) ; ctrl &87: CONTINUE |
| 8685 | EQUB <(tx_ctrl_machine_type-1) ; ctrl &88: machine type |
TX ctrl: machine-type query setupHandler for control byte Reached only via
|
||||
| 8686 | .tx_ctrl_machine_type | |||
| LDA #3 ; A=3: scout_status for machine type query | ||||
| 8688 | BNE store_status_copy_ptr ; Skip address addition, store status | |||
| fall through ↓ | ||||
TX ctrl: PEEK transfer setupSets
|
||||
| 868A | .tx_ctrl_peek | |||
| LDA #3 ; A=3: scout_status for PEEK op | ||||
| 868C | BNE tx_ctrl_store_and_add ; ALWAYS branch | |||
TX ctrl: POKE transfer setupSets
|
||||
| 868E | .tx_ctrl_poke | |||
| LDA #2 ; Scout status = 2 (POKE transfer) | ||||
| fall through ↓ | ||||
TX ctrl: store status and add transfer addressShared path for PEEK (
|
||||
| 8690 | .tx_ctrl_store_and_add←1← 868C BNE | |||
| STA rx_port ; Store scout status | ||||
| 8693 | CLC ; Clear carry for 4-byte addition | |||
| 8694 | PHP ; Save carry on stack | |||
| 8695 | LDY #&0c ; Y=&0C: start at offset 12 | |||
| 8697 | .add_bytes_loop←1← 86A4 BCC | |||
| LDA tx_addr_base,y ; Load workspace address byte | ||||
| 869A | PLP ; Restore carry from previous byte | |||
| 869B | ADC (nmi_tx_block),y ; Add TXCB address byte | |||
| 869D | STA tx_addr_base,y ; Store updated address byte | |||
| 86A0 | INY ; Next byte | |||
| 86A1 | PHP ; Save carry for next addition | |||
| fall through ↓ | ||||
TX ctrl: tail of address-add loop + setup_data_xfer entryTail of the 4-byte transfer-address addition loop that started in
Falls through (or is reached via the dispatch from
|
|
| 86A2 | .tx_ctrl_proc |
| CPY #&10 ; Compare Y with 16-byte boundary | |
| 86A4 | BCC add_bytes_loop ; Below boundary: continue addition |
| 86A6 | PLP ; Restore processor flags |
| 86A7 | BNE skip_buf_setup ; Skip buffer setup if transfer size is zero |
| 86A9 | .setup_data_xfer←1← 8665 BNE |
| LDA tx_dst_stn ; Load dest station for broadcast check | |
| 86AC | AND tx_dst_net ; AND with dest network |
| 86AF | CMP #&ff ; Both &FF = broadcast address? |
| 86B1 | BNE setup_unicast_xfer ; Not broadcast: unicast path |
| 86B3 | LDA #&0e ; Broadcast scout: 14 bytes total |
| 86B5 | STA rx_ctrl ; Store broadcast scout length |
| 86B8 | LDA #&40 ; A=&40: broadcast flag |
| 86BA | STA rx_src_net ; Set broadcast flag in tx_flags |
| 86BD | LDY #4 ; Y=4: start of address data in TXCB |
| 86BF | .copy_bcast_addr←1← 86C7 BCC |
| LDA (nmi_tx_block),y ; Copy TXCB address bytes to scout buffer | |
| 86C1 | STA tx_src_stn,y ; Store to TX source/data area |
| 86C4 | INY ; Next byte |
| 86C5 | CPY #&0c ; Done 8 bytes? (Y reaches &0C) |
| 86C7 | BCC copy_bcast_addr ; No: continue copying |
| 86C9 | BCS tx_ctrl_exit ; ALWAYS branch |
| 86CB | .setup_unicast_xfer←1← 86B1 BNE |
| LDA #0 ; A=0: clear flags for unicast | |
| 86CD | STA rx_src_net ; Clear tx_flags |
| 86D0 | .proc_op_status2 |
| LDA #2 ; scout_status=2: data transfer pending | |
| 86D2 | .store_status_copy_ptr←1← 8688 BNE |
| STA rx_port ; Store scout status | |
| 86D5 | .skip_buf_setup←1← 86A7 BNE |
| LDA nmi_tx_block ; Copy TX block pointer to workspace ptr | |
| 86D7 | STA port_ws_offset ; Store low byte |
| 86D9 | LDA nmi_tx_block_hi ; Copy TX block pointer high byte |
| 86DB | STA rx_buf_offset ; Store high byte |
| 86DD | JSR tx_calc_transfer ; Calculate transfer size from RXCB |
| 86E0 | .tx_ctrl_exit←1← 86C9 BCS |
| PLP ; Restore processor status from stack | |
| 86E1 | PLA ; Restore stacked registers (4 PLAs) |
| 86E2 | PLA |
| 86E3 | PLA |
| 86E4 | PLA |
| 86E5 | TAX ; Restore X from A |
| 86E6 | RTS ; Return to caller |
NMI TX data handlerWrites 2 bytes per NMI invocation to the TX FIFO at
After writing 2 bytes, checks if the frame is complete:
|
|||||||
| 86E7 | .nmi_tx_data | ||||||
| LDY rx_remote_addr ; Load TX buffer index | |||||||
| 86EA | BIT adlc_cr1 ; SR1: V=bit6(TDRA), N=bit7(IRQ) | ||||||
| 86ED | .tx_fifo_write←1← 8708 BMI | ||||||
| BVC tx_fifo_not_ready ; TDRA not set -- TX error | |||||||
| 86EF | LDA tx_dst_stn,y ; Load byte from TX buffer | ||||||
| 86F2 | STA adlc_tx ; Write to TX_DATA (continue frame) | ||||||
| 86F5 | INY ; Next TX buffer byte | ||||||
| 86F6 | LDA tx_dst_stn,y ; Load second byte from TX buffer | ||||||
| 86F9 | INY ; Advance TX index past second byte | ||||||
| 86FA | STY rx_remote_addr ; Save updated TX buffer index | ||||||
| 86FD | STA adlc_tx ; Write second byte to TX_DATA | ||||||
| 8700 | CPY rx_ctrl ; Compare index to TX length | ||||||
| 8703 | BCS tx_last_data ; Frame complete -- go to TX_LAST_DATA | ||||||
| 8705 | BIT adlc_cr1 ; Check if we can send another pair | ||||||
| 8708 | BMI tx_fifo_write ; IRQ set -- send 2 more bytes (tight loop) | ||||||
| 870A | JMP nmi_rti ; Wait for next NMI | ||||||
| 870D | .tx_error←1← 8750 BEQ | ||||||
| LDA #&42 ; Error &42 | |||||||
| 870F | BNE tx_store_error ; ALWAYS branch | ||||||
| 8711 | .tx_fifo_not_ready←1← 86ED BVC | ||||||
| LDA #&67 ; CR2=&67: clear status, return to listen | |||||||
| 8713 | STA adlc_cr2 ; Write CR2: clear status, idle listen | ||||||
| 8716 | LDA #&41 ; Error &41 (TDRA not ready) | ||||||
| 8718 | .tx_store_error←1← 870F BNE | ||||||
| LDY tx_src_stn ; INTOFF (also loads station ID) | |||||||
| 871B | .delay_nmi_disable←1← 871E BNE | ||||||
| PHA ; PHA/PLA delay loop (256 iterations for NMI disable) | |||||||
| 871C | PLA ; PHA/PLA delay (~7 cycles each) | ||||||
| 871D | INY ; Increment delay counter | ||||||
| 871E | BNE delay_nmi_disable ; Loop 256 times for NMI disable | ||||||
| 8720 | JMP tx_store_result ; Store error and return to idle | ||||||
TX_LAST_DATA and frame completionSignals end of TX frame by writing
The routine exits via |
||||||||||||||||||||||||||||
| 8723 | .tx_last_data←1← 8703 BCS | |||||||||||||||||||||||||||
| LDA #&3f ; CR2=&3F: TX_LAST_DATA | CLR_RX_ST | FLAG_IDLE | FC_TDRA | 2_1_BYTE | PSE | ||||||||||||||||||||||||||||
| 8725 | STA adlc_cr2 ; Write to ADLC CR2 | |||||||||||||||||||||||||||
| 8728 | LDA #&2f ; Install NMI handler at &8728 (TX completion) | |||||||||||||||||||||||||||
| 872A | LDY #&87 ; High byte of handler address | |||||||||||||||||||||||||||
| 872C | 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. Writes Full
Dispatches on
|
||||||||||||||||||||||||
| 872F | .nmi_tx_complete | |||||||||||||||||||||||
| LDA #&82 ; Jump to error handler | ||||||||||||||||||||||||
| 8731 | STA adlc_cr1 ; Write CR1 to switch from TX to RX | |||||||||||||||||||||||
| 8734 | BIT rx_src_net ; Test workspace flags | |||||||||||||||||||||||
| 8737 | BVC check_handshake_bit ; bit6 not set -- check bit0 | |||||||||||||||||||||||
| 8739 | JMP tx_result_ok ; bit6 set -- TX completion | |||||||||||||||||||||||
| 873C | .check_handshake_bit←1← 8737 BVC | |||||||||||||||||||||||
| LDA #1 ; A=1: mask for bit0 test | ||||||||||||||||||||||||
| 873E | BIT rx_src_net ; Test tx_flags bit0 (handshake) | |||||||||||||||||||||||
| 8741 | BEQ install_reply_scout ; bit0 clear: install reply handler | |||||||||||||||||||||||
| 8743 | JMP handshake_await_ack ; bit0 set -- four-way handshake data phase | |||||||||||||||||||||||
| 8746 | .install_reply_scout←1← 8741 BEQ | |||||||||||||||||||||||
| LDA #&4b ; Install nmi_reply_validate at &874B | ||||||||||||||||||||||||
| 8748 | JMP install_nmi_handler ; Install handler | |||||||||||||||||||||||
RX reply-scout handlerNMI handler installed before the reply-scout reception phase.
Tests |
|
| 874B | .nmi_reply_scout |
| LDA #1 ; A=&01: AP mask for SR2 | |
| 874D | BIT adlc_cr2 ; Test SR2 AP (Address Present) |
| 8750 | BEQ tx_error ; No AP -- error |
| 8752 | LDA adlc_tx ; Read first RX byte (destination station) |
| 8755 | CMP tx_src_stn ; Compare to our station ID (workspace copy) |
| 8758 | BNE reject_reply ; Not our station -- error/reject |
| 875A | LDA #&5f ; Install next handler at &8758 (reply continuation) |
| 875C | 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). Loads Optimisation: before installing, checks |
|
| 875F | .nmi_reply_cont |
| BIT adlc_cr2 ; Read RX byte (destination station) | |
| 8762 | BPL reject_reply ; No RDA -- error |
| 8764 | LDA adlc_tx ; Read destination network byte |
| 8767 | BNE reject_reply ; Non-zero -- network mismatch, error |
| 8769 | LDA #&76 ; A=&76: low byte of nmi_reply_validate (&8776) |
| 876B | BIT adlc_cr1 ; Test SR1 IRQ (N=bit7) -- more data ready? |
| 876E | BMI nmi_reply_validate ; IRQ set -- fall through to &8779 |
| 8770 | JMP install_nmi_handler ; IRQ not set -- install handler |
Abandon reply scout (1-instruction trampoline)Single Seven inbound refs in total (one |
|
| 8773 | .reject_reply←7← 8758 BNE← 8762 BPL← 8767 BNE← 8779 BPL← 8781 BNE← 8789 BNE← 8790 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
(
If all checks pass, the reply scout is valid and the ROM proceeds
to send the scout ACK ( |
|
| 8776 | .nmi_reply_validate←1← 876E BMI |
| BIT adlc_cr2 ; Test SR2 RDA (bit7). Must be set for valid reply. | |
| 8779 | BPL reject_reply ; No RDA -- error (FV masking RDA via PSE would cause this) |
| 877B | LDA adlc_tx ; Read source station |
| 877E | CMP tx_dst_stn ; Compare to original TX destination station (&0D20) |
| 8781 | BNE reject_reply ; Mismatch -- not the expected reply, error |
| 8783 | LDA adlc_tx ; Read source network |
| 8786 | CMP tx_dst_net ; Compare to original TX destination network (&0D21) |
| 8789 | BNE reject_reply ; Mismatch -- error |
| 878B | LDA #2 ; A=&02: FV mask for SR2 bit1 |
| 878D | BIT adlc_cr2 ; Test SR2 FV -- frame must be complete |
| 8790 | BEQ reject_reply ; No FV -- incomplete frame, error |
| 8792 | LDA #&a7 ; CR2=&A7: RTS|CLR_TX_ST|FC_TDRA|2_1_BYTE| PSE (TX in handshake) |
| 8794 | STA adlc_cr2 ; Write CR2: enable RTS for TX handshake |
| 8797 | LDA #&44 ; CR1=&44: RX_RESET | TIE (TX active for scout ACK) |
| 8799 | STA adlc_cr1 ; Write CR1: reset RX, enable TX interrupt |
| 879C | LDA #&86 ; Install handshake_await_ack into &0D43/&0D44 (four-way data phase) |
| 879E | LDY #&88 ; High byte &88 of next handler address |
| 87A0 | STA saved_nmi_lo ; Store low byte to nmi_next_lo |
| 87A3 | STY saved_nmi_hi ; Store high byte to nmi_next_hi |
| 87A6 | LDA tx_dst_stn ; Load dest station for scout ACK TX |
| 87A9 | BIT adlc_cr1 ; Test SR1 TDRA (V=bit6) |
| 87AC | BVC tx_check_tdra_ready ; TDRA not ready -- error |
| 87AE | STA adlc_tx ; Write dest station to TX FIFO |
| 87B1 | LDA tx_dst_net ; Write dest network to TX FIFO |
| 87B4 | STA adlc_tx ; Write dest network to TX FIFO |
| 87B7 | LDA #&be ; Install handler at &87B7 (write src addr for scout ACK) |
| 87B9 | LDY #&87 ; High byte &87 of handler address |
| 87BB | 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
the workspace copy Then dispatches on bit 1 of
Installs the chosen handler via
|
|||||||
| 87BE | .nmi_scout_ack_src | ||||||
| LDA tx_src_stn ; Load our station ID from workspace copy | |||||||
| 87C1 | BIT adlc_cr1 ; Test SR1 TDRA | ||||||
| 87C4 | .tx_check_tdra_ready←1← 87AC BVC | ||||||
| BVC data_tx_check_fifo ; TDRA not ready -- error | |||||||
| 87C6 | STA adlc_tx ; Write our station to TX FIFO | ||||||
| 87C9 | LDA #0 ; Write network=0 to TX FIFO | ||||||
| 87CB | STA adlc_tx ; Write network byte to TX FIFO | ||||||
| fall through ↓ | |||||||
Begin data-frame TX: install nmi_data_tx or altTests bit 1 of
Single caller ( |
|||||||
| 87CE | .data_tx_begin←1← 8339 JMP | ||||||
| LDA #2 ; Test bit 1 of tx_flags | |||||||
| 87D0 | BIT rx_src_net ; Check if immediate-op or data-transfer | ||||||
| 87D3 | BNE install_imm_data_nmi ; Bit 1 set: immediate op, use alt handler | ||||||
| 87D5 | LDA #&eb ; A=&EB: low byte of nmi_data_tx alt-entry (&87EB) | ||||||
| 87D7 | LDY #&87 ; Y=&87: high byte of nmi_data_tx | ||||||
| 87D9 | JMP set_nmi_vector ; Install and return via set_nmi_vector | ||||||
| 87DC | .install_imm_data_nmi←1← 87D3 BNE | ||||||
| LDA #&45 ; Install nmi_imm_data at &8837 | |||||||
| 87DE | LDY #&88 ; High byte of handler address | ||||||
| 87E0 | JMP set_nmi_vector ; Install and return via set_nmi_vector | ||||||
TX data phase: send payloadNMI handler that transmits the data payload of a four-way
handshake. Loads bytes from
The alt-entry at |
|||||||||
| 87E3 | .nmi_data_tx←1← 87ED BEQ | ||||||||
| LDY port_buf_len_hi ; Y = buffer offset, resume from last position | |||||||||
| 87E5 | BEQ data_tx_last ; No pages left: send final partial page | ||||||||
| 87E7 | LDY port_buf_len ; Load remaining byte count | ||||||||
| 87E9 | BEQ check_tdra_status ; Zero bytes left: skip to TDRA check | ||||||||
| 87EB | LDY port_buf_len ; Load remaining byte count (alt entry) | ||||||||
| 87ED | BEQ nmi_data_tx ; Zero: loop back to top of handler | ||||||||
| 87EF | .check_tdra_status←1← 87E9 BEQ | ||||||||
| BIT adlc_cr1 ; Test SR1 TDRA (V=bit6) | |||||||||
| 87F2 | .data_tx_check_fifo←2← 87C4 BVC← 8822 BMI | ||||||||
| BVC tube_tx_fifo_write ; TDRA not ready -- error | |||||||||
| ; 4.21 Master 128: save/restore ACCCON across the ; (open_port_buf),Y reads | |||||||||
| ; in this TX FIFO loop. Same idiom as copy_scout_to_buffer / ; nmi_data_rx_bulk; | |||||||||
| ; workspace &97 holds the desired ACCCON value pre-loaded by the ; caller. | |||||||||
| 87F4 | LDA acccon ; Save current ACCCON on stack (Master 128) | ||||||||
| 87F7 | PHA ; Push ACCCON snapshot | ||||||||
| 87F8 | LDA escapable ; Load desired ACCCON from workspace &97 | ||||||||
| 87FA | STA acccon ; Set ACCCON for the upcoming buffer reads | ||||||||
| 87FD | LDA (open_port_buf),y ; Write data byte to TX FIFO | ||||||||
| 87FF | STA adlc_tx ; Write first byte of pair to FIFO | ||||||||
| 8802 | INY ; Advance buffer offset | ||||||||
| 8803 | BNE write_second_tx_byte ; No page crossing | ||||||||
| 8805 | DEC port_buf_len_hi ; Page crossing: decrement page count | ||||||||
| 8807 | BEQ frame_end_restore ; No pages left: send last data | ||||||||
| 8809 | INC open_port_buf_hi ; Increment buffer high byte | ||||||||
| 880B | .write_second_tx_byte←1← 8803 BNE | ||||||||
| LDA (open_port_buf),y ; Load second byte of pair | |||||||||
| 880D | STA adlc_tx ; Write second byte to FIFO | ||||||||
| 8810 | INY ; Advance buffer offset | ||||||||
| 8811 | STY port_buf_len ; Save updated buffer position | ||||||||
| 8813 | BNE check_fifo_loop ; No page crossing | ||||||||
| 8815 | DEC port_buf_len_hi ; Page crossing: decrement page count | ||||||||
| 8817 | BEQ frame_end_restore ; No pages left: send last data | ||||||||
| 8819 | INC open_port_buf_hi ; Increment buffer high byte | ||||||||
| 881B | .check_fifo_loop←1← 8813 BNE | ||||||||
| PLA ; Pull saved ACCCON from stack | |||||||||
| 881C | STA acccon ; Restore caller's ACCCON between byte pairs | ||||||||
| 881F | .check_irq_loop | ||||||||
| BIT adlc_cr1 ; Test ADLC SR1 IRQ flag for next byte pair | |||||||||
| 8822 | BMI data_tx_check_fifo ; IRQ still set: more bytes to send | ||||||||
| 8824 | JMP nmi_rti ; IRQ cleared: return from NMI | ||||||||
| 8827 | .frame_end_restore←2← 8807 BEQ← 8817 BEQ | ||||||||
| PLA ; Pull saved ACCCON (frame-end path) | |||||||||
| 8828 | STA acccon ; Restore caller's ACCCON before TX_LAST_DATA | ||||||||
| 882B | .data_tx_last←3← 87E5 BEQ← 885E BEQ← 8874 BEQ | ||||||||
| LDA #&3f ; CR2=&3F: TX_LAST_DATA (close data frame) | |||||||||
| 882D | STA adlc_cr2 ; Write CR2 to close frame | ||||||||
| 8830 | LDA rx_src_net ; Check tx_flags for next action | ||||||||
| 8833 | BPL install_saved_handler ; Bit7 clear: error, install saved handler | ||||||||
| 8835 | LDA #&e5 ; Install discard_reset_listen at &83F2 | ||||||||
| 8837 | LDY #&83 ; High byte of &83F2 handler | ||||||||
| 8839 | JMP set_nmi_vector ; Set NMI vector and return | ||||||||
| 883C | .install_saved_handler←1← 8833 BPL | ||||||||
| LDA saved_nmi_lo ; Load saved next handler low byte | |||||||||
| 883F | LDY saved_nmi_hi ; Load saved next handler high byte | ||||||||
| 8842 | JMP set_nmi_vector ; Install saved handler and return | ||||||||
NMI handler: TX FIFO write from Tube bufferNMI continuation handler used during TX of a Tube-sourced data
frame. Tests SR1 TDRA via |
|
| 8845 | .nmi_data_tx_tube |
| BIT adlc_cr1 ; Tube TX: test SR1 TDRA | |
| 8848 | .tube_tx_fifo_write←2← 87F2 BVC← 8879 BMI |
| BVC tx_tdra_error ; TDRA not ready -- error | |
| 884A | LDA tube_data_register_3 ; Read byte from Tube R3 |
| 884D | STA adlc_tx ; Write to TX FIFO |
| 8850 | INC port_buf_len ; Increment 4-byte buffer counter |
| 8852 | BNE write_second_tube_byte ; Low byte didn't wrap |
| 8854 | INC port_buf_len_hi ; Carry into second byte |
| 8856 | BNE write_second_tube_byte ; No further carry |
| 8858 | INC open_port_buf ; Carry into third byte |
| 885A | BNE write_second_tube_byte ; No further carry |
| 885C | INC open_port_buf_hi ; Carry into fourth byte |
| 885E | BEQ data_tx_last ; Counter wrapped to zero: last data |
| 8860 | .write_second_tube_byte←3← 8852 BNE← 8856 BNE← 885A BNE |
| LDA tube_data_register_3 ; Read second Tube byte from R3 | |
| 8863 | STA adlc_tx ; Write second byte to TX FIFO |
| 8866 | INC port_buf_len ; Increment 4-byte counter (second byte) |
| 8868 | BNE check_tube_irq_loop ; Low byte didn't wrap |
| 886A | .tube_tx_inc_byte2 |
| INC port_buf_len_hi ; Carry into second byte | |
| 886C | BNE check_tube_irq_loop ; No further carry |
| 886E | .tube_tx_inc_byte3 |
| INC open_port_buf ; Carry into third byte | |
| 8870 | BNE check_tube_irq_loop ; No further carry |
| 8872 | .tube_tx_inc_byte4 |
| INC open_port_buf_hi ; Carry into fourth byte | |
| 8874 | BEQ data_tx_last ; Counter wrapped to zero: last data |
| 8876 | .check_tube_irq_loop←3← 8868 BNE← 886C BNE← 8870 BNE |
| BIT adlc_cr1 ; Test SR1 IRQ for tight loop | |
| 8879 | BMI tube_tx_fifo_write ; IRQ still set: write 2 more bytes |
| 887B | JMP nmi_rti ; No IRQ: return, wait for next NMI |
| 887E | .tx_tdra_error←1← 8848 BVC |
| LDA rx_src_net ; TX error: check flags for path | |
| 8881 | BPL tx_result_fail ; Bit7 clear: TX result = not listening |
| 8883 | JMP discard_reset_rx ; Bit7 set: discard and return to listen |
Four-way handshake: switch to RX for final ACKCalled via JMP from |
|
| 8886 | .handshake_await_ack←1← 8743 JMP |
| LDA #&82 ; CR1=&82: TX_RESET | RIE (switch to RX for final ACK) | |
| 8888 | STA adlc_cr1 ; Write to ADLC CR1 |
| 888B | LDA #&92 ; Install nmi_final_ack handler |
| 888D | LDY #&88 ; High byte of handler address |
| 888F | 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
On success, stores result=0 via
|
|
| 8892 | .nmi_final_ack |
| LDA #1 ; A=&01: AP mask | |
| 8894 | BIT adlc_cr2 ; Test SR2 AP |
| 8897 | BEQ tx_result_fail ; No AP -- error |
| 8899 | LDA adlc_tx ; Read dest station |
| 889C | CMP tx_src_stn ; Compare to our station (workspace copy) |
| 889F | BNE tx_result_fail ; Not our station -- error |
| 88A1 | LDA #&a6 ; A=&A6: low byte of nmi_final_ack_net (&88A6) |
| 88A3 | JMP install_nmi_handler ; Install continuation handler |
NMI handler: final-ACK source-net validationNMI continuation entry installed by
|
||||
| 88A6 | .nmi_final_ack_net | |||
| BIT adlc_cr2 ; Test SR2 RDA | ||||
| 88A9 | BPL tx_result_fail ; No RDA -- error | |||
| 88AB | LDA adlc_tx ; Read dest network | |||
| 88AE | BNE tx_result_fail ; Non-zero -- network mismatch, error | |||
| 88B0 | LDA #&ba ; Install nmi_final_ack_validate handler | |||
| 88B2 | BIT adlc_cr1 ; Test SR1 IRQ -- more data ready? | |||
| 88B5 | BMI nmi_final_ack_validate ; IRQ set -- fall through to validate | |||
| 88B7 | JMP install_nmi_handler ; Install handler | |||
Final ACK validationContinuation of Any mismatch or missing |
|
| 88BA | .nmi_final_ack_validate←1← 88B5 BMI |
| BIT adlc_cr2 ; Test SR2 RDA | |
| 88BD | BPL tx_result_fail ; No RDA -- error |
| 88BF | LDA adlc_tx ; Read source station |
| 88C2 | CMP tx_dst_stn ; Compare to TX dest station (&0D20) |
| 88C5 | BNE tx_result_fail ; Mismatch -- error |
| 88C7 | LDA adlc_tx ; Read source network |
| 88CA | CMP tx_dst_net ; Compare to TX dest network (&0D21) |
| 88CD | BNE tx_result_fail ; Mismatch -- error |
| 88CF | LDA rx_src_net ; Load TX flags for next action |
| 88D2 | BPL check_fv_final_ack ; bit7 clear: no data phase |
| 88D4 | JMP install_data_rx_handler ; Install data RX handler |
| 88D7 | .check_fv_final_ack←1← 88D2 BPL |
| LDA #2 ; A=&02: FV mask for SR2 bit1 | |
| 88D9 | BIT adlc_cr2 ; Test SR2 FV -- frame must be complete |
| 88DC | BEQ tx_result_fail ; No FV -- error |
| fall through ↓ | |
TX completion handlerLoads
|
||||
| 88DE | .tx_result_ok←2← 82E7 JMP← 8739 JMP | |||
| LDA #0 ; A=0: success result code | ||||
| 88E0 | BEQ tx_store_result ; Always taken (A=0) | |||
| fall through ↓ | ||||
TX failure: not listeningLoads error code
|
||||
| 88E2 | .tx_result_fail←11← 821A JMP← 8773 JMP← 8881 BPL← 8897 BEQ← 889F BNE← 88A9 BPL← 88AE BNE← 88BD BPL← 88C5 BNE← 88CD BNE← 88DC BEQ | |||
| LDA #&41 ; A=&41: not listening error code | ||||
| fall through ↓ | ||||
TX result store and completionStores the TX result code (in
|
||||
| 88E4 | .tx_store_result←2← 8720 JMP← 88E0 BEQ | |||
| LDY #0 ; Y=0: index into TX control block | ||||
| 88E6 | STA (nmi_tx_block),y ; Store result/error code at (nmi_tx_block),0 | |||
| 88E8 | LDA #&80 ; A=&80: TX-complete signal for tx_complete_flag | |||
| 88EA | STA tx_complete_flag ; Signal TX complete | |||
| 88ED | JMP discard_reset_rx ; Full ADLC reset and return to idle listen | |||
Purpose unknown. Unreferenced, unreachable. |
|
| 88F0 | .rom_gap_88f0 |
| EQUB &0E, &0E, &0A, &0A, &0A, &06, &06, &0A, &81, &00, &00, &00, &00, &01, &01, &81 | |
Calculate transfer size and reclaim Tube bufferInspects
Three callers:
|
|||||||||||||||
| 8900 | .tx_calc_transfer←3← 819A JSR← 84DB JSR← 86DD JSR | ||||||||||||||
| LDA acccon ; Read ACCCON (Master 128 access-control register) | |||||||||||||||
| 8903 | ORA #8 ; Set bit 3 of A (transfer-mode flag) | ||||||||||||||
| 8905 | STA escapable ; Store as escapable mode | ||||||||||||||
| 8907 | LDY #7 ; Y=7: scout-bytes counter | ||||||||||||||
| 8909 | LDA (port_ws_offset),y ; Read RXCB[7] (buffer addr high byte) | ||||||||||||||
| 890B | CMP #&ff ; Compare to &FF | ||||||||||||||
| 890D | BNE check_tx_in_progress ; Not &FF: normal buffer, skip Tube check | ||||||||||||||
| 890F | DEY ; Y=&06 | ||||||||||||||
| 8910 | LDA (port_ws_offset),y ; Read RXCB[6] (buffer addr byte 2) | ||||||||||||||
| 8912 | CMP #&fe ; Check if addr byte 2 >= &FE (Tube range) | ||||||||||||||
| 8914 | BCC check_tx_in_progress ; C clear: no Tube, plain transfer path | ||||||||||||||
| 8916 | BNE shadow_enable_flag ; Z clear (other state set): use fallback path | ||||||||||||||
| 8918 | LDA acccon ; Z set: re-read ACCCON for second decision | ||||||||||||||
| 891B | ROR ; Rotate bit 0 (E flag) into C | ||||||||||||||
| 891C | BCC shadow_enable_flag ; C clear: shadow not enabled, fallback path | ||||||||||||||
| 891E | LDA #4 ; Shadow enabled: set bit 2 of escapable | ||||||||||||||
| 8920 | TSB escapable ; Atomic bit-set on escapable | ||||||||||||||
| 8922 | .shadow_enable_flag←2← 8916 BNE← 891C BCC | ||||||||||||||
| BRA fallback_calc_transfer ; Branch to fallback_calc_transfer (always) | |||||||||||||||
| 8924 | .check_tx_in_progress←2← 890D BNE← 8914 BCC | ||||||||||||||
| LDA tube_present ; Transmit in progress? | |||||||||||||||
| 8927 | BEQ fallback_calc_transfer ; No: fallback path | ||||||||||||||
| 8929 | LDA rx_src_net ; Load TX flags for transfer setup | ||||||||||||||
| 892C | ORA #2 ; Set bit 1 (transfer complete) | ||||||||||||||
| 892E | STA rx_src_net ; Store with bit 1 set (Tube xfer) | ||||||||||||||
| 8931 | SEC ; Init borrow for 4-byte subtract | ||||||||||||||
| 8932 | PHP ; Save carry on stack | ||||||||||||||
| 8933 | LDY #4 ; Y=4: start at RXCB offset 4 | ||||||||||||||
| 8935 | .calc_transfer_size←1← 8947 BCC | ||||||||||||||
| LDA (port_ws_offset),y ; Load RXCB[Y] (current ptr byte) | |||||||||||||||
| 8937 | INY ; Y += 4: advance to RXCB[Y+4] | ||||||||||||||
| 8938 | INY ; (continued) | ||||||||||||||
| 8939 | INY ; (continued) | ||||||||||||||
| 893A | INY ; (continued) | ||||||||||||||
| 893B | PLP ; Restore borrow from previous byte | ||||||||||||||
| 893C | SBC (port_ws_offset),y ; Subtract RXCB[Y+4] (start ptr byte) | ||||||||||||||
| 893E | STA net_tx_ptr,y ; Store result byte | ||||||||||||||
| 8941 | DEY ; Y -= 3: next source byte | ||||||||||||||
| 8942 | DEY ; (continued) | ||||||||||||||
| 8943 | DEY ; (continued) | ||||||||||||||
| 8944 | PHP ; Save borrow for next byte | ||||||||||||||
| 8945 | CPY #8 ; Done all 4 bytes? | ||||||||||||||
| 8947 | BCC calc_transfer_size ; No: next byte pair | ||||||||||||||
| 8949 | PLP ; Discard final borrow | ||||||||||||||
| 894A | TXA ; Save X | ||||||||||||||
| 894B | PHA ; Save X | ||||||||||||||
| 894C | LDA #4 ; Compute address of RXCB+4 | ||||||||||||||
| 894E | CLC ; For base pointer addition | ||||||||||||||
| 894F | ADC port_ws_offset ; Add RXCB base to get RXCB+4 addr | ||||||||||||||
| 8951 | TAX ; X = low byte of RXCB+4 | ||||||||||||||
| 8952 | LDY rx_buf_offset ; Y = high byte of RXCB ptr | ||||||||||||||
| 8954 | LDA #&c2 ; Tube claim type &C2 | ||||||||||||||
| 8956 | JSR tube_addr_data_dispatch ; Claim Tube transfer address | ||||||||||||||
| 8959 | BCC restore_x_and_return ; No Tube: skip reclaim | ||||||||||||||
| 895B | LDA rx_port ; Tube: reclaim with scout status | ||||||||||||||
| 895E | JSR tube_addr_data_dispatch ; Reclaim with scout status type | ||||||||||||||
| 8961 | JSR release_tube ; Release Tube claim after reclaim | ||||||||||||||
| 8964 | SEC ; C=1: Tube address claimed | ||||||||||||||
| 8965 | .restore_x_and_return←1← 8959 BCC | ||||||||||||||
| PLA ; Restore X | |||||||||||||||
| 8966 | TAX ; Restore X from stack | ||||||||||||||
| 8967 | RTS ; Return with C = transfer status | ||||||||||||||
| 8968 | .fallback_calc_transfer←2← 8922 BRA← 8927 BEQ | ||||||||||||||
| LDY #4 ; Y=4: RXCB current pointer offset | |||||||||||||||
| 896A | LDA (port_ws_offset),y ; Load RXCB[4] (current ptr lo) | ||||||||||||||
| 896C | LDY #8 ; Y=8: RXCB start address offset | ||||||||||||||
| 896E | SEC ; Set carry for subtraction | ||||||||||||||
| 896F | SBC (port_ws_offset),y ; Subtract RXCB[8] (start ptr lo) | ||||||||||||||
| 8971 | STA port_buf_len ; Store transfer size lo | ||||||||||||||
| 8973 | LDY #5 ; Y=5: current ptr hi offset | ||||||||||||||
| 8975 | LDA (port_ws_offset),y ; Load RXCB[5] (current ptr hi) | ||||||||||||||
| 8977 | SBC #0 ; Propagate borrow only | ||||||||||||||
| 8979 | STA open_port_buf_hi ; Temp store of adjusted hi byte | ||||||||||||||
| 897B | LDY #8 ; Y=8: start address lo offset | ||||||||||||||
| 897D | LDA (port_ws_offset),y ; Copy RXCB[8] to open port buffer lo | ||||||||||||||
| 897F | STA open_port_buf ; Store to scratch (side effect) | ||||||||||||||
| 8981 | LDY #9 ; Y=9: start address hi offset | ||||||||||||||
| 8983 | LDA (port_ws_offset),y ; Load RXCB[9] | ||||||||||||||
| 8985 | SEC ; Set carry for subtraction | ||||||||||||||
| 8986 | SBC open_port_buf_hi ; Subtract adjusted hi byte | ||||||||||||||
| 8988 | STA port_buf_len_hi ; Store transfer size hi | ||||||||||||||
| 898A | SEC ; Return with C=1 | ||||||||||||||
| 898B | .nmi_shim_rom_src | ||||||||||||||
| RTS ; Return with C=1 (success) | |||||||||||||||
ADLC full resetPerforms a full ADLC hardware reset:
|
||||
| 898C | .adlc_full_reset←3← 8053 JSR← 80DF JSR← 821D JSR | |||
| LDA #&c1 ; CR1=&C1: TX_RESET | RX_RESET | AC (both sections in reset, address control set) | ||||
| 898E | STA adlc_cr1 ; Write CR1 to ADLC register 0 | |||
| 8991 | LDA #&1e ; CR4=&1E (via AC=1): 8-bit RX word length, abort extend enabled, NRZ encoding | |||
| 8993 | STA adlc_tx2 ; Write CR4 to ADLC register 3 | |||
| 8996 | LDA #0 ; CR3=&00 (via AC=1): no loop-back, no AEX, NRZ, no DTR | |||
| 8998 | STA adlc_cr2 ; Write CR3 to ADLC register 1 | |||
| fall through ↓ | ||||
Enter RX-listen modeConfigures the ADLC for passive RX-listen mode:
This is the idle state where the ADLC listens for incoming scout frames via NMI.
|
|||||||||||||||
| 899B | .adlc_rx_listen←2← 83E8 JSR← 89C7 JMP | ||||||||||||||
| LDA #&82 ; CR1=&82: TX_RESET | RIE (TX in reset, RX interrupts enabled) | |||||||||||||||
| 899D | STA adlc_cr1 ; Write to ADLC CR1 | ||||||||||||||
| 89A0 | LDA #&67 ; CR2=&67: CLR_TX_ST | CLR_RX_ST | FC_TDRA | 2_1_BYTE | PSE | ||||||||||||||
| 89A2 | STA adlc_cr2 ; Write to ADLC CR2 | ||||||||||||||
| 89A5 | RTS ; Return; ADLC now in RX listen mode | ||||||||||||||
Wait for idle NMI state and reset EconetService-13 ( When the NMI vector matches
|
|||||||
| 89A6 | .wait_idle_and_reset | ||||||
| BIT econet_init_flag ; Check if Econet has been initialised | |||||||
| 89A9 | BPL reset_enter_listen ; Not initialised: skip to RX listen | ||||||
| 89AB | .poll_nmi_idle←2← 89B0 BNE← 89B7 BNE | ||||||
| LDA nmi_jmp_lo ; Read current NMI handler low byte | |||||||
| 89AE | CMP #&9b ; Expected: &B3 (nmi_rx_scout low) | ||||||
| 89B0 | BNE poll_nmi_idle ; Not idle: spin and wait | ||||||
| 89B2 | LDA nmi_jmp_hi ; Read current NMI handler high byte | ||||||
| 89B5 | EOR #&80 ; Test if high byte = &80 (page of nmi_rx_scout) | ||||||
| 89B7 | BNE poll_nmi_idle ; Not idle: spin and wait | ||||||
| fall through ↓ | |||||||
Reset Econet flags and enter RX-listenDisables NMIs via Used during the wait-idle-and-reset path (svc &0D) to safely tear down the Econet state before another ROM can claim the NMI workspace.
|
|||||||
| 89B9 | .save_econet_state | ||||||
| BIT master_intoff ; INTOFF: disable NMIs | |||||||
| 89BC | BIT master_intoff ; INTOFF again (belt-and-braces) | ||||||
| 89BF | STA tx_complete_flag ; TX not in progress | ||||||
| 89C2 | STA econet_init_flag ; Econet not initialised | ||||||
| 89C5 | LDY #5 ; Y=5: service call workspace page | ||||||
| 89C7 | .reset_enter_listen←1← 89A9 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 shim at the start of the NFS workspace block. Unlike
the RAM shim (which uses a self-modifying Same sequence as the RAM shim:
The The 6502 /NMI is falling-edge triggered; the Econet NMI-enable
flip-flop (IC97) gates the ADLC IRQ onto /NMI. INTOFF clears the
flip-flop, forcing /NMI high; INTON sets it, allowing the ADLC
IRQ through. This creates a guaranteed high-to-low edge on /NMI
even when the ADLC IRQ is continuously asserted (e.g. when it
transitions atomically from TDRA to frame-complete without
de-asserting). Without this mechanism,
|
|
| 89CA | .nmi_bootstrap_entry |
| BIT master_intoff ; INTOFF: force /NMI high (IC97 flip-flop clear) | |
| 89CD | PHA ; Save A |
| 89CE | TYA ; Transfer Y to A |
| 89CF | PHA ; Save Y (via A) |
| 89D0 | LDA #0 ; ROM bank 0 (patched during init for actual bank) |
| 89D2 | STA romsel ; Select Econet ROM bank via ROMSEL |
| 89D5 | JMP nmi_rx_scout ; Jump to scout handler in ROM |
ROM copy of set_nmi_vector + nmi_rtiROM-resident version of the NMI-exit sequence; also the source
for the initial copy to RAM at
The INTON creates a guaranteed falling edge on /NMI if the ADLC IRQ is already asserted, ensuring the next handler fires immediately. |
|||||||
| 89D8 | .rom_set_nmi_vector | ||||||
| STY nmi_jmp_hi ; Store handler high byte at &0D0D | |||||||
| 89DB | STA nmi_jmp_lo ; Store handler low byte at &0D0C | ||||||
| 89DE | LDA romsel_copy ; Restore NFS ROM bank | ||||||
| 89E0 | STA romsel ; Page in via hardware latch | ||||||
| 89E3 | PLA ; Restore Y from stack | ||||||
| 89E4 | TAY ; Transfer ROM bank to Y | ||||||
| 89E5 | PLA ; Restore A from stack | ||||||
| 89E6 | BIT master_inton ; INTON: guaranteed /NMI edge if ADLC IRQ asserted | ||||||
| 89E9 | RTI ; Return from interrupt | ||||||
| 89EA | EQUB &05, &00, &21 | ||||||
svc_dispatch low-byte table (51 entries)Low-byte half of the Index 0 is a placeholder (target value unused – never reached); indices 1..50 cover:
Per-entry inline comments give the index and the call/reply each slot dispatches. |
|
| 89ED | .svc_dispatch_lo←1← 8E6A LDA |
| EQUB &04 ; &00: placeholder (never reached) | |
| 89EE | EQUB <(dispatch_rts-1) ; &01: no-op (RTS only) |
| 89EF | EQUB <(svc_dispatch_idx_2-1) ; &02: workspace claim helper (CMOS bit 0) |
| 89F0 | EQUB <(svc_2_priv_ws-1) ; &03: svc &02: private workspace pages |
| 89F1 | EQUB <(svc_3_autoboot-1) ; &04: svc &03: auto-boot |
| 89F2 | EQUB <(svc_4_star_command-1) ; &05: svc &04: unrecognised *command |
| 89F3 | EQUB <(svc5_irq_check-1) ; &06: svc &05: IRQ check |
| 89F4 | EQUB <(dispatch_rts-1) ; &07: no-op (RTS only) |
| 89F5 | EQUB <(svc_7_osbyte-1) ; &08: svc &07: unrecognised OSBYTE |
| 89F6 | EQUB <(svc_8_osword_disp-1) ; &09: svc &08: OSWORD dispatch |
| 89F7 | EQUB <(svc_9_help-1) ; &0A: svc &09: *HELP |
| 89F8 | EQUB <(dispatch_rts-1) ; &0B: no-op (RTS only) |
| 89F9 | EQUB <(econet_restore-1) ; &0C: svc &0B: NMI release |
| 89FA | EQUB <(wait_idle_and_reset-1) ; &0D: svc &0D: wait idle and reset |
| 89FB | EQUB <(svc_18_fs_select-1) ; &0E: svc &12: FS select |
| 89FC | EQUB <(match_on_suffix-1) ; &0F: svc &18: interactive HELP 'ON ' matcher |
| 89FD | EQUB <(raise_y_to_c8-1) ; &10: svc &21: static workspace claim |
| 89FE | EQUB <(set_rom_ws_page-1) ; &11: svc &22: dynamic workspace offer |
| 89FF | EQUB <(store_ws_page_count-1) ; &12: svc &23: top-of-static-workspace |
| 8A00 | EQUB <(noop_dey_rts-1) ; &13: svc &24: dynamic workspace claim |
| 8A01 | EQUB <(copy_template_to_zp-1) ; &14: svc &25: FS name + info reply |
| 8A02 | EQUB <(svc_26_close_all_files-1) ; &15: svc &26: close all files |
| 8A03 | EQUB <(nfs_init_body-1) ; &16: svc &27: post-hard-reset re-init |
| 8A04 | EQUB <(print_fs_ps_help-1) ; &17: svc &28: print *FS/*PS no-arg syntax help |
| 8A05 | EQUB <(svc_29_status-1) ; &18: svc &29: *STATUS handler |
| 8A06 | EQUB <(lang_0_insert_key-1) ; &19: language reply 0: insert remote key |
| 8A07 | EQUB <(lang_1_remote_boot-1) ; &1A: language reply 1: remote boot |
| 8A08 | EQUB <(lang_2_save_palette_vdu-1) ; &1B: language reply 2: save palette/VDU |
| 8A09 | EQUB <(lang_3_exec_0100-1) ; &1C: language reply 3: execute at &0100 |
| 8A0A | EQUB <(lang_4_validated-1) ; &1D: language reply 4: remote validated |
| 8A0B | EQUB <(fscv_0_opt_entry-1) ; &1E: FSCV 0: *OPT |
| 8A0C | EQUB <(fscv_1_eof-1) ; &1F: FSCV 1: EOF |
| 8A0D | EQUB <(cmd_run_via_urd-1) ; &20: FSCV 2: *RUN |
| 8A0E | EQUB <(fscv_3_star_cmd-1) ; &21: FSCV 3: *command |
| 8A0F | EQUB <(cmd_run_via_urd-1) ; &22: FSCV 4: *RUN (alias) |
| 8A10 | EQUB <(fscv_5_cat-1) ; &23: FSCV 5: *CAT |
| 8A11 | EQUB <(fscv_6_shutdown-1) ; &24: FSCV 6: shutdown |
| 8A12 | EQUB <(fscv_7_read_handles-1) ; &25: FSCV 7: read handles |
| 8A13 | EQUB <(dispatch_rts-1) ; &26: no-op (RTS only) |
| 8A14 | EQUB <(ps_scan_resume-1) ; &27: PS scan tail (after pop_requeue) |
| 8A15 | EQUB <(cmd_info_dispatch-1) ; &28: *Info dispatch |
| 8A16 | EQUB <(check_urd_present-1) ; &29: URD-present check |
| 8A17 | EQUB <(ex_init_scan_x0-1) ; &2A: *Ex scan init |
| 8A18 | EQUB <(fsreply_1_boot-1) ; &2B: FS reply 1: copy handles + boot |
| 8A19 | EQUB <(fsreply_2_copy_handles-1) ; &2C: FS reply 2: copy handles |
| 8A1A | EQUB <(fsreply_3_set_csd-1) ; &2D: FS reply 3: set CSD |
| 8A1B | EQUB <(cmd_run_via_urd-1) ; &2E: FS reply 4: *RUN (alias) |
| 8A1C | EQUB <(fsreply_5_set_lib-1) ; &2F: FS reply 5: set library |
| 8A1D | EQUB <(net_1_read_handle-1) ; &30: net handle 1: read handle |
| 8A1E | EQUB <(net_2_read_entry-1) ; &31: net handle 2: read handle entry |
| 8A1F | EQUB <(net_3_close_handle-1) ; &32: net handle 3: close handle |
svc_dispatch high-byte table (51 entries + 1 padding)High-byte half of the |
|
| 8A20 | .svc_dispatch_hi←1← 8E66 LDA |
| EQUB &E9 ; &00: placeholder (never reached) | |
| 8A21 | EQUB >(dispatch_rts-1) ; &01: no-op (RTS only) |
| 8A22 | EQUB >(svc_dispatch_idx_2-1) ; &02: workspace claim helper (CMOS bit 0) |
| 8A23 | EQUB >(svc_2_priv_ws-1) ; &03: svc &02: private workspace pages |
| 8A24 | EQUB >(svc_3_autoboot-1) ; &04: svc &03: auto-boot |
| 8A25 | EQUB >(svc_4_star_command-1) ; &05: svc &04: unrecognised *command |
| 8A26 | EQUB >(svc5_irq_check-1) ; &06: svc &05: IRQ check |
| 8A27 | EQUB >(dispatch_rts-1) ; &07: no-op (RTS only) |
| 8A28 | EQUB >(svc_7_osbyte-1) ; &08: svc &07: unrecognised OSBYTE |
| 8A29 | EQUB >(svc_8_osword_disp-1) ; &09: svc &08: OSWORD dispatch |
| 8A2A | EQUB >(svc_9_help-1) ; &0A: svc &09: *HELP |
| 8A2B | EQUB >(dispatch_rts-1) ; &0B: no-op (RTS only) |
| 8A2C | EQUB >(econet_restore-1) ; &0C: svc &0B: NMI release |
| 8A2D | EQUB >(wait_idle_and_reset-1) ; &0D: svc &0D: wait idle and reset |
| 8A2E | EQUB >(svc_18_fs_select-1) ; &0E: svc &12: FS select |
| 8A2F | EQUB >(match_on_suffix-1) ; &0F: svc &18: interactive HELP 'ON ' matcher |
| 8A30 | EQUB >(raise_y_to_c8-1) ; &10: svc &21: static workspace claim |
| 8A31 | EQUB >(set_rom_ws_page-1) ; &11: svc &22: dynamic workspace offer |
| 8A32 | EQUB >(store_ws_page_count-1) ; &12: svc &23: top-of-static-workspace |
| 8A33 | EQUB >(noop_dey_rts-1) ; &13: svc &24: dynamic workspace claim |
| 8A34 | EQUB >(copy_template_to_zp-1) ; &14: svc &25: FS name + info reply |
| 8A35 | EQUB >(svc_26_close_all_files-1) ; &15: svc &26: close all files |
| 8A36 | EQUB >(nfs_init_body-1) ; &16: svc &27: post-hard-reset re-init |
| 8A37 | EQUB >(print_fs_ps_help-1) ; &17: svc &28: print *FS/*PS no-arg syntax help |
| 8A38 | EQUB >(svc_29_status-1) ; &18: svc &29: *STATUS handler |
| 8A39 | EQUB >(lang_0_insert_key-1) ; &19: language reply 0: insert remote key |
| 8A3A | EQUB >(lang_1_remote_boot-1) ; &1A: language reply 1: remote boot |
| 8A3B | EQUB >(lang_2_save_palette_vdu-1) ; &1B: language reply 2: save palette/VDU |
| 8A3C | EQUB >(lang_3_exec_0100-1) ; &1C: language reply 3: execute at &0100 |
| 8A3D | EQUB >(lang_4_validated-1) ; &1D: language reply 4: remote validated |
| 8A3E | EQUB >(fscv_0_opt_entry-1) ; &1E: FSCV 0: *OPT |
| 8A3F | EQUB >(fscv_1_eof-1) ; &1F: FSCV 1: EOF |
| 8A40 | EQUB >(cmd_run_via_urd-1) ; &20: FSCV 2: *RUN |
| 8A41 | EQUB >(fscv_3_star_cmd-1) ; &21: FSCV 3: *command |
| 8A42 | EQUB >(cmd_run_via_urd-1) ; &22: FSCV 4: *RUN (alias) |
| 8A43 | EQUB >(fscv_5_cat-1) ; &23: FSCV 5: *CAT |
| 8A44 | EQUB >(fscv_6_shutdown-1) ; &24: FSCV 6: shutdown |
| 8A45 | EQUB >(fscv_7_read_handles-1) ; &25: FSCV 7: read handles |
| 8A46 | EQUB >(dispatch_rts-1) ; &26: no-op (RTS only) |
| 8A47 | EQUB >(ps_scan_resume-1) ; &27: PS scan tail (after pop_requeue) |
| 8A48 | EQUB >(cmd_info_dispatch-1) ; &28: *Info dispatch |
| 8A49 | EQUB >(check_urd_present-1) ; &29: URD-present check |
| 8A4A | EQUB >(ex_init_scan_x0-1) ; &2A: *Ex scan init |
| 8A4B | EQUB >(fsreply_1_boot-1) ; &2B: FS reply 1: copy handles + boot |
| 8A4C | EQUB >(fsreply_2_copy_handles-1) ; &2C: FS reply 2: copy handles |
| 8A4D | EQUB >(fsreply_3_set_csd-1) ; &2D: FS reply 3: set CSD |
| 8A4E | EQUB >(cmd_run_via_urd-1) ; &2E: FS reply 4: *RUN (alias) |
| 8A4F | EQUB >(fsreply_5_set_lib-1) ; &2F: FS reply 5: set library |
| 8A50 | EQUB >(net_1_read_handle-1) ; &30: net handle 1: read handle |
| 8A51 | EQUB >(net_2_read_entry-1) ; &31: net handle 2: read handle entry |
| 8A52 | EQUB >(net_3_close_handle-1) ; &32: net handle 3: close handle |
| 8A53 | EQUB &8A ; padding (table has only 51 entries) |
Service call dispatch (Master 128)Handles service calls 1, 4, 8, 9, 13, 14, and 15.
On service 15 the ROM verifies the host OS via OSBYTE 0 with the
input
Only Master 128 and Master Econet Terminal are supported. Any
other version gets a
|
||||||||||||||||||||||||||||||||||||||
| 8A54 | .service_handler←1← 8003 JMP | |||||||||||||||||||||||||||||||||||||
| PHA ; Save service call number | ||||||||||||||||||||||||||||||||||||||
| 8A55 | CMP #&0f ; Service call &0F (vectors claimed)? | |||||||||||||||||||||||||||||||||||||
| 8A57 | BNE restore_rom_slot_entry ; No: skip vectors-claimed handling | |||||||||||||||||||||||||||||||||||||
| 8A59 | PHY ; Save Y on stack across the version-check | |||||||||||||||||||||||||||||||||||||
| 8A5A | LDA #osbyte_read_os_version ; OSBYTE 0: read OS version | |||||||||||||||||||||||||||||||||||||
| 8A5C | LDX #1 ; X=1 to request version number | |||||||||||||||||||||||||||||||||||||
| 8A5E | JSR osbyte ; Read OS version number into X | |||||||||||||||||||||||||||||||||||||
| 8A61 | CPX #3 ; OS 3.2/3.5 (Master 128)? | |||||||||||||||||||||||||||||||||||||
| 8A63 | BEQ restore_rom_slot ; Yes: target OS, skip Bad ROM message | |||||||||||||||||||||||||||||||||||||
| 8A65 | CPX #4 ; OS 4.0 (Master Econet Terminal)? | |||||||||||||||||||||||||||||||||||||
| 8A67 | BEQ restore_rom_slot ; Yes: target OS, skip Bad ROM message | |||||||||||||||||||||||||||||||||||||
| 8A69 | TXA ; Transfer OS version to A | |||||||||||||||||||||||||||||||||||||
| 8A6A | PHP ; Save flags (Z set if OS 1.00) across print | |||||||||||||||||||||||||||||||||||||
| 8A6B | JSR print_inline ; Print 'CR>Bad ROM ' to mark non-Master OS | |||||||||||||||||||||||||||||||||||||
| 8A6E | EQUS ".Bad ROM " ; svc 13 fail path | |||||||||||||||||||||||||||||||||||||
| 8A77 | LDA romsel_copy ; Load this ROM's slot number | |||||||||||||||||||||||||||||||||||||
| 8A79 | JSR print_num_no_leading ; Print slot number as decimal | |||||||||||||||||||||||||||||||||||||
| 8A7C | JSR print_newline_no_spool ; Print trailing newline, bypassing *SPOOL | |||||||||||||||||||||||||||||||||||||
| 8A7F | LDX romsel_copy ; Reload ROM slot for workspace clearing | |||||||||||||||||||||||||||||||||||||
| 8A81 | PLP ; Restore flags | |||||||||||||||||||||||||||||||||||||
| 8A82 | BEQ clear_workspace_byte ; OS 1.00: skip INX (table starts at slot 0) | |||||||||||||||||||||||||||||||||||||
| 8A84 | INX ; Adjust index for OS 1.20/2.00/5.00 layout | |||||||||||||||||||||||||||||||||||||
| 8A85 | .clear_workspace_byte←1← 8A82 BEQ | |||||||||||||||||||||||||||||||||||||
| LDA #0 ; A=0 | ||||||||||||||||||||||||||||||||||||||
| 8A87 | STA rom_type_table,x ; Clear workspace byte for this ROM | |||||||||||||||||||||||||||||||||||||
| 8A8A | .restore_rom_slot←2← 8A63 BEQ← 8A67 BEQ | |||||||||||||||||||||||||||||||||||||
| LDX romsel_copy ; Restore ROM slot to X | ||||||||||||||||||||||||||||||||||||||
| 8A8C | PLY ; Restore Y from stack | |||||||||||||||||||||||||||||||||||||
| 8A8D | .restore_rom_slot_entry←1← 8A57 BNE | |||||||||||||||||||||||||||||||||||||
| PLA ; Pop service call number into A | ||||||||||||||||||||||||||||||||||||||
| 8A8E | PHA ; Re-save service call number | |||||||||||||||||||||||||||||||||||||
| 8A8F | CMP #&24 ; Service call &24 (Dynamic Workspace requirements)? | |||||||||||||||||||||||||||||||||||||
| 8A91 | BNE check_adlc_flag ; No: skip ADLC check | |||||||||||||||||||||||||||||||||||||
| 8A93 | LDA adlc_cr1 ; Read ADLC status register 1 | |||||||||||||||||||||||||||||||||||||
| 8A96 | AND #&10 ; Mask relevant status bits | |||||||||||||||||||||||||||||||||||||
| 8A98 | BNE check_adlc_flag ; Non-zero: ADLC absent, set flag | |||||||||||||||||||||||||||||||||||||
| 8A9A | .set_adlc_absent | |||||||||||||||||||||||||||||||||||||
| ROL rom_ws_pages,x ; Shift bit 7 into carry | ||||||||||||||||||||||||||||||||||||||
| 8A9D | SEC ; Set carry to mark ADLC absent | |||||||||||||||||||||||||||||||||||||
| 8A9E | ROR rom_ws_pages,x ; Rotate carry into bit 7 of slot flag | |||||||||||||||||||||||||||||||||||||
| 8AA1 | .check_adlc_flag←2← 8A91 BNE← 8A98 BNE | |||||||||||||||||||||||||||||||||||||
| LDA rom_ws_pages,x ; Load ROM slot flag byte | ||||||||||||||||||||||||||||||||||||||
| 8AA4 | ASL ; Shift bit 7 (ADLC absent) into carry | |||||||||||||||||||||||||||||||||||||
| 8AA5 | PLA ; Restore service call number | |||||||||||||||||||||||||||||||||||||
| 8AA6 | BCC dispatch_svc_with_state ; ADLC present: continue dispatch | |||||||||||||||||||||||||||||||||||||
| 8AA8 | RTS ; ADLC absent: decline service, return | |||||||||||||||||||||||||||||||||||||
| 8AA9 | .dispatch_svc_with_state←1← 8AA6 BCC | |||||||||||||||||||||||||||||||||||||
| TAX ; Transfer service number to X | ||||||||||||||||||||||||||||||||||||||
| 8AAA | LDA svc_state ; Save current service state | |||||||||||||||||||||||||||||||||||||
| 8AAC | PHA ; Push old state | |||||||||||||||||||||||||||||||||||||
| 8AAD | TXA ; Restore service number to A | |||||||||||||||||||||||||||||||||||||
| 8AAE | STA svc_state ; Store as current service state | |||||||||||||||||||||||||||||||||||||
| 8AB0 | CMP #&0d ; Service < 13? | |||||||||||||||||||||||||||||||||||||
| 8AB2 | BCC dispatch_svc_index ; Yes: use as dispatch index directly | |||||||||||||||||||||||||||||||||||||
| 8AB4 | SBC #5 ; Subtract 5 (map 13-17 to 8-12) | |||||||||||||||||||||||||||||||||||||
| 8AB6 | CMP #&0d ; Mapped value = 13? (original was 18) | |||||||||||||||||||||||||||||||||||||
| 8AB8 | BEQ dispatch_svc_index ; Yes: valid service 18 (FS select) | |||||||||||||||||||||||||||||||||||||
| 8ABA | BCC dispatch_svc_state_check ; C clear: service number was below the prior CMP threshold, take dispatch fall-through | |||||||||||||||||||||||||||||||||||||
| 8ABC | SBC #5 ; Subtract 5 to remap service range | |||||||||||||||||||||||||||||||||||||
| 8ABE | CMP #&0e ; Compare with &0E | |||||||||||||||||||||||||||||||||||||
| 8AC0 | BEQ dispatch_svc_index ; Equal: dispatch directly | |||||||||||||||||||||||||||||||||||||
| 8AC2 | BCC dispatch_svc_state_check ; Below: take dispatch fall-through | |||||||||||||||||||||||||||||||||||||
| 8AC4 | SBC #8 ; Subtract 8 to remap further | |||||||||||||||||||||||||||||||||||||
| 8AC6 | CMP #&0f ; Compare with &0F | |||||||||||||||||||||||||||||||||||||
| 8AC8 | BCC dispatch_svc_state_check ; Below: dispatch fall-through | |||||||||||||||||||||||||||||||||||||
| 8ACA | CMP #&18 ; Compare with &18 | |||||||||||||||||||||||||||||||||||||
| 8ACC | BCC dispatch_svc_index ; Below: dispatch index now in A | |||||||||||||||||||||||||||||||||||||
| 8ACE | .dispatch_svc_state_check←3← 8ABA BCC← 8AC2 BCC← 8AC8 BCC | |||||||||||||||||||||||||||||||||||||
| LDA #0 ; Unknown service: set index to 0 | ||||||||||||||||||||||||||||||||||||||
| 8AD0 | .dispatch_svc_index←4← 8AB2 BCC← 8AB8 BEQ← 8AC0 BEQ← 8ACC BCC | |||||||||||||||||||||||||||||||||||||
| TAX ; Transfer dispatch index to X | ||||||||||||||||||||||||||||||||||||||
| 8AD1 | BEQ restore_svc_state ; Index 0: unhandled service, skip | |||||||||||||||||||||||||||||||||||||
| 8AD3 | LDA ws_page ; Save current workspace page | |||||||||||||||||||||||||||||||||||||
| 8AD5 | PHA ; Push old page | |||||||||||||||||||||||||||||||||||||
| 8AD6 | STY ws_page ; Set workspace page from Y parameter | |||||||||||||||||||||||||||||||||||||
| 8AD8 | TYA ; Transfer Y to A | |||||||||||||||||||||||||||||||||||||
| 8AD9 | LDY #0 ; Y=0 for dispatch offset | |||||||||||||||||||||||||||||||||||||
| 8ADB | JSR svc_dispatch ; Dispatch to service handler via table | |||||||||||||||||||||||||||||||||||||
| 8ADE | PLA ; Restore old workspace page | |||||||||||||||||||||||||||||||||||||
| 8ADF | STA ws_page ; Store it back | |||||||||||||||||||||||||||||||||||||
| 8AE1 | .restore_svc_state←1← 8AD1 BEQ | |||||||||||||||||||||||||||||||||||||
| LDX svc_state ; Get service state (return code) | ||||||||||||||||||||||||||||||||||||||
| 8AE3 | PLA ; Restore old service state | |||||||||||||||||||||||||||||||||||||
| 8AE4 | STA svc_state ; Store it back | |||||||||||||||||||||||||||||||||||||
| 8AE6 | TXA ; Transfer return code to A | |||||||||||||||||||||||||||||||||||||
| 8AE7 | .restore_romsel_rts | |||||||||||||||||||||||||||||||||||||
| LDX romsel_copy ; Restore ROM slot to X | ||||||||||||||||||||||||||||||||||||||
| 8AE9 | 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
|
|||||||
| 8AEA | .cmd_roff | ||||||
| LDY #0 ; Offset 0 in receive block | |||||||
| 8AEC | LDA (net_rx_ptr),y ; Load remote operation flag | ||||||
| 8AEE | BEQ clear_svc_and_ws ; Zero: already off, skip to cleanup | ||||||
| 8AF0 | LDA #0 ; A=0 | ||||||
| 8AF2 | TAX ; X=&00 | ||||||
| 8AF3 | STA (net_rx_ptr),y ; Clear remote operation flag | ||||||
| 8AF5 | TAY ; Y=&00 | ||||||
| 8AF6 | LDA #osbyte_read_write_econet_keyboard_disable ; OSBYTE &C9: keyboard disable | ||||||
| 8AF8 | JSR osbyte ; Enable keyboard (for Econet) | ||||||
| 8AFB | LDA #&0a ; A=&0A: workspace init parameter | ||||||
| 8AFD | JSR tx_econet_abort ; Initialise workspace area | ||||||
| fall through ↓ | |||||||
Scan keyboard for remote-operation keysUses OSBYTE
|
|||||||||||
| 8B00 | .scan_remote_keys←1← 986F JSR | ||||||||||
| STX nfs_workspace ; Save X in workspace | |||||||||||
| 8B02 | LDA #&ce ; A=&CE: start of key range | ||||||||||
| 8B04 | .loop_scan_key_range←1← 8B0F BEQ | ||||||||||
| LDX nfs_workspace ; Restore X from workspace | |||||||||||
| 8B06 | LDY #&7f ; Y=&7F: OSBYTE scan parameter | ||||||||||
| 8B08 | JSR osbyte ; OSBYTE: scan keyboard | ||||||||||
| 8B0B | ADC #1 ; Advance to next key code | ||||||||||
| 8B0D | CMP #&d0 ; Reached &D0? | ||||||||||
| 8B0F | BEQ loop_scan_key_range ; No: loop back (scan &CE and &CF) | ||||||||||
| 8B11 | .clear_svc_and_ws←1← 8AEE BEQ | ||||||||||
| LDA #0 ; A=0 | |||||||||||
| 8B13 | STA svc_state ; Clear service state | ||||||||||
| 8B15 | STA nfs_workspace ; Clear workspace byte | ||||||||||
| 8B17 | RTS ; Return | ||||||||||
Save OS text pointer for later retrievalCopies
|
||||
| 8B18 | .save_text_ptr←3← 8C46 JSR← 8C71 JSR← A603 JSR | |||
| PHA ; Save A | ||||
| 8B19 | LDA os_text_ptr ; Copy OS text pointer low | |||
| 8B1B | STA fs_crc_lo ; to fs_crc_lo | |||
| 8B1D | LDA os_text_ptr_hi ; Copy OS text pointer high | |||
| 8B1F | STA fs_crc_hi ; to fs_crc_hi | |||
| 8B21 | PLA ; Restore A | |||
| 8B22 | .return_from_save_text_ptr←2← 8B47 BNE← 8B50 BMI | |||
| RTS ; Return | ||||
Select Econet network filing systemComputes a checksum over the first
|
|||||||
| 8B23 | .cmd_net_fs←2← 8B52 JSR← 900D JSR | ||||||
| JSR get_ws_page ; Get workspace page for this ROM slot | |||||||
| 8B26 | STA fs_load_addr_hi ; Store as high byte of load address | ||||||
| 8B28 | LDA #0 ; A=0 | ||||||
| 8B2A | STA fs_load_addr ; Clear low byte of load address | ||||||
| 8B2C | CLC ; Clear carry for addition | ||||||
| 8B2D | LDY #&76 ; Y=&76: checksum range end | ||||||
| 8B2F | .loop_sum_rom_bytes←1← 8B32 BPL | ||||||
| ADC (fs_load_addr),y ; Add byte to running checksum | |||||||
| 8B31 | DEY ; Decrement index | ||||||
| 8B32 | BPL loop_sum_rom_bytes ; Loop until all bytes summed | ||||||
| 8B34 | LDY #&77 ; Y=&77: checksum storage offset | ||||||
| 8B36 | EOR (fs_load_addr),y ; Compare with stored checksum | ||||||
| 8B38 | RTS ; Return -- last instruction of cmd_net_fs body | ||||||
| 8B39 | .cmd_net_check_hw | ||||||
| LDA #&20 ; A=&20: ADLC IRQ-status mask (CR2 bit 5) | |||||||
| 8B3B | BIT adlc_cr2 ; Read ADLC CR2/SR2 (&FEA1) | ||||||
| 8B3E | BEQ select_fs_via_cmd_net_fs ; Z set (no carrier): proceed to FS-select | ||||||
| 8B40 | LDA #3 ; A=3: 'ROM has no NFS' error code | ||||||
| 8B42 | JMP build_simple_error ; Raise via build_simple_error (never returns) | ||||||
Service 18: filing-system selection requestService-18 entry point.
|
||||||||||||
| 8B45 | .svc_18_fs_select | |||||||||||
| CPY #5 ; Service 18 carries FS number in Y; Econet is FS 5 | ||||||||||||
| 8B47 | BNE return_from_save_text_ptr ; Not us: pass the call on (RTS via shared return) | |||||||||||
| 8B49 | LDA #0 ; A=0 to claim the service | |||||||||||
| 8B4B | STA svc_state ; Clear svc_state and fall into ensure_fs_selected | |||||||||||
| fall through ↓ | ||||||||||||
Ensure ANFS is the active filing systemIf bit 7 of
|
||||
| 8B4D | .ensure_fs_selected←7← 8E8F JSR← 96B9 JSR← A9CC JSR← A9DA JSR← AAC2 JSR← AAD0 JSR← AC4C JSR | |||
| BIT fs_flags ; Test fs_flags bit 7 (ANFS active) | ||||
| 8B50 | BMI return_from_save_text_ptr ; Already active: tail-RTS via shared exit | |||
| fall through ↓ | ||||
Force ANFS selection (raise net checksum on failure)Tail-fragment of
|
|||||||
| 8B52 | .select_fs_via_cmd_net_fs←2← 8B3E BEQ← 8CDD JSR | ||||||
| JSR cmd_net_fs ; Auto-select ANFS via the *NFS handler | |||||||
| 8B55 | BEQ select_fs_cmd_net_fs ; Z=1 (A=0): selection succeeded | ||||||
| 8B57 | JMP error_net_checksum ; Otherwise raise 'net checksum' error | ||||||
| 8B5A | .select_fs_cmd_net_fs←1← 8B55 BEQ | ||||||
| LDA osword_pb_ptr_hi ; Read osword_pb_ptr_hi | |||||||
| 8B5C | PHA ; Push it | ||||||
| 8B5D | LDA osword_pb_ptr ; Read osword_pb_ptr lo | ||||||
| 8B5F | PHA ; Push it | ||||||
| 8B60 | .done_rom_checksum | ||||||
| JSR notify_new_fs ; Call FSCV with A=6 (new FS) | |||||||
| 8B63 | LDY #9 ; Y=9: end of FS context block | ||||||
| 8B65 | .loop_copy_fs_ctx←1← 8B6D BNE | ||||||
| LDA (net_rx_ptr),y ; Load byte from receive block | |||||||
| 8B67 | STA hazel_minus_2,y ; Store into FS workspace | ||||||
| 8B6A | DEY ; Decrement index | ||||||
| 8B6B | CPY #1 ; Reached offset 1? | ||||||
| 8B6D | BNE loop_copy_fs_ctx ; No: continue copying | ||||||
| 8B6F | ROL fs_flags ; Shift bit 7 of FS flags into carry | ||||||
| 8B72 | CLC ; Clear carry | ||||||
| 8B73 | ROR fs_flags ; Clear bit 7 of FS flags | ||||||
| 8B76 | LDY #&0d ; Y=&0D: vector table size - 1 | ||||||
| 8B78 | .loop_set_vectors←1← 8B7F BPL | ||||||
| LDA fs_vector_table,y ; Load FS vector address | |||||||
| 8B7B | STA vec_filev,y ; Store into FILEV vector table | ||||||
| 8B7E | DEY ; Decrement index | ||||||
| 8B7F | BPL loop_set_vectors ; Loop until all vectors installed | ||||||
| 8B81 | JSR init_adlc_and_vectors ; Initialise ADLC and NMI workspace | ||||||
| 8B84 | LDY #&1b ; Y=&1B: extended vector offset | ||||||
| 8B86 | LDX #7 ; X=7: two more vectors to set up | ||||||
| 8B88 | JSR write_vector_entry ; Set up extended vectors | ||||||
| 8B8B | LDA #0 ; A=0 | ||||||
| 8B8D | STA hazel_fs_pending_state ; Clear FS state byte | ||||||
| 8B90 | STA hazel_chan_attr ; Clear workspace byte | ||||||
| 8B93 | STA hazel_fs_lib_flags ; Clear workspace byte | ||||||
| 8B96 | JSR store_rx_attribute ; Clear receive attribute byte | ||||||
| 8B99 | STA hazel_fs_error_code ; Clear workspace byte | ||||||
| 8B9C | JSR setup_ws_ptr ; Set up workspace pointers | ||||||
| 8B9F | JSR init_channel_table ; Initialise FS state | ||||||
| 8BA2 | LDY #&77 ; Y=&77: workspace block size - 1 | ||||||
| 8BA4 | .loop_copy_ws_page←1← 8BAA BPL | ||||||
| LDA (fs_ws_ptr),y ; Load byte from source workspace | |||||||
| 8BA6 | STA hazel_fcb_addr_lo,y ; Store to page &10 shadow copy | ||||||
| 8BA9 | DEY ; Decrement index | ||||||
| 8BAA | BPL loop_copy_ws_page ; Loop until all bytes copied | ||||||
| 8BAC | LDA #&80 ; A=&80: FS selected flag | ||||||
| 8BAE | TSB fs_flags ; Set bit 0 of fs_flags (= NFS active) | ||||||
| 8BB1 | JSR issue_svc_15 ; Issue Master service call &0F (vector update) | ||||||
| 8BB4 | PLA ; Pop saved osword_pb_ptr lo | ||||||
| 8BB5 | STA osword_pb_ptr ; Restore osword_pb_ptr lo | ||||||
| 8BB7 | PLA ; Pop saved osword_pb_ptr hi | ||||||
| 8BB8 | STA osword_pb_ptr_hi ; Restore osword_pb_ptr hi | ||||||
| 8BBA | RTS ; Return | ||||||
*HELP NFS topic: print NFS-specific commandsLoads
|
||||
| 8BBB | .help_print_nfs_cmds←1← 8C6E JMP | |||
| LDX #&35 ; X=&35: NFS command table offset | ||||
| 8BBD | JSR print_cmd_table ; Print help for NFS commands | |||
| fall through ↓ | ||||
*HELP UTILS topic handlerSets
|
|||||||
| 8BC0 | .help_utils | ||||||
| LDX #0 ; X=0: utility command table offset | |||||||
| 8BC2 | BEQ print_cmd_table ; ALWAYS branch | ||||||
*HELP NET topic handlerSets
|
|||||||
| 8BC4 | .help_net | ||||||
| LDX #&35 ; X=&35: NFS command table offset | |||||||
| fall through ↓ | |||||||
Print *HELP command listing with optional header
Either path then falls through to
|
||||||||||||
| 8BC6 | .print_cmd_table←2← 8BBD JSR← 8BC2 BEQ | |||||||||||
| BVC print_table_newline ; V clear: take newline-only path (skip version header) | ||||||||||||
| 8BC8 | PHX ; Save X (cmd-table offset) | |||||||||||
| 8BC9 | PHY ; Save Y (text-pointer offset) | |||||||||||
| 8BCA | JSR print_version_header ; Print the version-banner header | |||||||||||
| 8BCD | PLY | |||||||||||
| 8BCE | PLX | |||||||||||
| 8BCF | CLV ; Clear overflow flag | |||||||||||
| 8BD0 | BVC print_cmd_table_loop ; ALWAYS branch | |||||||||||
| 8BD2 | .print_table_newline←1← 8BC6 BVC | |||||||||||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||||||||||
| fall through ↓ | ||||||||||||
Enumerate and print command table entriesWalks the ANFS command table from offset
The syntax descriptor byte's low 5 bits index into
|
||||||||||
| 8BD5 | .print_cmd_table_loop←2← 8BD0 BVC← 8C61 JSR | |||||||||
| TYA ; Save Y (command line offset) | ||||||||||
| 8BD6 | PHA ; Push it | |||||||||
| 8BD7 | PHP ; Save processor status | |||||||||
| fall through ↓ | ||||||||||
*HELP table walker per-entry bodyLoads
Single caller (the
|
||||||||||
| 8BD8 | .loop_next_entry←1← 8C22 JMP | |||||||||
| LDA cmd_table_fs,x ; Load byte from command table | ||||||||||
| 8BDB | BPL print_indent ; Bit 7 clear: valid entry, continue | |||||||||
| 8BDD | JMP done_print_table ; End of table: finish up | |||||||||
| 8BE0 | .print_indent←1← 8BDB BPL | |||||||||
| JSR print_inline ; Print two-space indent | ||||||||||
| 8BE3 | EQUS " " | |||||||||
| 8BE5 | LDY #9 ; Y=9: cmd_table_fs sub-table 1 offset | |||||||||
| 8BE7 | LDA cmd_table_fs,x ; Read cmd_table_fs+X (entry name byte) | |||||||||
| 8BEA | .loop_print_cmd_name←1← 8BF2 BPL | |||||||||
| JSR osasci ; Write character | ||||||||||
| 8BED | INX ; Advance table pointer | |||||||||
| 8BEE | DEY ; Decrement padding counter | |||||||||
| 8BEF | LDA cmd_table_fs,x ; Load next character | |||||||||
| 8BF2 | BPL loop_print_cmd_name ; Bit 7 clear: more chars, continue | |||||||||
| 8BF4 | .loop_pad_spaces←1← 8BFA BPL | |||||||||
| LDA #&20 ; Pad with spaces | ||||||||||
| 8BF6 | JSR osasci ; Write character 32 | |||||||||
| 8BF9 | DEY ; Decrement remaining pad count | |||||||||
| 8BFA | BPL loop_pad_spaces ; More padding needed: loop | |||||||||
| 8BFC | LDA cmd_table_fs,x ; Load syntax descriptor byte | |||||||||
| 8BFF | AND #&1f ; Mask to get syntax string index | |||||||||
| 8C01 | TAY ; Use index as Y | |||||||||
| 8C02 | LDA cmd_syntax_table,y ; Look up syntax string offset | |||||||||
| 8C05 | TAY ; Transfer offset to Y | |||||||||
| fall through ↓ | ||||||||||
Per-character body of *HELP syntax string emit
Two callers: the
|
||||||||||||
| 8C06 | .loop_print_syntax←2← 8C13 JMP← 8C19 JMP | |||||||||||
| INY ; Advance to next character | ||||||||||||
| 8C07 | LDA syn_opt_dir,y ; Load syntax string character | |||||||||||
| 8C0A | BEQ done_entry_newline ; Zero terminator: end of syntax | |||||||||||
| 8C0C | CMP #&0d ; Carriage return: line continuation | |||||||||||
| 8C0E | BNE print_syntax_char ; No: print the character | |||||||||||
| 8C10 | JSR help_wrap_if_serial ; Handle line wrap in syntax output | |||||||||||
| 8C13 | JMP loop_print_syntax ; Continue with next character | |||||||||||
| 8C16 | .print_syntax_char←1← 8C0E BNE | |||||||||||
| JSR osasci ; Write character | ||||||||||||
| 8C19 | JMP loop_print_syntax ; Continue with next character | |||||||||||
| 8C1C | .done_entry_newline←1← 8C0A BEQ | |||||||||||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||||||||||
| 8C1F | INX ; X += 3: skip syntax descriptor and address | |||||||||||
| 8C20 | INX ; (continued) | |||||||||||
| 8C21 | INX ; (continued) | |||||||||||
| 8C22 | JMP loop_next_entry ; Loop for next command | |||||||||||
Cleanup epilogue for print_cmd_tablePops the saved
|
||||||
| 8C25 | .done_print_table←1← 8BDD JMP | |||||
| PLP ; Restore processor status | ||||||
| 8C26 | PLA ; Restore Y | |||||
| 8C27 | TAY ; Transfer to Y | |||||
| 8C28 | RTS ; Return | |||||
Wrap *HELP syntax lines for serial outputChecks the output destination via
|
||||||||||||||
| 8C29 | .help_wrap_if_serial←1← 8C10 JSR | |||||||||||||
| LDA vdu_mode ; Read output stream type | ||||||||||||||
| 8C2C | BEQ return_from_help_wrap ; Stream 0 (VDU): no wrapping | |||||||||||||
| 8C2E | CMP #3 ; Stream 3 (printer)? | |||||||||||||
| 8C30 | BEQ return_from_help_wrap ; Yes: no wrapping | |||||||||||||
| 8C32 | PHY ; Save Y across OS call | |||||||||||||
| 8C33 | JSR osnewl ; Write newline (characters 10 and 13) | |||||||||||||
| 8C36 | LDY #&0b ; Y=&0B: indent width - 1 | |||||||||||||
| 8C38 | LDA #&20 ; Space character | |||||||||||||
| 8C3A | .loop_indent_spaces←1← 8C3E BPL | |||||||||||||
| JSR osasci ; Write character 32 | ||||||||||||||
| 8C3D | DEY ; Decrement indent counter | |||||||||||||
| 8C3E | BPL loop_indent_spaces ; More spaces needed: loop | |||||||||||||
| 8C40 | PLY | |||||||||||||
| 8C41 | .return_from_help_wrap←2← 8C2C BEQ← 8C30 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.
|
||||
| 8C42 | .svc_4_star_command | |||
| LDX #0 ; X=0: start of utility command table | ||||
| 8C44 | LDY ws_page ; Get command line offset | |||
| 8C46 | .svc4_dispatch_lookup←2← 95EB JMP← 968E JMP | |||
| JSR save_text_ptr ; Save text pointer to fs_crc | ||||
| 8C49 | JSR match_fs_cmd ; Try to match command in table | |||
| 8C4C | BCS svc_return_unclaimed ; No match: return to caller | |||
| 8C4E | 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 &91. 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).
|
|||||||||
| 8C51 | .svc_9_help | ||||||||
| JSR check_credits_easter_egg ; Check for credits Easter egg | |||||||||
| 8C54 | LDY ws_page ; Get command line offset | ||||||||
| 8C56 | LDA (os_text_ptr),y ; Load character at offset | ||||||||
| 8C58 | CMP #&0d ; Is it CR (bare *HELP)? | ||||||||
| 8C5A | BNE check_help_topic ; No: check for specific topic | ||||||||
| 8C5C | JSR print_version_header ; Print version string | ||||||||
| 8C5F | LDX #&91 ; X=&91: start of help command list | ||||||||
| 8C61 | JSR print_cmd_table_loop ; Print command list from table | ||||||||
| fall through ↓ | |||||||||
Restore Y and return service-call unclaimedReloads Reached from the four service-handler escape paths at
|
||||
| 8C64 | .svc_return_unclaimed←5← 8C4C BCS← 8C91 BEQ← 8CD5 BNE← 95BE JMP← 9689 JMP | |||
| LDY ws_page ; Restore Y (command line offset) | ||||
| 8C66 | RTS ; Return unclaimed | |||
| 8C67 | .check_help_topic←1← 8C5A BNE | |||
| BIT always_set_v_byte ; Test for topic match (sets flags) | ||||
| 8C6A | CMP #&2e ; Is first char '.' (abbreviation)? | |||
| 8C6C | BNE match_help_topic ; No: try topic-specific help | |||
| 8C6E | JMP help_print_nfs_cmds ; '.' found: show full command list | |||
| 8C71 | .match_help_topic←1← 8C6C BNE | |||
| JSR save_text_ptr ; Save text pointer to fs_crc | ||||
| 8C74 | .loop_dispatch_help←1← 8C8F BNE | |||
| PHP ; Save flags | ||||
| 8C75 | LDX #&91 ; X=&91: help command table start | |||
| 8C77 | JSR match_fs_cmd ; Try to match help topic in table | |||
| 8C7A | BCS skip_if_no_match ; No match: try next topic | |||
| 8C7C | PLP ; Restore flags | |||
| 8C7D | LDA #&8c ; Push return address high (&8C) | |||
| 8C7F | PHA ; Push it for RTS dispatch | |||
| 8C80 | LDA #&73 ; Push return address low (&74) | |||
| 8C82 | PHA ; Push it for RTS dispatch | |||
| 8C83 | LDA cmd_dispatch_hi_table,x ; Load dispatch address high | |||
| 8C86 | PHA ; Push dispatch high for RTS | |||
| 8C87 | LDA cmd_dispatch_lo_table,x ; Load dispatch address low | |||
| 8C8A | PHA ; Push dispatch low for RTS | |||
| 8C8B | RTS ; Dispatch via RTS (returns to &8C80) | |||
| 8C8C | .skip_if_no_match←1← 8C7A BCS | |||
| PLP ; Restore flags from before match | ||||
| 8C8D | CMP #&0d ; End of command line? | |||
| 8C8F | BNE loop_dispatch_help ; No: try matching next topic | |||
| 8C91 | BEQ svc_return_unclaimed ; ALWAYS branch | |||
Print ANFS version string and station numberUses an inline string after
|
||||
| 8C93 | .print_version_header←2← 8BCA JSR← 8C5C JSR | |||
| JSR print_inline ; Print version string via inline | ||||
| 8C96 | .version_string_cr | |||
| EQUS ".Advanced NFS 4.21." | ||||
| 8CA9 | NOP ; NOP -- bit-7 terminator + harmless resume opcode | |||
| 8CAA | JMP print_station_id ; Tail-call print_station_id to append ' Econet Station n>' (and ' No Clock' if appropriate) | |||
Read workspace page number for current ROM slotIndexes into the MOS per-ROM workspace table
|
||||||||
| 8CAD | .get_ws_page←4← 8B23 JSR← 8CBD JSR← 8F2F JSR← B3A0 JSR | |||||||
| LDY romsel_copy ; Y = current ROM slot number from MOS copy at &F4 | ||||||||
| 8CAF | LDA rom_ws_pages,y ; Load workspace page byte for this ROM slot | |||||||
| 8CB2 | TAY ; Hold a copy of the slot byte in Y while we test bit 6 | |||||||
| 8CB3 | ROL ; ROL puts pre-ROL bit 6 into the post-ROL N flag (and pre-ROL bit 7 into C) | |||||||
| 8CB4 | PHP ; Save those flags so the upcoming ROR doesn't lose N | |||||||
| 8CB5 | ROR ; ROR restores A to its original value (using the saved C) | |||||||
| 8CB6 | PLP ; Restore the ROL flags: N is now pre-ROL bit 6 | |||||||
| 8CB7 | BPL get_ws_page_loop ; Bit 6 clear: skip the OR (no ADLC-absent flag) | |||||||
| 8CB9 | ORA #&80 ; Bit 6 set: re-set bit 7 in the returned page byte (the ADLC-absent flag uses bit 7 in callers) | |||||||
| 8CBB | .get_ws_page_loop←1← 8CB7 BPL | |||||||
| TAY ; Transfer to Y | ||||||||
| 8CBC | RTS ; Return with page in A and Y | |||||||
Set up zero-page pointer to workspace pageCalls
|
||||||
| 8CBD | .setup_ws_ptr←2← 8B9C JSR← 8F4F JSR | |||||
| JSR get_ws_page ; Get workspace page for ROM slot | ||||||
| 8CC0 | STY nfs_temp ; Store page in nfs_temp | |||||
| 8CC2 | LDA #0 ; A=0 | |||||
| 8CC4 | STA fs_ws_ptr ; Clear low byte of pointer | |||||
| 8CC6 | .return_from_setup_ws_ptr←1← 8CEC 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.
|
||||||||
| 8CC7 | .svc_3_autoboot | |||||||
| LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: scan keyboard from key 16 | ||||||||
| 8CC9 | JSR osbyte ; Keyboard scan starting from key 16 | |||||||
| 8CCC | TXA ; X is key number if key is pressed, or &ff otherwise | |||||||
| 8CCD | BMI select_net_fs ; No key pressed: select Net FS | |||||||
| 8CCF | CMP #&19 ; Key &19 (N)? | |||||||
| 8CD1 | BEQ write_key_state ; Yes: write key state and boot | |||||||
| 8CD3 | EOR #&55 ; EOR with &55: maps to zero if 'N' | |||||||
| 8CD5 | BNE svc_return_unclaimed ; Not N key: return unclaimed | |||||||
| 8CD7 | .write_key_state←1← 8CD1 BEQ | |||||||
| TAY ; Y=key | ||||||||
| 8CD8 | LDA #osbyte_write_keys_pressed ; OSBYTE &78: write keys pressed | |||||||
| 8CDA | JSR osbyte ; Write current keys pressed (X and Y) | |||||||
| 8CDD | .select_net_fs←1← 8CCD BMI | |||||||
| JSR select_fs_via_cmd_net_fs ; Select NFS as current filing system | ||||||||
| 8CE0 | LDA #0 ; A=0: clear svc_state marker | |||||||
| 8CE2 | STA svc_state ; Store -> svc_state | |||||||
| 8CE4 | JSR print_station_id ; Print station number | |||||||
| 8CE7 | JSR osnewl ; Write newline (characters 10 and 13) | |||||||
| 8CEA | LDX ws_page ; Get workspace page | |||||||
| 8CEC | BNE return_from_setup_ws_ptr ; Non-zero: already initialised, return | |||||||
| 8CEE | LDA hazel_fs_lib_flags ; Load boot flags | |||||||
| 8CF1 | ORA #4 ; Set bit 2 (auto-boot in progress) | |||||||
| 8CF3 | STA hazel_fs_lib_flags ; Store updated boot flags | |||||||
| 8CF6 | LDX #&1c ; X=&1C: boot filename address low | |||||||
| 8CF8 | LDY #&8d ; Y=&8D: boot filename address high | |||||||
| 8CFA | JMP fscv_3_star_cmd ; Execute boot file | |||||||
Notify OS of filing-system selectionLoads Single caller (&8b60 inside the FS-selection sequence).
|
||||
| 8CFD | .notify_new_fs←1← 8B60 JSR | |||
| LDA #6 ; A=6: notify new filing system | ||||
| fall through ↓ | ||||
Dispatch to filing-system control vector (FSCV)Indirect Also contains
|
||||
| 8CFF | .call_fscv←1← A59E JMP | |||
| JMP (vec_fscv) ; Tail-jump via FSCV vector (filing-system change service) | ||||
Issue OSBYTE 143 service 15 (vectors-claimed) requestTail-call wrapper that loads X=&0F (service number 15) and tail-jumps to OSBYTE 143 (issue paged ROM service request), which broadcasts service 15 to all sideways ROMs. ANFS calls this from svc_2_private_workspace after claiming its workspace, to give other ROMs a chance to react.
|
||||
| 8D02 | .issue_svc_15←1← 8BB1 JSR | |||
| LDX #&0f ; X=&0F: service 15 (vectors claimed) | ||||
| 8D04 | .issue_svc_osbyte | |||
| LDA #osbyte_issue_service_request ; A=&8F: OSBYTE 'Issue paged-ROM service request' | ||||
| 8D06 | JMP osbyte ; Issue paged ROM service call | |||
svc_dispatch table[2] handlerReached only via PHA/PHA/RTS dispatch from the
|
||||
| 8D09 | .svc_dispatch_idx_2 | |||
| PHY ; Save Y on stack | ||||
| 8D0A | LDX #&11 ; X=&11: CMOS offset for Econet station-flags | |||
| 8D0C | JSR osbyte_a1 ; Read CMOS byte: result in Y | |||
| 8D0F | TYA ; A = CMOS byte | |||
| 8D10 | PLY ; Restore caller's Y | |||
| 8D11 | AND #1 ; Isolate bit 0 (page-&0B fallback flag) | |||
| 8D13 | BEQ return_1 ; Bit clear: keep caller's Y | |||
| 8D15 | CPY #&10 ; Caller's Y already >= &10? | |||
| 8D17 | BCS return_1 ; Yes: keep it | |||
| 8D19 | LDY #&10 ; Y < &10 with bit set: clamp to &10 | |||
| 8D1B | .return_1←2← 8D13 BEQ← 8D17 BCS | |||
| RTS ; Return | ||||
| 8D1C | EQUS "i .Boot" | |||
| 8D23 | EQUB &0D | |||
Easter egg: match *HELP keyword to author creditsMatches the On a full match, prints the ANFS author credits:
Each name is terminated by |
|
| 8D24 | .check_credits_easter_egg←1← 8C51 JSR |
| LDY ws_page ; Y = ws_page (workspace high page) | |
| 8D26 | LDX #5 ; X=5: start of credits keyword |
| 8D28 | .loop_match_credits←1← 8D31 BNE |
| LDA (os_text_ptr),y ; Load character from command line | |
| 8D2A | CMP credits_keyword_start,x ; Compare with credits keyword |
| 8D2D | BNE done_credits_check ; Mismatch: check if keyword complete |
| 8D2F | INY ; Advance command line pointer |
| 8D30 | INX ; Advance keyword pointer |
| 8D31 | BNE loop_match_credits ; Continue matching |
| 8D33 | .done_credits_check←1← 8D2D BNE |
| CPX #&0c ; Reached end of keyword (X=&0D)? | |
| 8D35 | BNE return_from_credits_check ; No: keyword not fully matched, return |
| 8D37 | LDX #0 ; X=0: start of credits text |
| 8D39 | .loop_emit_credits←1← 8D42 BNE |
| LDA credits_keyword_start,x ; Load character from credits string | |
| 8D3C | BEQ return_from_credits_check ; Zero terminator: done printing |
| 8D3E | JSR osasci ; Write character |
| 8D41 | INX ; Advance string pointer |
| 8D42 | BNE loop_emit_credits ; Continue printing |
| 8D44 | .return_from_credits_check←2← 8D35 BNE← 8D3C BEQ |
| RTS ; Return | |
| 8D45 | .credits_keyword_start←2← 8D2A CMP← 8D39 LDA |
| EQUB &0D ; CR | |
| 8D46 | EQUS "The authors of ANFS are;" |
| 8D5E | EQUB &0D ; CR |
| 8D5F | EQUS "B Cockburn" |
| 8D69 | EQUB &0D |
| 8D6A | EQUS "J Du" |
| 8D6E | EQUS "nn" |
| 8D70 | EQUB &0D |
| 8D71 | EQUS "B Robertson" |
| 8D7C | EQUB &0D |
| 8D7D | EQUS "J Wills" |
| 8D84 | EQUB &0D ; CR |
| 8D85 | EQUB &0D, &00 |
| 8D87 | .cmd_iam_save_ctx |
| PHY ; Save caller Y | |
| 8D88 | LDA fs_last_byte_flag ; Read fs_last_byte_flag (work_bd) |
| 8D8A | LDX fs_options ; Read fs_options (work_bb) |
| 8D8C | LDY fs_block_offset ; Read fs_block_offset (work_bc) |
| 8D8E | PHA ; Push fs_last_byte_flag for restore on return |
| 8D8F | PHX |
| 8D90 | PHY |
| fall through ↓ | |
*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.
|
||||
| 8D91 | .cmd_iam | |||
| LDA #osbyte_close_spool_exec ; OSBYTE &77: close SPOOL/EXEC | ||||
| 8D93 | STA hazel_fs_pending_state ; Store as pending operation marker | |||
| 8D96 | JSR osbyte ; Close any *SPOOL and *EXEC files | |||
| 8D99 | LDY #0 ; Y=0 | |||
| 8D9B | STY fs_work_4 ; Clear password entry flag | |||
| 8D9D | JSR process_all_fcbs ; Reset FS connection state | |||
| 8DA0 | STZ hazel_fs_pending_state ; Clear hazel_fs_pending_state (connection-attempt flag) | |||
| 8DA3 | PLY | |||
| 8DA4 | PLX | |||
| 8DA5 | PLA ; Pop and discard saved fs_last_byte_flag | |||
| fall through ↓ | ||||
Set FS transfer parameters via set_xfer_params3-byte trampoline that calls
|
|
| 8DA6 | .load_transfer_params |
| JSR set_xfer_params ; Set up transfer parameters | |
| 8DA9 | PLY |
| 8DAA | LDA (fs_options),y ; Load first option byte |
| 8DAC | JSR is_decimal_digit ; Parse station number if present |
| 8DAF | BCC cmd_pass ; Not a digit: skip to password entry |
| 8DB1 | JSR parse_addr_arg ; Parse user ID string |
| 8DB4 | BCS skip_no_fs_addr ; No user ID: go to password |
| 8DB6 | STA hazel_fs_network ; Store file server station low |
| 8DB9 | JSR clear_if_station_match ; Check and store FS network |
| 8DBC | INY ; Skip separator |
| 8DBD | JSR parse_addr_arg ; Parse next argument |
| 8DC0 | .skip_no_fs_addr←1← 8DB4 BCS |
| BEQ cmd_pass ; No FS address: skip to password | |
| 8DC2 | STA hazel_fs_station ; Store file server station high |
| 8DC5 | LDX #&ff ; X=&FF: pre-decrement for loop |
| 8DC7 | .loop_copy_logon_cmd←1← 8DCE BPL |
| INX ; Advance index | |
| 8DC8 | LDA cmd_table_nfs_iam,x ; Load logon command template byte |
| 8DCB | STA hazel_txcb_data,x ; Store into transmit buffer |
| 8DCE | BPL loop_copy_logon_cmd ; Bit 7 clear: more bytes, loop |
| 8DD0 | JSR copy_arg_validated ; Send logon with file server lookup |
| 8DD3 | 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.
|
||||
| 8DD5 | .cmd_pass←2← 8DAF BCC← 8DC0 BEQ | |||
| JSR copy_arg_to_buf_x0 ; Build FS command packet | ||||
| 8DD8 | .scan_pass_prompt←1← 8DD3 BEQ | |||
| LDY #&ff ; Y=&FF: pre-increment for loop | ||||
| 8DDA | .loop_scan_colon←1← 8DE4 BNE | |||
| INY ; Advance to next byte | ||||
| 8DDB | LDA hazel_txcb_data,y ; Load byte from reply buffer | |||
| 8DDE | CMP #&0d ; Is it CR (end of prompt)? | |||
| 8DE0 | BEQ send_pass_to_fs ; Yes: no colon found, skip to send | |||
| 8DE2 | CMP #&3a ; Is it ':' (password prompt)? | |||
| 8DE4 | BNE loop_scan_colon ; No: keep scanning | |||
| 8DE6 | JSR print_byte_no_spool ; Print byte no-spool | |||
| 8DE9 | STY fs_work_4 ; Save position of colon | |||
| 8DEB | .read_pw_char←4← 8DFB BNE← 8DFF BEQ← 8E02 BNE← 8E0E BNE | |||
| LDA #&ff ; A=&FF: mark as escapable | ||||
| 8DED | STA need_release_tube ; Set escape flag | |||
| 8DEF | JSR check_escape_and_classify ; Check for escape condition | |||
| 8DF2 | JSR osrdch ; Read a character from the current input stream | |||
| 8DF5 | CMP #&15 ; A=character read | |||
| 8DF7 | BNE check_pw_special ; Not NAK (&15): check other chars | |||
| 8DF9 | LDY fs_work_4 ; Restore colon position | |||
| 8DFB | BNE read_pw_char ; Non-zero: restart from colon | |||
| 8DFD | .loop_erase_pw←1← 8E06 BEQ | |||
| CPY fs_work_4 ; At colon position? | ||||
| 8DFF | BEQ read_pw_char ; Yes: restart password input | |||
| 8E01 | DEY ; Backspace: move back one character | |||
| 8E02 | BNE read_pw_char ; If not at start: restart input | |||
| 8E04 | .check_pw_special←1← 8DF7 BNE | |||
| CMP #&7f ; Delete key (&7F)? | ||||
| 8E06 | BEQ loop_erase_pw ; Yes: handle backspace | |||
| 8E08 | STA hazel_txcb_data,y ; Store character in password buffer | |||
| 8E0B | INY ; Advance buffer pointer | |||
| 8E0C | CMP #&0d ; Is it CR (end of password)? | |||
| 8E0E | BNE read_pw_char ; No: read another character | |||
| 8E10 | JSR print_newline_no_spool ; Print newline no-spool | |||
| 8E13 | .send_pass_to_fs←1← 8DE0 BEQ | |||
| TYA ; Transfer string length to A | ||||
| 8E14 | PHA ; Save string length | |||
| 8E15 | JSR init_txcb ; Set up transmit control block | |||
| 8E18 | JSR init_tx_ptr_for_pass ; Send to file server and get reply | |||
| 8E1B | PLX | |||
| 8E1C | INX ; Include terminator | |||
| 8E1D | LDY #0 ; Y=0 | |||
| 8E1F | BEQ send_cmd_and_dispatch ; ALWAYS branch | |||
Clear hazel_fs_network if it matches the bridge status byteCalls Called by
|
||||
| 8E21 | .clear_if_station_match←2← 8DB9 JSR← A9EC JSR | |||
| JSR init_bridge_poll ; Ensure bridge initialised; A=spool_control_flag (bridge status) | ||||
| 8E24 | EOR hazel_fs_network ; EOR with hazel_fs_network: zero result if equal | |||
| 8E27 | BNE return_from_station_match ; Different: return without clearing | |||
| 8E29 | STA hazel_fs_network ; Same: clear station byte | |||
| 8E2C | .return_from_station_match←1← 8E27 BNE | |||
| RTS ; Return | ||||
Branch to *RUN handler if first arg char is '&'Reads the first character of the parsed command text via
Single caller (the FS command-name post-match path at
|
|||||||
| 8E2D | .check_urd_prefix←1← 9597 JMP | ||||||
| LDY #0 ; Y=0: first character offset | |||||||
| 8E2F | LDA (fs_crc_lo),y ; Load first character of command text | ||||||
| 8E31 | CMP #&26 ; Is it '&' (URD prefix)? | ||||||
| 8E33 | BNE pass_send_cmd ; No: send as normal FS command | ||||||
| 8E35 | JMP cmd_run_via_urd ; Yes: route via *RUN for URD prefix handling | ||||||
| 8E38 | .pass_send_cmd←1← 8E33 BNE | ||||||
| JSR copy_arg_to_buf_x0 ; Build FS command packet | |||||||
| 8E3B | TAY ; Transfer result to Y | ||||||
| fall through ↓ | |||||||
Send FS command and dispatch the reply
Two callers: the fall-through from
|
||||||||||
| 8E3C | .send_cmd_and_dispatch←3← 8E1F BEQ← 9460 JMP← B370 JMP | |||||||||
| JSR save_net_tx_cb ; Set up command and send to FS | ||||||||||
| 8E3F | LDX hazel_txcb_network ; Load reply function code | |||||||||
| 8E42 | BEQ dispatch_rts ; Zero: no reply, return | |||||||||
| 8E44 | LDA hazel_txcb_data ; Load first reply byte | |||||||||
| 8E47 | LDY #&25 ; Y=&25: logon dispatch offset | |||||||||
| 8E49 | BNE svc_dispatch ; ALWAYS branch | |||||||||
| 8E4B | .fscv_handler | |||||||||
| JSR set_xfer_params ; Parse reply as decimal number | ||||||||||
| 8E4E | CMP #&0c ; Result >= 8? | |||||||||
| 8E50 | BCS dispatch_rts ; Yes: out of range, return | |||||||||
| 8E52 | TAX ; Transfer handle to X | |||||||||
| 8E53 | JSR mask_owner_access ; Look up in open files table | |||||||||
| 8E56 | TYA ; Transfer result to A | |||||||||
| 8E57 | LDY #&1d ; Y=&1D: handle dispatch offset | |||||||||
| 8E59 | BNE svc_dispatch ; ALWAYS branch | |||||||||
Dispatch directory operation via PHA/PHA/RTSValidates (In 4.18 the offset was
|
||||
| 8E5B | .dir_op_dispatch←1← 855D JSR | |||
| CPX #5 ; Handle >= 5? | ||||
| 8E5D | BCS dispatch_rts ; Yes: out of range, return | |||
| 8E5F | LDY #&18 ; Y=&18: settles X_final to &19..&1D (lang reply 0..4) | |||
| fall through ↓ | ||||
PHA/PHA/RTS table dispatchComputes a target index by incrementing Routine extent is &8E61-&8E70 (the
|
|||||||||
| 8E61 | .svc_dispatch←5← 8ADB JSR← 8E49 BNE← 8E59 BNE← 8E63 BPL← 8EE6 JMP | ||||||||
| INX ; Advance X to target index | |||||||||
| 8E62 | DEY ; Decrement Y offset counter | ||||||||
| 8E63 | BPL svc_dispatch ; Y still positive: continue counting | ||||||||
| 8E65 | TAY ; Y=&FF: will be ignored by caller | ||||||||
| 8E66 | LDA svc_dispatch_hi,x ; Load dispatch address high byte | ||||||||
| 8E69 | PHA ; Push high byte for RTS dispatch | ||||||||
| 8E6A | .push_dispatch_lo | ||||||||
| LDA svc_dispatch_lo,x ; Load dispatch address low byte | |||||||||
| 8E6D | PHA ; Push low byte for RTS dispatch | ||||||||
| 8E6E | LDX fs_options ; Load FS options pointer | ||||||||
| 8E70 | .dispatch_rts←3← 8E42 BEQ← 8E50 BCS← 8E5D BCS | ||||||||
| RTS ; Dispatch via RTS | |||||||||
Service &24: dynamic workspace claim (1 page)Two-byte handler reached via |
|
| 8E71 | .noop_dey_rts |
| DEY ; Claim 1 page (DEY = decrement Y by 1) | |
| 8E72 | RTS ; Return |
Service &25: FS name + info replyReached via |
|
| 8E73 | .copy_template_to_zp |
| LDX #&0a ; X = 10 (top of 11-byte template) | |
| 8E75 | .loop_copy_return_template←1← 8E7C BPL |
| LDA fs_info_template,x ; Load template byte X from &8E7F+X | |
| 8E78 | STA (os_text_ptr),y ; Store at (&F2),Y |
| 8E7A | INY ; Advance destination cursor |
| 8E7B | DEX ; Step to previous template byte |
| 8E7C | BPL loop_copy_return_template ; Loop until X has wrapped past 0 |
| 8E7E | .fs_template_done←1← 8E8D BVC |
| RTS ; Return | |
FS-name reply template (11 bytes, byte-reversed)Source data for the byte-reverse copy in
|
|
| 8E7F | .fs_info_template←1← 8E75 LDA |
| EQUB &05 | |
| 8E80 | EQUS "/ TEN" ; 11-byte template (length 5 in [0], then ' TEN'); copied to (&F2),Y by copy_template_to_zp |
Service &26: close all files (FILEV via Y=0)Reached via |
|
| 8E8A | .svc_26_close_all_files |
| BIT fs_flags ; Test bit 6 of fs_flags (NFS currently selected?) | |
| 8E8D | BVC fs_template_done ; Clear: return without acting |
| 8E8F | JSR ensure_fs_selected ; Ensure NFS is the selected FS |
| 8E92 | LDA #0 ; A=0 |
| 8E94 | TAY ; Y=0 -- FILEV 'close all files' sub-call |
| 8E95 | JMP findv_handler ; Tail-call findv_handler (= FILEV) |
Read CMOS RAM byte 0 (Master 128)Sets Single caller (
|
||||
| 8E98 | .read_cmos_byte_0←1← 8FBB JSR | |||
| LDX #0 ; X=0: CMOS RAM index 0 (station ID) | ||||
| fall through ↓ | ||||
OSBYTE &A1 (read Master CMOS RAM byte)Loads Dual-use trick: the 5 bytes
|
|||||||||
| 8E9A | .osbyte_a1←20← 8D0C JSR← 8F13 JSR← 8F60 JSR← 8F68 JSR← 8F70 JSR← 8F7A JSR← 8F9C JSR← 904F LDA← 9055 LDA← 95F7 JSR← 95FE JSR← 961B JSR← 9625 JSR← 9644 JSR← 9661 JSR← 9672 JSR← 967F JSR← A0E3 JSR← A70D JSR← B6DE JSR | ||||||||
| LDA #osbyte_read_cmos_ram ; A=&A1: OSBYTE &A1 = read CMOS RAM | |||||||||
| 8E9C | JMP osbyte ; Master and Compact: Read CMOS RAM/EEPROM byte X | ||||||||
Printer-server name template (8 bytes)Eight bytes ( |
|
| ; 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 = | |
| ; &8E9F). 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. | |
| 8E9F | EQUS "PRINT " ; PS template: default name "PRINT " |
| 8EA5 | EQUB &01, &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. |
|
| 8EA7 | .fs_vector_table←1← 8B78 LDA |
| EQUW ev_filev ; FILEV dispatch | |
| 8EA9 | EQUW ev_argsv ; ARGSV dispatch |
| 8EAB | EQUW ev_bgetv ; BGETV dispatch |
| 8EAD | EQUW ev_bputv ; BPUTV dispatch |
| 8EAF | EQUW ev_gbpbv ; GBPBV dispatch |
| 8EB1 | EQUW ev_findv ; FINDV dispatch |
| 8EB3 | EQUW ev_fscv ; FSCV dispatch |
| 8EB5 | EQUW filev_handler ; FILEV handler |
| 8EB7 | EQUB &4A ; (ROM bank — not read) |
| 8EB8 | EQUW argsv_handler ; ARGSV handler |
| 8EBA | EQUB &44 ; (ROM bank — not read) |
| 8EBB | EQUW bgetv_handler ; BGETV handler |
| 8EBD | EQUB &57 ; (ROM bank — not read) |
| 8EBE | EQUW bputv_handler ; BPUTV handler |
| 8EC0 | EQUB &42 ; (ROM bank — not read) |
| 8EC1 | EQUW gbpbv_handler ; GBPBV handler |
| 8EC3 | EQUB &41 ; (ROM bank — not read) |
| 8EC4 | EQUW findv_handler ; FINDV handler |
| 8EC6 | EQUB &52 ; (ROM bank — not read) |
| 8EC7 | EQUW fscv_handler ; FSCV handler |
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.
|
|||||||||
| 8EC9 | .osbyte_x0←4← 805D JSR← 9041 JSR← 9731 JSR← 99FD JSR | ||||||||
| LDX #0 ; X=0 then fall through into osbyte_yff | |||||||||
| 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.
|
|||||||||
| 8ECB | .osbyte_yff←1← 8067 JSR | ||||||||
| LDY #&ff ; Y=&FF: 'read' parameter for OSBYTE | |||||||||
| 8ECD | .jmp_osbyte←1← 8ED6 BEQ | ||||||||
| JMP osbyte ; Tail-call OSBYTE | |||||||||
| 8ED0 | EQUB &FC, &AC | ||||||||
OSBYTE wrapper with X=0, Y=0Sets
|
|||||||||
| 8ED2 | .osbyte_x0_y0←1← 9A10 JSR | ||||||||
| LDX #0 ; X=0: clear OSBYTE X arg | |||||||||
| 8ED4 | LDY #0 ; Y=0 | ||||||||
| 8ED6 | 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.
|
||||
| 8ED8 | .svc_7_osbyte | |||
| LDA osbyte_a_copy ; Get original OSBYTE A parameter | ||||
| 8EDA | SBC #&31 ; Subtract &31 (map &32-&35 to 1-4) | |||
| 8EDC | CMP #4 ; In range 0-3? | |||
| 8EDE | BCS return_from_raise_y_to_c8 ; No: not ours, return unclaimed | |||
| 8EE0 | TAX ; Transfer to X as dispatch index | |||
| 8EE1 | STZ svc_state ; Clear svc_state | |||
| 8EE3 | TYA ; Transfer Y to A (OSBYTE Y param) | |||
| 8EE4 | LDY #&2f ; Y=&2F: OSBYTE dispatch offset | |||
| 8EE6 | JMP svc_dispatch ; Dispatch to OSBYTE handler via table | |||
Master 128 service &21 handler: claim static hidden-RAM workspaceFour-instruction stub:
|
|||||||
| 8EE9 | .raise_y_to_c8 | ||||||
| CPY #&c8 ; Y already >= &C8? | |||||||
| 8EEB | BCS return_from_raise_y_to_c8 ; Yes: return Y unchanged | ||||||
| 8EED | LDY #&c8 ; No: raise Y to &C8 | ||||||
| 8EEF | .return_from_raise_y_to_c8←2← 8EDE BCS← 8EEB BCS | ||||||
| RTS ; Return | |||||||
Record workspace page count (capped at &D3)Stores the workspace allocation from service 1 into offset
|
||||
| 8EF0 | .store_ws_page_count | |||
| TYA ; Transfer Y to A | ||||
| 8EF1 | PHA ; Push for save | |||
| 8EF2 | CMP #&d3 ; Y >= &D3? | |||
| 8EF4 | BCC done_cap_ws_count ; No: use Y as-is | |||
| 8EF6 | LDA #&d3 ; Cap at &D3 | |||
| 8EF8 | .done_cap_ws_count←1← 8EF4 BCC | |||
| LDY #&0b ; Offset &0B in receive block | ||||
| 8EFA | STA (net_rx_ptr),y ; Store workspace page count | |||
| 8EFC | PLY ; Pop -- save Y temporarily | |||
| 8EFD | RTS ; Return -- ws_page count saved | |||
| 8EFE | .set_rom_ws_page←1← 8F35 BCS | |||
| TYA ; Caller's page (in Y) into A | ||||
| 8EFF | LDY romsel_copy ; Y = current ROM slot from romsel_copy | |||
| 8F01 | PHA ; Push restored value | |||
| 8F02 | AND #&7f ; Mask bit 7 (workspace flag) | |||
| 8F04 | STA rom_ws_pages,y ; Publish page into rom_ws_pages[slot] (bit 7 cleared = workspace claimed) | |||
| 8F07 | LDY master_break_type_shadow ; Read Master break-type shadow (&FE2B) | |||
| 8F0A | LDY master_romsel_shadow ; Read &FE28 (Master ROMSEL shadow) | |||
| 8F0D | PLY ; Pop saved Y | |||
| 8F0E | INY ; Increment for next page | |||
| 8F0F | RTS ; Return | |||
Service-2 page-allocation prologueReads CMOS byte This routine handles only the workspace-page allocation half of
service 2. The bring-up remainder (station ID, FS workspace zero,
|
||||
| 8F10 | .svc_2_priv_ws | |||
| PHY ; Save Y on stack (caller's claim) | ||||
| 8F11 | LDX #&11 ; X=&11: CMOS RAM byte index | |||
| 8F13 | JSR osbyte_a1 ; Read CMOS &11 via osbyte_a1 | |||
| 8F16 | TYA ; A = CMOS &11 value | |||
| 8F17 | AND #4 ; Mask bit 2 (workspace-size flag) | |||
| 8F19 | BNE private_ws_set_bit ; Bit 2 set: keep caller's Y, advance by 2 | |||
| 8F1B | LDA #&0b ; Bit 2 clear: A=&0B (use 11-page minimum) | |||
| 8F1D | BRA commit_workspace_pages ; BRA to common tail | |||
| 8F1F | .private_ws_set_bit←1← 8F19 BNE | |||
| PLY ; Bit-2-set path: restore Y | ||||
| 8F20 | TYA ; TYA / INY / INY -- raise Y by 2 pages | |||
| 8F21 | INY ; Y += 1 | |||
| 8F22 | INY ; Y += 1 again (total +2) | |||
| 8F23 | PHY ; Push raised Y | |||
| 8F24 | .commit_workspace_pages←1← 8F1D BRA | |||
| STA net_rx_ptr_hi ; Store final page count high to net_rx_ptr_hi | ||||
| 8F26 | INC ; Increment for nfs_workspace_hi | |||
| 8F27 | STA nfs_workspace_hi ; Store workspace high page | |||
| 8F29 | LDA #0 ; A=0: clear-byte for the lo halves below | |||
| 8F2B | STA net_rx_ptr ; Clear net_rx_ptr_lo (page-aligned) | |||
| 8F2D | STA nfs_workspace ; Clear nfs_workspace_lo (page-aligned) | |||
| 8F2F | JSR get_ws_page ; Compute workspace start page via get_ws_page | |||
| 8F32 | CPY #&dc ; Y >= &DC? | |||
| 8F34 | PLY ; Restore Y from stack | |||
| 8F35 | BCS set_rom_ws_page ; Yes: jump to set_rom_ws_page (error path) | |||
| 8F37 | RTS ; Return | |||
ANFS initialisation bodyReached only via PHA/PHA/RTS dispatch (table index 22 in the
svc_dispatch table at
Returns via Reached via Master 128 service call
The full set of Master 128 service calls ANFS handles, dispatched
via the CMP/SBC normalisation chain in
Everything else (svc |
|||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F38 | .nfs_init_body | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| LDA #0 ; A=0: clear-byte for the next four stores | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F3A | STA ws_page ; Clear ws_page (workspace page count) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F3C | STA tx_complete_flag ; Clear tx_complete_flag | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F3F | LDY #0 ; Y=0: receive-block offset 0 (remote-op flag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F41 | STA (net_rx_ptr),y ; Clear remote-op flag at (net_rx_ptr)+0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F43 | LDA last_break_type ; Read l028D (current ROM number) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F46 | BNE nfs_init_check_fs_flags ; Non-zero (re-init): take nfs_init_check_fs_flags path | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F48 | LDA #&10 ; A=&10: fs_flags bit 4 mask (checks 'workspace already set up') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F4A | BIT fs_flags | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F4D | BEQ alloc_post_restore_check ; Zero: first ROM init, skip FS setup | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F4F | .nfs_init_check_fs_flags←1← 8F46 BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSR setup_ws_ptr ; Set up workspace pointers | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F52 | STA fs_flags ; Clear FS flags | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F55 | TAY ; A=0, transfer to Y | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F56 | .loop_zero_workspace←1← 8F59 BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| STA (fs_ws_ptr),y ; Clear byte in FS workspace | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F58 | INY ; Next workspace byte | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F59 | BNE loop_zero_workspace ; Loop until full page (256 bytes) cleared | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F5B | JSR copy_ps_data_y1c ; Copy initial PS template (1C bytes) into ws | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F5E | LDX #1 ; X=1: CMOS &01 = port number | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F60 | JSR osbyte_a1 ; Read CMOS &01 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F63 | STY hazel_fs_station ; Store at hazel_fs_station (workspace+0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F66 | LDX #2 ; X=2: CMOS &02 = network number | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F68 | JSR osbyte_a1 ; Read CMOS &02 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F6B | STY hazel_fs_network ; Store at hazel_fs_network | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F6E | LDX #3 ; X=3: CMOS &03 = FS station | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F70 | JSR osbyte_a1 ; Read CMOS &03 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F73 | TYA ; A = FS station | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F74 | LDY #2 ; Y=2: nfs_workspace offset for FS station | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F76 | STA (nfs_workspace),y ; Store FS station at (nfs_workspace)+2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F78 | LDX #4 ; X=4: CMOS &04 = FS network | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F7A | JSR osbyte_a1 ; Read CMOS &04 (FS network) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F7D | TYA ; A = FS network | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F7E | LDY #3 ; Y=3: nfs_workspace offset for FS network | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F80 | STA (nfs_workspace),y ; Store at NFS workspace offset 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F82 | LDX #3 ; X=3: init data byte count | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F84 | .loop_copy_init_data←1← 8F8B BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| LDA cdir_size_done,x ; Load initialisation data byte | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F87 | STA fs_flags,x ; Store in workspace | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F8A | DEX ; Decrement counter | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F8B | BNE loop_copy_init_data ; More bytes: loop | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F8D | STX hazel_fs_messages_flag ; Clear workspace flag | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F90 | STX hazel_fs_flags ; Clear workspace byte | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F93 | JSR reset_spool_buf_state ; Initialise ADLC protection table | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F96 | DEX ; X=&FF (underflow from X=0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F97 | STX spool_control_flag ; Initialise workspace flag to &FF | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F9A | LDX #&11 ; X=&11: CMOS &11 (ANFS settings) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F9C | JSR osbyte_a1 ; Read CMOS &11 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8F9F | TYA ; A = settings byte | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FA0 | AND #&40 ; Mask bit 6 (CMOS protection-state flag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FA2 | BEQ init_copy_skip_cmos ; Bit clear: skip the &FF substitution | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FA4 | LDA #&ff ; A=&FF -- enable protection | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FA6 | .init_copy_skip_cmos←1← 8FA2 BEQ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSR set_via_shadow_pair ; Set shadow ACR/IER pair | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FA9 | .loop_alloc_handles←1← 8FB6 BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| LDA ws_page ; Get current workspace page | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FAB | JSR byte_to_2bit_index ; Allocate FS handle page | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FAE | BCS done_alloc_handles ; Allocation failed: finish init | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FB0 | LDA #&3f ; A=&3F: default handle permissions | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FB2 | STA (nfs_workspace),y ; Store handle permissions | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FB4 | INC ws_page ; Advance to next page | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FB6 | BNE loop_alloc_handles ; Continue allocating: loop | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FB8 | .done_alloc_handles←1← 8FAE BCS | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSR restore_fs_context ; Restore FS context from saved state | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FBB | .alloc_post_restore_check←1← 8F4D BEQ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSR read_cmos_byte_0 ; Read CMOS &00 (= station ID byte) | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FBE | TYA ; Y (CMOS value) into A | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FBF | BNE alloc_common_entry ; Non-zero: station ID valid -> alloc_common_entry | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FC1 | .alloc_error_overflow←1← 9000 BEQ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSR print_inline ; Print 'Station number in CMOS RAM invalid...' warning | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FC4 | EQUS "Station number in CMOS RAM invalid..Using 1 instead!..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FFB | LDA #1 ; A=1: default station ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FFD | BRA alloc_store_station_id ; BRA to alloc_store_station_id with default | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8FFF | .alloc_common_entry←1← 8FBF BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| INY ; Check next byte (CMOS station ID hi?) | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9000 | BEQ alloc_error_overflow ; INY wrapped past 0 (station=&FF then INY=&00): report 'CMOS RAM invalid' and default to 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9002 | BRA alloc_store_station_id ; BRA to alloc_store_station_id (always) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9004 | .alloc_store_station_id←2← 8FFD BRA← 9002 BRA | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| LDY #1 ; Y=1: net_rx_ptr offset for station-ID byte | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9006 | STA (net_rx_ptr),y ; Store station ID into (net_rx_ptr)+1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9008 | LDX #&40 ; X=&40: econet_flags init value | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 900A | STX econet_flags ; Initialise econet_flags | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 900D | JSR cmd_net_fs ; Call cmd_net_fs to select NFS | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9010 | BEQ complete_nfs_init ; Z: selection succeeded | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9012 | LDA #&10 ; A=&10: bit 4 marker for fs_flags | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9014 | ORA fs_flags | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9017 | STA fs_flags ; Store updated fs_flags | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 901A | .complete_nfs_init←1← 9010 BEQ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSR init_adlc_and_vectors ; Initialise ADLC and FILEV/ARGSV/...vectors | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 901D | LDA #3 ; A=3: spool-ctrl byte 'init' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 901F | JSR handle_spool_ctrl_byte ; Initialise *SPOOL handle in workspace | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9022 | JSR init_bridge_poll ; Send a bridge-discovery packet and poll | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9025 | PHA ; Save current bridge byte | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9026 | EOR hazel_fs_network ; With stored hazel_fs_network (network number) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9029 | BNE verify_copy_station_id ; Different: take verify_copy_station_id path | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 902B | STA hazel_fs_network ; Same: store as new hazel_fs_network | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 902E | LDY #3 ; Y=3: net_rx_ptr offset 3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9030 | STA (net_rx_ptr),y ; Store at (net_rx_ptr)+3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9032 | .verify_copy_station_id←1← 9029 BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| PLA ; Restore saved byte | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9033 | LDY #3 ; Y=3: workspace offset | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9035 | EOR (nfs_workspace),y | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9037 | BNE return_2 ; Mismatch: skip store | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9039 | STA (nfs_workspace),y ; Match: store at (nfs_workspace)+3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 903B | .return_2←1← 9037 BNE | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| RTS ; Return | |||||||||||||||||||||||||||||||||||||||||||||||||||||
Initialise ADLC and install extended vectorsReads the ROM pointer table via OSBYTE
|
||||
| 903C | .init_adlc_and_vectors←2← 8B81 JSR← 901A JSR | |||
| JSR adlc_init ; Initialise ADLC hardware | ||||
| 903F | LDA #&a8 ; OSBYTE &A8: read ROM pointer table | |||
| 9041 | JSR osbyte_x0 ; Read ROM pointer table address | |||
| 9044 | STX fs_error_ptr ; Store table pointer low | |||
| 9046 | STY fs_crflag ; Store table pointer high | |||
| 9048 | LDY #&36 ; Y=&36: NETV vector offset | |||
| 904A | STY netv ; Set NETV address | |||
| 904D | LDX #1 ; X=1: one more vector pair to set | |||
| fall through ↓ | ||||
Install extended-vector table entriesCopies vector addresses from the dispatch table at
After the loop, stores
|
|||||||||
| 904F | .write_vector_entry←2← 8B88 JSR← 9061 BNE | ||||||||
| LDA osbyte_a1,y ; Load vector address low byte | |||||||||
| 9052 | STA (fs_error_ptr),y ; Store into extended vector table | ||||||||
| 9054 | INY ; Advance to high byte | ||||||||
| 9055 | LDA osbyte_a1,y ; Load vector address high byte | ||||||||
| 9058 | STA (fs_error_ptr),y ; Store into extended vector table | ||||||||
| 905A | INY ; Advance to ROM ID byte | ||||||||
| 905B | LDA romsel_copy ; Load current ROM slot number | ||||||||
| 905D | STA (fs_error_ptr),y ; Store ROM ID in extended vector | ||||||||
| 905F | INY ; Advance to next vector entry | ||||||||
| 9060 | DEX ; Decrement vector counter | ||||||||
| 9061 | BNE write_vector_entry ; More vectors to set: loop | ||||||||
| 9063 | RTS ; Return | ||||||||
Restore FS context from HAZEL into RX blockCopies 8 bytes (offsets 2..9) from the HAZEL FS state block into
the receive control block at Called by
|
||||
| 9064 | .restore_fs_context←3← 8FB8 JSR← 907B JSR← A6D2 JMP | |||
| LDY #9 ; Y=9: end of FS context block | ||||
| 9066 | .loop_restore_ctx←1← 906E BNE | |||
| LDA hazel_minus_2,y ; Load FS context byte | ||||
| 9069 | STA (net_rx_ptr),y ; Store into receive block | |||
| 906B | DEY ; Decrement index | |||
| 906C | CPY #1 ; Reached offset 1? | |||
| 906E | BNE loop_restore_ctx ; No: continue copying | |||
| 9070 | RTS ; Return | |||
Deselect filing system and save workspaceIf the filing system is currently selected (bit 7 of
|
|
| 9071 | .fscv_6_shutdown |
| BIT fs_flags ; FS currently selected? | |
| 9074 | BPL return_from_fs_shutdown ; No (bit 7 clear): return |
| 9076 | LDY #0 ; Y=0 |
| 9078 | JSR process_all_fcbs ; Close all FCBs (process_all_fcbs) |
| 907B | JSR restore_fs_context ; Restore FS context to receive block |
| 907E | LDY #&76 ; Y=&76: checksum range end |
| 9080 | LDA #0 ; A=0: checksum accumulator |
| 9082 | CLC ; Clear carry for addition |
| 9083 | .loop_checksum_byte←1← 9087 BPL |
| ADC hazel_fcb_addr_lo,y ; Add byte from page &10 shadow | |
| 9086 | DEY ; Decrement index |
| 9087 | BPL loop_checksum_byte ; Loop until all bytes summed |
| 9089 | LDY #&77 ; Y=&77: checksum storage offset |
| 908B | BPL store_ws_byte ; ALWAYS branch |
| 908D | .loop_copy_to_ws←1← 9093 BPL |
| LDA hazel_fcb_addr_lo,y ; Load byte from page &10 shadow | |
| 9090 | .store_ws_byte←1← 908B BPL |
| STA (fs_ws_ptr),y ; Copy to FS workspace | |
| 9092 | DEY ; Decrement index |
| 9093 | BPL loop_copy_to_ws ; Loop until all bytes copied |
| 9095 | LDA fs_flags ; Load FS flags |
| 9098 | AND #&7f ; Clear bit 7 (FS no longer selected) |
| 909A | STA fs_flags ; Store updated flags |
| 909D | .return_from_fs_shutdown←1← 9074 BPL |
| RTS ; Return | |
Verify workspace checksum integritySums bytes 0.. The checksummed page holds open-file information (preserved when ANFS is not the current filing system) and the current printer type. Can only be reset by a control-BREAK. Preserves
|
||||||||
| 909E | .verify_ws_checksum←5← 9EAB JSR← A02F JSR← A10B JSR← A14C JSR← B99A JSR | |||||||
| PHP ; Save processor status | ||||||||
| 909F | PHA ; Save A | |||||||
| 90A0 | PHY | |||||||
| 90A1 | LDY #&76 ; Y=&76: checksum range end | |||||||
| 90A3 | LDA #0 ; A=0: checksum accumulator | |||||||
| 90A5 | CLC ; Clear carry for addition | |||||||
| 90A6 | .loop_sum_ws←1← 90A9 BPL | |||||||
| ADC (fs_ws_ptr),y ; Add byte from FS workspace | ||||||||
| 90A8 | DEY ; Decrement index | |||||||
| 90A9 | BPL loop_sum_ws ; Loop until all bytes summed | |||||||
| 90AB | LDY #&77 ; Y=&77: checksum storage offset | |||||||
| 90AD | CMP (fs_ws_ptr),y ; Compare with stored checksum | |||||||
| 90AF | BNE error_net_checksum ; Mismatch: raise checksum error | |||||||
| 90B1 | PLY | |||||||
| 90B2 | PLA ; Restore A | |||||||
| 90B3 | PLP ; Restore processor status | |||||||
| 90B4 | RTS ; Return (checksum valid) | |||||||
Raise 'net checksum' BRK errorLoads error code |
|
| 90B5 | .error_net_checksum←2← 8B57 JMP← 90AF BNE |
| LDA #&aa ; Error number &AA | |
| 90B7 | JSR error_bad_inline ; Raise 'net checksum' error |
| 90BA | EQUS "net checksum." |
| fall through ↓ | |
Print Econet station number and clock statusUses Called by
|
||||
| 90C7 | .print_station_id←2← 8CAA JMP← 8CE4 JSR | |||
| JSR print_inline ; Print 'Station ' inline string | ||||
| 90CA | EQUS "Econet Station " ; Print 'Econet Station ' via inline | |||
| 90D9 | LDY #1 ; Y=1: PB station-byte offset | |||
| 90DB | LDA (net_rx_ptr),y ; Read RX[1] = station number | |||
| 90DD | JSR print_num_no_leading ; Print as decimal (no leading zeros) | |||
| 90E0 | LDA #&20 ; Space character | |||
| 90E2 | BIT adlc_cr2 ; Check ADLC status register 2 | |||
| 90E5 | BEQ done_print_newline ; Clock present: skip warning | |||
| 90E7 | JSR print_inline ; Print ' No Clock' via inline | |||
| 90EA | EQUS " No Clock" | |||
| 90F3 | NOP ; String terminator | |||
| 90F4 | .done_print_newline←1← 90E5 BEQ | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| 90F7 | RTS ; Return | |||
*HELP / *SYNTAX argument strings (8 messages)Eight zero-terminated argument-syntax strings used by the *HELP
text builder. Each string describes the argument shape of a
particular command group; their offsets within this table are
stored in |
|
| ; *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. | |
| 90F8 | .syn_opt_dir←1← 8C07 LDA |
| EQUS "(<dir>)" ; Syn 1: *Dir, *LCat, *LEx, *Wipe | |
| 90FF | EQUB &00 |
| 9100 | .syn_iam |
| EQUS "(<stn. id.>) <user id.> " ; Syn 2: *I Am (login) | |
| 9118 | EQUB &0D ; Line break |
| 9119 | EQUS "((:<CR>)<password>)" ; syntax help for *Pass / *I am |
| 912C | EQUB &00 |
| 912D | .syn_object |
| EQUS "<object>" ; Syn 3: *Delete, *FS, *Remove | |
| 9135 | EQUB &00 |
| 9136 | EQUS "<filename> (<offset> " ; Store as string pointer low Store as string pointer high |
| 914B | EQUB &0D |
| 914C | EQUS "(<address>))" ; Syn 4 continued: address clause |
| 9158 | EQUB &00 ; Null terminator |
| 9159 | .syn_dir |
| EQUS "<dir>" ; Syn 5: *Lib | |
| 915E | EQUB &00 |
| 915F | EQUS "<dir> (<number>)" |
| 916F | EQUB &00 |
| 9170 | .syn_password |
| EQUS "(:<CR>) <password> " ; Syn 7: *Pass | |
| 9183 | EQUB &0D |
| 9184 | EQUS "<new password>" ; Syn 7 continued: new password |
| 9192 | EQUB &00 |
| 9193 | EQUS "(<stn. id.>|<ps type>)" ; syntax help for *PS / *Pollps |
| 91A9 | EQUB &00 |
| 91AA | .syn_access |
| EQUS "<object> (L)(W)(R)(/(W)(R))" ; Syn 9: *Access | |
| 91C5 | EQUB &00 ; Null terminator |
| 91C6 | .syn_rename |
| EQUS "<filename> <new filename>" ; Syn 10: *Rename | |
| 91DF | EQUB &00 ; Null terminator |
| 91E0 | .syn_opt_stn |
| EQUS "(<stn. id.>)" ; Syn 11: (station id. argument) | |
| 91EC | EQUB &00 ; Null terminator |
Argument-syntax offset table (12 entries)Twelve byte offsets indexing into
|
|
| ; Command syntax string offset table | |
| ; 13 offsets into syn_opt_dir (&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. | |
| 91ED | .cmd_syntax_table←1← 8C02 LDA |
| EQUB syn_iam - syn_opt_dir - 2 ; Idx 0: 'opt_dir' (offset -2 variant for *Dir's INY-twice walker) | |
| 91EE | EQUB &FF ; Idx 1: &FF = no syntax string for this index |
| 91EF | EQUB syn_iam - syn_opt_dir - 1 ; Idx 2: "(<stn.id.>) user id.>..." |
| 91F0 | EQUB syn_object - syn_opt_dir - 1 ; Idx 3: " |
| 91F1 | EQUB &3D ; Idx 4: " |
| 91F2 | EQUB &60 ; Idx 5: ' |
| 91F3 | EQUB &66 ; Idx 6: continued |
| 91F4 | EQUB &77 ; Idx 7: "(: |
| 91F5 | EQUB &9A ; Idx 8: "(<stn.id.>| |
| 91F6 | EQUB syn_access - syn_opt_dir - 1 ; Idx 9: " |
| 91F7 | EQUB &CD ; Idx 10: ' |
| 91F8 | EQUB syn_opt_stn - syn_opt_dir - 1 ; Idx 11: "(<stn. id.>)" |
Print CR via OSASCI, bypassing any open *SPOOL fileLoads Called from
|
||||
| 91F9 | .print_newline_no_spool←6← 8A7C JSR← 8E10 JSR← 9D3E JSR← B1E0 JSR← B227 JMP← B7B7 JSR | |||
| LDA #&0d ; A=&0D (CR) for OSASCI translation; fall through | ||||
| fall through ↓ | ||||
Print A via OSASCI, bypassing any open *SPOOL filePushes the caller's flags, then forces
Eight inner-ROM callers:
|
||||
| 91FB | .print_char_no_spool←11← 925F BRA← 92A4 JSR← 9D30 JSR← 9D5C JMP← B1B9 JSR← B21F JSR← B2F9 JSR← B321 JSR← B752 JSR← B776 JSR← B78D JSR | |||
| PHP ; Save caller's flags (V from caller is irrelevant — see &91FC) | ||||
| 91FC | BIT always_set_v_byte ; Unconditionally sets V=1 (bit 6 of operand &FF) | |||
| 91FF | BVS save_regs_for_print_no_spool ; V=1 always, branch always taken (skips the CLV path) | |||
| fall through ↓ | ||||
Print A via OSWRCH (raw, no CR translation), bypass *SPOOLAs Used when the caller wants to emit a raw byte (e.g. a VDU
control code) without
|
||||
| 9201 | .print_byte_no_spool←2← 8DE6 JSR← B76E JSR | |||
| PHP ; Alt entry: save caller's flags BEFORE forcing V=0 | ||||
| 9202 | CLV ; Force V=0 -> OSWRCH path at &9220 (raw byte) | |||
| 9203 | .save_regs_for_print_no_spool←1← 91FF BVS | |||
| PHX ; Save X | ||||
| 9204 | PHY ; Save Y | |||
| 9205 | PHA ; Save A (the byte to print) | |||
| 9206 | PHP ; Save inner P — V here picks OSASCI vs OSWRCH later | |||
| 9207 | LDA #osbyte_read_write_spool_file_handle ; OSBYTE 199 (read/write *SPOOL file handle) | |||
| 9209 | LDX #0 ; X=0: handle value to write | |||
| 920B | LDY #0 ; Y=0: write mode (NEW = (OLD AND 0) EOR X = X = 0) | |||
| 920D | JSR osbyte ; Closes spool; X returns OLD handle | |||
| 9210 | CPX #&20 ; OLD < ' '? (likely 0 = was already closed) | |||
| 9212 | BCC do_print_no_spool ; Yes: leave spool closed for the print | |||
| 9214 | CPX #&30 ; OLD >= '0'? | |||
| 9216 | BCS do_print_no_spool ; Yes (>= &30): leave spool closed | |||
| 9218 | JSR osbyte ; OLD in [&20,&2F] (NFS handle range): re-open spool with X=OLD | |||
| 921B | LDX #0 ; Clear X for the post-print restore | |||
| 921D | .do_print_no_spool←2← 9212 BCC← 9216 BCS | |||
| PLP ; Restore inner P (V=1 OSASCI / V=0 OSWRCH) | ||||
| 921E | PLA ; Pull A (the byte) | |||
| 921F | PHA ; Push it back so the final epilogue PLA still works | |||
| 9220 | BVC print_via_oswrch ; V=0 -> OSWRCH (raw); V=1 -> OSASCI (CR translation) | |||
| 9222 | JSR osasci ; OSASCI: writes A, translating CR to CR/LF | |||
| 9225 | BRA restore_spool_and_return ; Skip OSWRCH branch | |||
| 9227 | .print_via_oswrch←1← 9220 BVC | |||
| JSR oswrch ; OSWRCH: writes A as a raw byte | ||||
| 922A | .restore_spool_and_return←1← 9225 BRA | |||
| LDA #osbyte_read_write_spool_file_handle ; OSBYTE 199 again to restore spool state | ||||
| 922C | LDY #&ff ; Y=&FF (read mode): NEW = OLD EOR X | |||
| 922E | JSR osbyte ; X=0 -> no change; X=OLD -> writes OLD back | |||
| 9231 | PLA ; Pull A (preserved across the call) | |||
| 9232 | PLY ; Pull Y | |||
| 9233 | PLX ; Pull X | |||
| 9234 | PLP ; Pull caller's original flags | |||
| 9235 | RTS ; Return | |||
Print A as two hexadecimal digitsSaves Callers:
|
|||||||
| 9236 | .print_hex_byte←2← BD90 JSR← BE38 JSR | ||||||
| PHA ; Save full byte | |||||||
| 9237 | LSR ; Shift high nybble to low | ||||||
| 9238 | LSR ; Continue shifting | ||||||
| 9239 | LSR ; Continue shifting | ||||||
| 923A | LSR ; High nybble now in bits 0-3 | ||||||
| 923B | JSR print_hex_nybble ; Print high nybble as hex digit | ||||||
| 923E | PLA ; Restore full byte | ||||||
| fall through ↓ | |||||||
Print low nybble of A as hex digitMasks
|
||||
| 923F | .print_hex_nybble←1← 923B JSR | |||
| AND #&0f ; Mask to low nybble | ||||
| 9241 | CMP #&0a ; Digit >= &0A? | |||
| 9243 | BCC add_ascii_base ; No: skip letter adjustment | |||
| 9245 | ADC #6 ; Add 7 to get 'A'-'F' (6 + carry) | |||
| 9247 | .add_ascii_base←1← 9243 BCC | |||
| ADC #&30 ; Add &30 for ASCII '0'-'9' or 'A'-'F' | ||||
| 9249 | JMP osasci ; Write character | |||
Print A as two hex digits, *SPOOL-bypassingAs
|
|||||||
| 924C | .print_hex_byte_no_spool←2← 9D53 JSR← B1A9 JSR | ||||||
| PHA ; Save full byte | |||||||
| 924D | LSR ; Shift high nybble to low (LSR x4) | ||||||
| 924E | LSR ; LSR / LSR / LSR -- shift hi nibble down to lo | ||||||
| 924F | LSR ; (continued) | ||||||
| 9250 | LSR ; (continued) | ||||||
| 9251 | JSR print_hex_nybble_no_spool ; Print high nybble as hex digit | ||||||
| 9254 | PLA ; Restore full byte; fall through for low nybble | ||||||
| fall through ↓ | |||||||
Print low nybble of A as one hex digit, *SPOOL-bypassingAs print_hex_nybble (&923F) but emits via the print_char_no_spool tail-call instead of OSASCI directly, so the digit is not captured by any active *SPOOL file. Standard AND #&0F / CMP #&0A / +6-or-not / + #&30 mapping for hex digits 0-9 / A-F. Tail-jumps to print_char_no_spool via BRA.
|
||||
| 9255 | .print_hex_nybble_no_spool←1← 9251 JSR | |||
| AND #&0f ; Mask to low nybble | ||||
| 9257 | CMP #&0a ; Digit >= &0A? | |||
| 9259 | BCC print_nybble_leading_zero ; No: skip letter adjustment | |||
| 925B | ADC #6 ; Add 7 to get 'A'-'F' (6 + carry) | |||
| 925D | .print_nybble_leading_zero←1← 9259 BCC | |||
| ADC #&30 ; Add &30 for ASCII '0'-'9' or 'A'-'F' | ||||
| 925F | BRA print_char_no_spool ; Tail-jump to *SPOOL-bypassing print | |||
| fall through ↓ | ||||
Print inline string, high-bit terminatedPops the return address from the stack, prints each byte via
Common terminators:
|
|||||||||||||||||
| 9261 | .print_inline←33← 8A6B JSR← 8BE0 JSR← 8C93 JSR← 8FC1 JSR← 90C7 JSR← 90E7 JSR← 95AC JSR← 95C1 JSR← 95C8 JSR← 95CD JSR← 95DA JSR← 964C JSR← 9653 JSR← 9668 JSR← 9679 JSR← B460 JSR← B46C JSR← B483 JSR← B48D JSR← B498 JSR← B568 JSR← B60A JSR← B61F JSR← B642 JSR← B64F JSR← B65E JSR← B66E JSR← B67D JSR← BDAC JSR← BDC4 JSR← BDD0 JSR← BE04 JSR← BE21 JSR | ||||||||||||||||
| PLA ; Pop return address (low) — points to last byte of JSR | |||||||||||||||||
| 9262 | STA fs_error_ptr ; Store as fs_error_ptr (return-addr saved) | ||||||||||||||||
| 9264 | PLA ; Pop return address (high) | ||||||||||||||||
| 9265 | STA fs_crflag ; Store as fs_crflag (entry flag) | ||||||||||||||||
| 9267 | LDY #0 ; Y=0: start scanning at offset 0 | ||||||||||||||||
| fall through ↓ | |||||||||||||||||
print_inline pointer-advance step
|
|
| 9269 | .loop_next_char←1← 9284 JMP |
| INC fs_error_ptr ; Advance pointer to next character | |
| 926B | BNE load_char ; Z clear: continue with this char |
| 926D | INC fs_crflag ; Z set (CR): increment fs_crflag |
| 926F | .load_char←1← 926B BNE |
| LDA (fs_error_ptr),y ; Load next byte from inline string | |
| 9271 | BMI resume_caller ; Bit 7 set? Done — this byte is the next opcode |
| 9273 | LDA fs_error_ptr ; Read fs_error_ptr (saved across OSASCI) |
| 9275 | PHA ; Push it |
| 9276 | LDA fs_crflag ; Read fs_crflag |
| 9278 | PHA ; Push it |
| 9279 | LDA (fs_error_ptr),y ; Reload character (pointer may have been clobbered) |
| 927B | JSR osasci ; Print character via OSASCI |
| 927E | PLA ; Pop saved fs_crflag |
| 927F | STA fs_crflag ; Restore fs_crflag |
| 9281 | PLA ; Pop saved fs_error_ptr |
| 9282 | STA fs_error_ptr ; Restore fs_error_ptr |
| 9284 | JMP loop_next_char ; Loop back |
| 9287 | .resume_caller←1← 9271 BMI |
| JMP (fs_error_ptr) ; Jump to address of high-bit byte (resumes code) | |
Print inline string, high-bit terminated, *SPOOL-bypassingAs Used by status output that should not be saved to a spool file
(e.g. Six callers:
|
||||||||
| 928A | .print_inline_no_spool←13← 981A JSR← B158 JSR← B162 JSR← B170 JSR← B17B JSR← B197 JSR← B1AC JSR← B1BF JSR← B1CE JSR← B2F0 JSR← B75E JSR← B77E JSR← B7CB JSR | |||||||
| PLA ; Pop return-addr low byte (-> string pointer low) | ||||||||
| 928B | STA fs_error_ptr ; Save in fs_error_ptr (the loop's pointer low) | |||||||
| 928D | PLA ; Pop return-addr high byte | |||||||
| 928E | STA fs_crflag ; Save in fs_crflag (the loop's pointer high) | |||||||
| 9290 | LDY #0 ; Y=0: indirect index for (fs_error_ptr),Y | |||||||
| 9292 | .loop_print_inline_string←1← 92AD BRA | |||||||
| INC fs_error_ptr ; Step pointer low byte to next char | ||||||||
| 9294 | BNE print_next_string_char ; No carry: skip high-byte INC | |||||||
| 9296 | INC fs_crflag ; Page wrap: bump pointer high | |||||||
| 9298 | .print_next_string_char←1← 9294 BNE | |||||||
| LDA (fs_error_ptr),y ; Read next character from inline string | ||||||||
| 929A | BMI print_char_terminator ; Bit 7 set: terminator -- this byte is the next opcode | |||||||
| 929C | LDA fs_error_ptr ; Save pointer low (print_char_no_spool may clobber) | |||||||
| 929E | PHA ; Push it | |||||||
| 929F | LDA fs_crflag ; Save pointer high | |||||||
| 92A1 | PHA ; Push it | |||||||
| 92A2 | LDA (fs_error_ptr),y ; Reload the character we're about to print | |||||||
| 92A4 | JSR print_char_no_spool ; Print it via the *SPOOL-bypassing OSASCI wrapper | |||||||
| 92A7 | PLA ; Pop pointer high back | |||||||
| 92A8 | STA fs_crflag ; Restore | |||||||
| 92AA | PLA ; Pop pointer low back | |||||||
| 92AB | STA fs_error_ptr ; Restore | |||||||
| 92AD | BRA loop_print_inline_string ; Always taken (BRA-style; A is non-zero from print) | |||||||
| 92AF | .print_char_terminator←1← 929A BMI | |||||||
| JMP (fs_error_ptr) ; Resume execution at the terminator byte's address (JMP indirect via fs_error_ptr) | ||||||||
Parse decimal or hex station address argumentReads characters from the command argument at
|
|||||||||
| 92B2 | .parse_addr_arg←5← 8DB1 JSR← 8DBD JSR← A3C9 JSR← A3DE JSR← B0B5 JSR | ||||||||
| STZ fs_load_addr_2 ; Zero the accumulator (fs_load_addr_2) | |||||||||
| 92B4 | LDA (fs_crc_lo),y ; Read first command-line byte | ||||||||
| 92B6 | CMP #&26 ; Hex prefix '&'? | ||||||||
| 92B8 | BNE next_dec_char ; No: try decimal path | ||||||||
| 92BA | INY ; Yes: skip the '&' | ||||||||
| 92BB | LDA (fs_crc_lo),y ; Read first hex digit | ||||||||
| 92BD | BCS check_digit_range ; Always taken (CMP #'&' set C if A>='&'); jump into the hex digit-range check Convert to channel index | ||||||||
| 92BF | .next_hex_char←1← 92EE BCC | ||||||||
| INY ; Step to next character | |||||||||
| 92C0 | LDA (fs_crc_lo),y ; Read next hex digit candidate | ||||||||
| 92C2 | CMP #&2e ; Dot? Net.station separator | ||||||||
| 92C4 | BEQ handle_dot_sep ; Yes: switch to station-parsing mode | ||||||||
| 92C6 | CMP #&21 ; Below '!' (CR/space)? End of argument | ||||||||
| 92C8 | BCC done_parse_num ; Yes: number complete | ||||||||
| 92CA | .check_digit_range←1← 92BD BCS | ||||||||
| CMP #&30 ; Below '0'? | |||||||||
| 92CC | BCC skip_if_not_hex ; Yes: not a hex digit | ||||||||
| 92CE | CMP #&3a ; Above '9'? (CMP #':') | ||||||||
| 92D0 | BCC extract_digit_value ; No (it's '0'-'9'): straight to digit extraction Get stack pointer | ||||||||
| 92D2 | AND #&5f ; Force uppercase via AND #&5F | ||||||||
| 92D4 | ADC #&b8 ; Map 'A'-'F' to &FA-&FF (ADC #&B8 with C from earlier CMP #':' which set C) Convert to channel index | ||||||||
| 92D6 | BCS err_bad_hex ; Carry out of ADC: was below 'A' -- bad hex | ||||||||
| 92D8 | CMP #&fa ; Below &FA? (digit > 'F' overflowed past) | ||||||||
| 92DA | .skip_if_not_hex←1← 92CC BCC | ||||||||
| BCC err_bad_hex ; Yes: bad hex (out of [&FA,&FF]) | |||||||||
| 92DC | .extract_digit_value←1← 92D0 BCC | ||||||||
| AND #&0f ; Mask to nibble | |||||||||
| 92DE | STA fs_load_addr_3 ; Stash digit value in fs_load_addr_3 | ||||||||
| 92E0 | LDA fs_load_addr_2 ; Load accumulator | ||||||||
| 92E2 | CMP #&10 ; Above 16? (would overflow when shifted left 4) Transfer back to X | ||||||||
| 92E4 | BCS error_overflow ; Yes: overflow | ||||||||
| 92E6 | ASL ; Shift accumulator left 4 (multiply by 16) | ||||||||
| 92E7 | ASL ; (shift 2) | ||||||||
| 92E8 | ASL ; (shift 3) | ||||||||
| 92E9 | ASL ; (shift 4) | ||||||||
| 92EA | ADC fs_load_addr_3 ; Add new nibble | ||||||||
| 92EC | STA fs_load_addr_2 ; Save updated accumulator | ||||||||
| 92EE | BCC next_hex_char ; No carry: continue (always taken since accumulator was checked < 16 above) | ||||||||
| 92F0 | .next_dec_char←2← 92B8 BNE← 931A BNE | ||||||||
| LDA (fs_crc_lo),y ; Read next decimal-digit candidate | |||||||||
| 92F2 | CMP #&2e ; Dot? Net.station separator | ||||||||
| 92F4 | BEQ handle_dot_sep ; Yes: switch to station-parsing mode | ||||||||
| 92F6 | CMP #&21 ; Below '!' (CR/space)? | ||||||||
| 92F8 | BCC done_parse_num ; Yes: number complete | ||||||||
| 92FA | JSR is_dec_digit_only ; Test for '0'-'9' and reject '&'/'.' | ||||||||
| 92FD | BCC error_bad_number ; Not a decimal digit: bad number | ||||||||
| 92FF | AND #&0f ; Mask to nibble | ||||||||
| 9301 | STA fs_load_addr_3 ; Stash digit | ||||||||
| 9303 | ASL fs_load_addr_2 ; Accumulator * 2 | ||||||||
| 9305 | BCS error_overflow ; Overflowed: too big for byte | ||||||||
| 9307 | LDA fs_load_addr_2 ; Reload doubled value | ||||||||
| 9309 | ASL ;
|
||||||||
| 930A | BCS error_overflow ; Overflow check | ||||||||
| 930C | ASL ;
|
||||||||
| 930D | BCS error_overflow ; Overflow check | ||||||||
| 930F | ADC fs_load_addr_2 ; ul> li>accumulator (now * 8 + * 2 = * 10)/li> /ul> | ||||||||
| 9311 | BCS error_overflow ; Overflow check | ||||||||
| 9313 | ADC fs_load_addr_3 ;
|
||||||||
| 9315 | BCS error_overflow ; Overflow check | ||||||||
| 9317 | STA fs_load_addr_2 ; Save * 10 + digit | ||||||||
| 9319 | INY ; Step input cursor | ||||||||
| 931A | BNE next_dec_char ; Always taken (Y wraps at 256, never zero in practice) | ||||||||
| 931C | .done_parse_num←2← 92C8 BCC← 92F8 BCC | ||||||||
| LDA fs_work_4 ; Read mode flag | |||||||||
| 931E | BPL validate_station ; Bit 7 clear: in net.station mode -- validate result | ||||||||
| 9320 | LDA fs_load_addr_2 ; Decimal-only mode: get result | ||||||||
| 9322 | BEQ error_bad_param ; Result is zero: bad parameter | ||||||||
| 9324 | RTS ; Return with parsed result in A (decimal-only path) | ||||||||
| 9325 | .validate_station←1← 931E BPL | ||||||||
| LDA fs_load_addr_2 ; Reload result | |||||||||
| 9327 | CMP #&ff ; Station 255 is reserved (broadcast) | ||||||||
| 9329 | BEQ err_bad_station_num ; Yes: bad station number | ||||||||
| 932B | LDA fs_load_addr_2 ; Reload result for the next test | ||||||||
| 932D | BNE return_parsed ; Non-zero: valid station, return | ||||||||
| 932F | LDA fs_work_4 ; Zero result: must have followed a dot to be valid | ||||||||
| 9331 | BEQ err_bad_station_num ; No dot was seen: bad station number | ||||||||
| 9333 | DEY ; Dot seen: peek the byte before current cursor | ||||||||
| 9334 | LDA (fs_crc_lo),y ; Read previous byte | ||||||||
| 9336 | INY ; Restore Y | ||||||||
| 9337 | EOR #&2e ; Was previous char '.'? | ||||||||
| 9339 | BNE err_bad_station_num ; No: bad station number | ||||||||
| 933B | .return_parsed←1← 932D BNE | ||||||||
| SEC ; All checks passed: C=1 marks 'parsed successfully' | |||||||||
| 933C | RTS ; Return | ||||||||
| 933D | .handle_dot_sep←2← 92C4 BEQ← 92F4 BEQ | ||||||||
| LDA fs_work_4 ; Dot already seen? | |||||||||
| 933F | BNE error_bad_number ; Yes: 'Bad number' (multiple dots) | ||||||||
| 9341 | INC fs_work_4 ; Set dot-seen flag | ||||||||
| 9343 | LDA fs_load_addr_2 ; Get parsed network number (before dot) | ||||||||
| 9345 | CMP #&ff ; Network 255 is reserved | ||||||||
| 9347 | BEQ error_bad_net_num ; Yes: 'Bad network number' | ||||||||
| 9349 | RTS ; Return; caller continues parsing the station | ||||||||
Raise 'Bad hex' BRK errorLoads error code |
|
| 934A | .err_bad_hex←3← 92D6 BCS← 92DA BCC← BE9F JMP |
| LDA #&f1 ; Error code &F1 | |
| 934C | JSR error_bad_inline ; Raise 'Bad hex' error |
| 934F | EQUS "hex." |
| 9353 | .error_overflow←6← 92E4 BCS← 9305 BCS← 930A BCS← 930D BCS← 9311 BCS← 9315 BCS |
| BIT fs_work_4 ; Test fs_work_4 bit 7 | |
| 9355 | BMI error_bad_param ; Bit 7 set: redirect to error_bad_param |
| 9357 | .err_bad_station_num←3← 9329 BEQ← 9331 BEQ← 9339 BNE |
| LDA #&d0 ; A=&D0: 'Bad station' error code | |
| 9359 | JSR error_bad_inline ; Raise via error_bad_inline (never returns) |
| 935C | EQUS "station number." |
| 936B | .error_bad_number←2← 92FD BCC← 933F BNE |
| LDA #&f0 ; A=&F0: 'Bad number' error code | |
| 936D | JSR error_bad_inline ; Raise via error_bad_inline (never returns) |
| 9370 | EQUS "number." |
| 9377 | .error_bad_param←2← 9322 BEQ← 9355 BMI |
| LDA #&94 ; A=&94: 'Bad parameter' error code | |
| 9379 | JSR error_bad_inline ; Raise via error_bad_inline (never returns) |
| 937C | EQUS "parameter." |
| 9386 | .error_bad_net_num←1← 9347 BEQ |
| LDA #&d1 ; A=&D1: 'Bad net number' error code | |
| 9388 | JSR error_bad_inline ; Raise via error_bad_inline (never returns) |
| 938B | EQUS "network number." |
| fall through ↓ | |
Test for digit, '&', or '.' separatorCompares Called by
|
|||||||
| 939A | .is_decimal_digit←3← 8DAC JSR← B3C3 JSR← B59A JSR | ||||||
| CMP #&26 ; Hex prefix '&'? | |||||||
| 939C | BEQ return_from_digit_test ; Yes: treat as digit-like (carry set on exit) | ||||||
| 939E | CMP #&2e ; Network/station separator '.'? | ||||||
| 93A0 | BEQ return_from_digit_test ; Yes: also digit-like; else fall through to decimal test | ||||||
| fall through ↓ | |||||||
Test for decimal digit '0'..'9'Uses two
The net effect: carry set only for
|
|||||||
| 93A2 | .is_dec_digit_only←1← 92FA JSR | ||||||
| CMP #&3a ; Above '9'? (CMP #':') | |||||||
| 93A4 | BCS not_a_digit ; Yes: not a digit -- jump to clear-carry exit | ||||||
| 93A6 | CMP #&30 ; Below '0'? (CMP sets carry if A >= '0') | ||||||
| 93A8 | .return_from_digit_test←2← 939C BEQ← 93A0 BEQ | ||||||
| RTS ; Carry now reflects '0'-'9' membership; return | |||||||
| 93A9 | .not_a_digit←1← 93A4 BCS | ||||||
| CLC ; Out-of-range exit: clear carry to signal not-a-digit | |||||||
| 93AA | RTS ; Return | ||||||
Read and encode directory entry access byteLoads the access byte from offset &0E of the directory entry via
|
||||||
| 93AB | .get_access_bits←2← 9E0D JSR← 9E39 JSR | |||||
| LDY #&0e ; Y=&0E: directory entry access byte offset | ||||||
| 93AD | LDA (fs_options),y ; Read access byte through fs_options pointer | |||||
| 93AF | AND #&3f ; Mask to 6 protection bits (clears the unused top two) | |||||
| 93B1 | LDX #4 ; X=4: encode-table column index for owner-access bits | |||||
| 93B3 | BNE begin_prot_encode ; Always taken: LDX #4 cleared Z, so BNE is unconditional | |||||
| fall through ↓ | ||||||
Encode protection bits via lookup tableMasks
|
|||||||
| 93B5 | .get_prot_bits←2← 9D17 JSR← 9E56 JSR | ||||||
| AND #&1f ; Mask to 5 protection bits (low 5) | |||||||
| 93B7 | LDX #&ff ; X=&FF; INX inside the loop bumps to 0 for column 0 | ||||||
| 93B9 | .begin_prot_encode←1← 93B3 BNE | ||||||
| STA fs_error_ptr ; Park source bits in fs_error_ptr -- the LSR target | |||||||
| 93BB | LDA #0 ; A=0: accumulator for encoded result | ||||||
| 93BD | .loop_encode_prot←1← 93C5 BNE | ||||||
| INX ; Advance table column index | |||||||
| 93BE | LSR fs_error_ptr ; Shift next source bit into carry | ||||||
| 93C0 | BCC skip_clear_prot ; Source bit was 0: skip the OR for this column | ||||||
| 93C2 | ORA prot_bit_encode_table,x ; Source bit was 1: OR in this column's encoded mask | ||||||
| 93C5 | .skip_clear_prot←1← 93C0 BCC | ||||||
| BNE loop_encode_prot ; Continue while either fs_error_ptr or A is non-zero (loop ends when source exhausted and result still 0) | |||||||
| 93C7 | RTS ; Return with encoded value in A | ||||||
Bit-permutation table for protection / access encoding11-byte lookup table used by Two callers partition the table:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93C8 | .prot_bit_encode_table←1← 93C2 ORA | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EQUB &50 ; prot src bit 0 -> out bits 6,4 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93C9 | EQUB &20 ; prot src bit 1 -> out bit 5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93CA | EQUB &05 ; prot src bit 2 -> out bits 2,0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93CB | EQUB &02 ; prot src bit 3 -> out bit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93CC | EQUB &88 ; prot src bit 4 -> out bits 7,3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93CD | EQUB &04 ; access src bit 0 -> out bit 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93CE | EQUB &08 ; access src bit 1 -> out bit 3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93CF | EQUB &80 ; access src bit 2 -> out bit 7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93D0 | EQUB &10 ; access src bit 3 -> out bit 4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93D1 | EQUB &01 ; access src bit 4 -> out bit 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 93D2 | EQUB &02 ; access src bit 5 -> out bit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Set OS text pointer then transfer parametersStores
|
||||||
| 93D3 | .set_text_and_xfer_ptr←2← A42F JSR← B0FE JSR | |||||
| STX os_text_ptr ; Save text pointer low byte (where caller wants OS to scan from) | ||||||
| 93D5 | STY os_text_ptr_hi ; Save text pointer high byte; fall through to set_xfer_params | |||||
| fall through ↓ | ||||||
Set FS transfer byte count and source pointerStores Called by 5 sites across
|
||||||||
| 93D7 | .set_xfer_params←6← 8DA6 JSR← 8E4B JSR← 9C22 JSR← A032 JSR← A14F JSR← B118 JSR | |||||||
| STA fs_last_byte_flag ; Stash transfer byte count (in A) | ||||||||
| 93D9 | STX fs_crc_lo ; Source pointer low byte | |||||||
| 93DB | STY fs_crc_hi ; Source pointer high byte; fall through to set_options_ptr | |||||||
| fall through ↓ | ||||||||
Set FS options pointer and clear escape flagStores Called by
|
||||||
| 93DD | .set_options_ptr←2← 9EB0 JSR← BD15 JSR | |||||
| STX fs_options ; Options pointer low byte (parameter block base) | ||||||
| 93DF | STY fs_block_offset ; Options pointer high byte; fall through to clear_escapable | |||||
| fall through ↓ | ||||||
Clear bit 0 of need_release_tube preserving flagsPHP / LSR need_release_tube / PLP / RTS. Shifts bit 0 of need_release_tube into carry while clearing it, then restores the caller's flags so the operation is invisible to NZC-sensitive code. Single caller (&9B72 in the recv-and-classify reply path). |
|
| 93E1 | .clear_escapable←1← 9B72 JMP |
| PHP ; Save flags so the LSR doesn't disturb caller's NZC | |
| 93E2 | LSR need_release_tube ; Shift bit 0 of need_release_tube into carry, clearing the bit |
| 93E4 | PLP ; Restore caller's flags |
| 93E5 | RTS ; Return |
Compare 5-byte handle buffers for equalityLoops Called by
|
||||||||
| 93E6 | .cmp_5byte_handle←2← 9C85 JSR← 9D88 JSR | |||||||
| LDX #4 ; X=4: loop from offset 4 down to 1 (skips offset 0) | ||||||||
| 93E8 | .loop_cmp_handle←1← 93EF BNE | |||||||
| LDA addr_work,x ; Load saved-handle byte from addr_work[X] | ||||||||
| 93EA | EOR fs_load_addr_3,x ; EOR with parsed handle byte; Z set iff bytes match | |||||||
| 93EC | BNE return_from_cmp_handle ; Mismatch: bail out with Z clear | |||||||
| 93EE | DEX ; Decrement to next byte | |||||||
| 93EF | BNE loop_cmp_handle ; Loop while X != 0 (offset 0 is intentionally not compared) | |||||||
| 93F1 | .return_from_cmp_handle←1← 93EC BNE | |||||||
| RTS ; Return; Z reflects last EOR (set = match, clear = mismatch) | ||||||||
FSCV reason 7: report FCB handle rangeReturns the FCB handle range to the caller:
|
||||||
| 93F2 | .fscv_7_read_handles | |||||
| LDX #&20 ; X=&20: handle-table base offset | ||||||
| 93F4 | LDY #&2f ; Y=&2F: handle count + flag | |||||
| 93F6 | RTS ; Return | |||||
Set connection-active flag in channel tableSaves registers on the stack, recovers the original Called by
|
||||
| 93F7 | .set_conn_active←2← 9F35 JSR← A1AC JSR | |||
| PHP ; Save flags so the rest of the routine is transparent | ||||
| 93F8 | PHA ; Save A (the attribute byte we need to recover via stack) | |||
| 93F9 | PHX ; Save X | |||
| 93FA | TSX ; Capture S into X to address stack from below | |||
| 93FB | LDA stack_page_2,x ; Re-read the original A from stack[X+2] (above PHX/PHA) | |||
| 93FE | JSR attr_to_chan_index ; Convert attribute byte to channel-table index | |||
| 9401 | BMI clear_channel_flag ; No matching channel: skip the flag set, just restore | |||
| 9403 | LDA #&40 ; A=&40: bit 6 = connection-active mask | |||
| 9405 | ORA hazel_fcb_status,x ; OR with current status byte for this channel | |||
| 9408 | STA hazel_fcb_status,x ; Write back the updated status | |||
| 940B | BNE clear_channel_flag ; Always taken (A is non-zero after the OR with &40); join shared exit | |||
| fall through ↓ | ||||
Clear connection-active flag in channel tableMirror of
|
||||
| 940D | .clear_conn_active←2← 9F96 JSR← A1A7 JSR | |||
| PHP ; Save flags | ||||
| 940E | PHA ; Save A | |||
| 940F | PHX ; Save X | |||
| 9410 | TSX ; Capture S into X for stack-relative reads | |||
| 9411 | LDA stack_page_2,x ; Re-read the attribute byte from stack[X+2] | |||
| 9414 | JSR attr_to_chan_index ; Convert attribute to channel index | |||
| 9417 | BMI clear_channel_flag ; No matching channel: just restore | |||
| 9419 | LDA #&bf ; A=&BF: bit 6 clear mask | |||
| 941B | AND hazel_fcb_status,x | |||
| 941E | STA hazel_fcb_status,x ; Write back the updated status | |||
| 9421 | .clear_channel_flag←3← 9401 BMI← 940B BNE← 9417 BMI | |||
| PLX ; Restore X (saved at PHX) | ||||
| 9422 | PLA ; Restore A | |||
| 9423 | PLP ; Restore flags | |||
| 9424 | RTS ; Return; A and X preserved across the call | |||
Shared *Access / *Delete / *Info / *Lib command handlerCopies the command name to the TX buffer, parses a quoted
filename argument via
|
||||||
| 9425 | .cmd_fs_operation | |||||
| JSR copy_fs_cmd_name ; Copy command name 'Access'/'Delete'/'Info'/'Lib' to TX buffer | ||||||
| 9428 | PHX | |||||
| 9429 | JSR parse_quoted_arg ; Parse quoted filename argument from command line | |||||
| 942C | JSR parse_access_prefix ; Parse the access prefix (e.g. L,W,R) into a bitmask | |||||
| 942F | PLX | |||||
| 9430 | JSR check_not_ampersand ; Reject '&' character in filename | |||||
| 9433 | CMP #&0d ; End of line? | |||||
| 9435 | BNE read_filename_char ; No: copy filename chars to buffer | |||||
| fall through ↓ | ||||||
Raise 'Bad file name' BRK errorLoads error code |
|
| 9437 | .error_bad_filename←3← 944B BEQ← 953E JMP← B279 JMP |
| LDA #&cc ; Error number &CC | |
| 9439 | JSR error_bad_inline ; Raise 'Bad file name' error |
| 943C | EQUS "file name." |
| fall through ↓ | |
Reject '&' as filename characterLoads the first character from the parse buffer at Also contains
|
||||
| 9446 | .check_not_ampersand←2← 9430 JSR← 944E JSR | |||
| LDA hazel_parse_buf ; Load first parsed character | ||||
| 9449 | CMP #&26 ; Is it '&'? | |||
| 944B | BEQ error_bad_filename ; Yes: invalid filename | |||
| 944D | RTS ; Return | |||
Loop reading filename chars into TX bufferPer-character loop body of the filename-copy logic in
Three callers: the loop's own
|
|||||||||
| 944E | .read_filename_char←3← 9435 BNE← 945C BRA← 950F JMP | ||||||||
| JSR check_not_ampersand ; Reject '&' in current char | |||||||||
| 9451 | STA hazel_txcb_data,x ; Store character in TX buffer | ||||||||
| 9454 | INX ; Advance buffer pointer | ||||||||
| 9455 | CMP #&0d ; End of line? | ||||||||
| 9457 | BEQ send_fs_request ; Yes: send request to file server | ||||||||
| 9459 | JSR strip_token_prefix ; Strip BASIC token prefix byte | ||||||||
| 945C | BRA read_filename_char ; BRA back to read_filename_char | ||||||||
| fall through ↓ | |||||||||
Send FS command with no extra dispatch offsetLoads |
|
| 945E | .send_fs_request←2← 9457 BEQ← 9537 JMP |
| LDY #0 ; Y=0: ensure offset starts from beginning of TX command buffer | |
| 9460 | JMP send_cmd_and_dispatch ; Send the FS command and dispatch the reply |
Copy matched command name to TX bufferScans backwards in
|
|||||||||||||
| 9463 | .copy_fs_cmd_name←2← 9425 JSR← 94C5 JSR | ||||||||||||
| PHY ; Save Y on entry | |||||||||||||
| 9464 | .loop_scan_flag←1← 9468 BPL | ||||||||||||
| DEX ; Scan backwards in command table | |||||||||||||
| 9465 | LDA cmd_table_fs,x ; Load table byte | ||||||||||||
| 9468 | BPL loop_scan_flag ; Bit 7 clear: keep scanning | ||||||||||||
| 946A | INX ; Point past flag byte to name start | ||||||||||||
| 946B | LDY #0 ; Y=0: TX buffer offset | ||||||||||||
| 946D | .loop_copy_name←1← 9477 BNE | ||||||||||||
| LDA cmd_table_fs,x ; Load command name character | |||||||||||||
| 9470 | BMI append_space ; Bit 7 set: end of name | ||||||||||||
| 9472 | STA hazel_txcb_data,y ; Store character in TX buffer | ||||||||||||
| 9475 | INX ; Advance table pointer | ||||||||||||
| 9476 | INY ; Advance buffer pointer | ||||||||||||
| 9477 | BNE loop_copy_name ; Continue copying name | ||||||||||||
| 9479 | .append_space←1← 9470 BMI | ||||||||||||
| LDA #&20 ; Space separator | |||||||||||||
| 947B | STA hazel_txcb_data,y ; Append space after command name | ||||||||||||
| 947E | INY ; Advance buffer pointer | ||||||||||||
| 947F | TYA ; Transfer length to A | ||||||||||||
| 9480 | TAX ; And to X (buffer position) | ||||||||||||
| 9481 | PLY | ||||||||||||
| 9482 | .return_from_copy_cmd_name←1← 94B7 BEQ | ||||||||||||
| RTS ; Return | |||||||||||||
Parse possibly-quoted filename argumentReads from the command line at
|
|||||||||
| 9483 | .parse_quoted_arg←2← 9429 JSR← 94CC JSR | ||||||||
| LDA #0 ; A=0: no quote mode | |||||||||
| 9485 | TAX ; X=&00 | ||||||||
| 9486 | STA hazel_quote_mode ; Clear quote tracking flag | ||||||||
| 9489 | .loop_skip_spaces←1← 9490 BNE | ||||||||
| LDA (fs_crc_lo),y ; Load char from command line | |||||||||
| 948B | CMP #&20 ; Space? | ||||||||
| 948D | BNE check_open_quote ; No: check for opening quote | ||||||||
| 948F | INY ; Skip leading space | ||||||||
| 9490 | BNE loop_skip_spaces ; Continue skipping spaces | ||||||||
| 9492 | .check_open_quote←1← 948D BNE | ||||||||
| CMP #&22 ; Double-quote character? | |||||||||
| 9494 | BNE loop_copy_arg_char ; No: start reading filename | ||||||||
| 9496 | INY ; Skip opening quote | ||||||||
| 9497 | EOR hazel_quote_mode ; Toggle quote mode flag | ||||||||
| 949A | STA hazel_quote_mode ; Store updated quote mode | ||||||||
| 949D | .loop_copy_arg_char←2← 9494 BNE← 94B2 BNE | ||||||||
| LDA (fs_crc_lo),y ; Load char from command line | |||||||||
| 949F | CMP #&22 ; Double-quote? | ||||||||
| 94A1 | BNE store_arg_char ; No: store character as-is | ||||||||
| 94A3 | EOR hazel_quote_mode ; Toggle quote mode | ||||||||
| 94A6 | STA hazel_quote_mode ; Store updated quote mode | ||||||||
| 94A9 | LDA #&20 ; Replace closing quote with space | ||||||||
| 94AB | .store_arg_char←1← 94A1 BNE | ||||||||
| STA hazel_parse_buf,x ; Store character in parse buffer | |||||||||
| 94AE | INY ; Advance command line pointer | ||||||||
| 94AF | INX ; Advance buffer pointer | ||||||||
| 94B0 | CMP #&0d ; End of line? | ||||||||
| 94B2 | BNE loop_copy_arg_char ; No: continue parsing | ||||||||
| 94B4 | LDA hazel_quote_mode ; Check quote balance flag | ||||||||
| 94B7 | BEQ return_from_copy_cmd_name ; Balanced: return OK | ||||||||
| 94B9 | LDA brk_ptr ; Unbalanced: use BRK ptr for error | ||||||||
| 94BB | JSR error_bad_inline ; Raise 'Bad string' error | ||||||||
| 94BE | EQUS "string." ; Store to TXCB | ||||||||
| 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
|
||||
| 94C5 | .cmd_rename | |||
| JSR copy_fs_cmd_name ; Copy 'Rename ' to TX buffer | ||||
| 94C8 | PHX | |||
| 94C9 | JSR mask_owner_access ; Clear owner-only access bits before parsing | |||
| 94CC | JSR parse_quoted_arg ; Parse the quoted source filename | |||
| 94CF | JSR parse_access_prefix ; Parse access prefix on the source filename | |||
| 94D2 | PLX | |||
| 94D3 | .loop_copy_rename←1← 94F1 BRA | |||
| LDA hazel_parse_buf ; Load next parsed character | ||||
| 94D6 | CMP #&0d ; End of line? | |||
| 94D8 | BNE store_rename_char ; No: store character | |||
| 94DA | .error_bad_rename←1← 950D BNE | |||
| LDA #&b0 ; Error number &B0 | ||||
| 94DC | JSR error_bad_inline ; Raise 'Bad rename' error | |||
| 94DF | EQUS "rename." ; Add 5 for header size | |||
| 94E6 | .store_rename_char←1← 94D8 BNE | |||
| STA hazel_txcb_data,x ; Store character in TX buffer | ||||
| 94E9 | INX ; Advance buffer pointer | |||
| 94EA | CMP #&20 ; Space (name separator)? | |||
| 94EC | BEQ skip_rename_spaces ; Yes: first name complete | |||
| 94EE | JSR strip_token_prefix ; Strip BASIC token prefix byte | |||
| 94F1 | BRA loop_copy_rename ; BRA back to loop_copy_rename | |||
| 94F3 | .skip_rename_spaces←2← 94EC BEQ← 94FB BEQ | |||
| JSR strip_token_prefix ; Strip token from next char | ||||
| 94F6 | LDA hazel_parse_buf ; Load next parsed character | |||
| 94F9 | CMP #&20 ; Still a space? | |||
| 94FB | BEQ skip_rename_spaces ; Yes: skip multiple spaces | |||
| 94FD | LDA hazel_fs_lib_flags ; Save current FS options | |||
| 9500 | PHA ; Push them | |||
| 9501 | JSR mask_owner_access ; Reset access mask for second name | |||
| 9504 | PHX ; Save loop index across the access parse | |||
| 9505 | JSR parse_access_prefix ; Parse access prefix on the second filename | |||
| 9508 | PLX ; Restore loop index | |||
| 9509 | PLA ; Restore original FS options | |||
| 950A | CMP hazel_fs_lib_flags ; Options changed (cross-FS)? | |||
| 950D | BNE error_bad_rename ; Yes: error (can't rename across FS) | |||
| 950F | JMP read_filename_char ; Copy second filename and send | |||
*Dir command handlerHandles three argument syntaxes:
The cross-FS form sends a file-server selection command (code
|
||||||||||||
| 9512 | .cmd_dir | |||||||||||
| LDA (fs_crc_lo),y ; Get first char of argument | ||||||||||||
| 9514 | CMP #&26 ; Is it '&' (FS selector prefix)? | |||||||||||
| 9516 | BNE dir_pass_simple ; No: simple dir change | |||||||||||
| 9518 | INY ; Skip '&' | |||||||||||
| 9519 | LDA (fs_crc_lo),y ; Get char after '&' | |||||||||||
| 951B | CMP #&0d ; End of line? | |||||||||||
| 951D | BEQ setup_fs_root ; Yes: '&' alone (root directory) | |||||||||||
| 951F | CMP #&20 ; Space? | |||||||||||
| 9521 | BNE check_fs_dot ; No: check for '.' separator | |||||||||||
| 9523 | .setup_fs_root←1← 951D BEQ | |||||||||||
| LDY #&ff ; Y=&FF: pre-increment for loop | ||||||||||||
| 9525 | .loop_copy_fs_num←1← 952D BNE | |||||||||||
| INY ; Advance index | ||||||||||||
| 9526 | LDA (fs_crc_lo),y ; Load char from command line | |||||||||||
| 9528 | STA hazel_txcb_data,y ; Copy to TX buffer | |||||||||||
| 952B | CMP #&26 ; Is it '&' (end of FS path)? | |||||||||||
| 952D | BNE loop_copy_fs_num ; No: keep copying | |||||||||||
| 952F | LDA #&0d ; Replace '&' with CR terminator | |||||||||||
| 9531 | STA hazel_txcb_data,y ; Store CR in buffer | |||||||||||
| 9534 | INY ; Point past CR | |||||||||||
| 9535 | TYA ; Transfer length to A | |||||||||||
| 9536 | TAX ; And to X (byte count) | |||||||||||
| 9537 | JMP send_fs_request ; Send directory request to server | |||||||||||
| 953A | .check_fs_dot←1← 9521 BNE | |||||||||||
| CMP #&2e ; Is char after '&' a dot? | ||||||||||||
| 953C | BEQ parse_fs_dot_dir ; Yes: &FS.dir format | |||||||||||
| 953E | JMP error_bad_filename ; No: invalid syntax | |||||||||||
| 9541 | .parse_fs_dot_dir←1← 953C BEQ | |||||||||||
| INY ; Skip '.' | ||||||||||||
| 9542 | STY fs_load_addr ; Save dir path start position | |||||||||||
| 9544 | LDA #4 ; FS command 4: examine directory | |||||||||||
| 9546 | STA hazel_txcb_data ; Store in TX buffer | |||||||||||
| 9549 | LDA hazel_fs_lib_flags ; Load FS flags | |||||||||||
| 954C | ORA #&40 ; Set bit 6 (FS selection active) | |||||||||||
| 954E | STA hazel_fs_lib_flags ; Store updated flags | |||||||||||
| 9551 | LDX #1 ; X=1: buffer offset | |||||||||||
| 9553 | JSR copy_arg_validated ; Copy FS number to buffer | |||||||||||
| 9556 | LDY #&12 ; Y=&12: select FS command code | |||||||||||
| 9558 | JSR save_net_tx_cb ; Send FS selection command | |||||||||||
| 955B | LDA hazel_txcb_data ; Load reply status | |||||||||||
| 955E | CMP #2 ; Status 2 (found)? | |||||||||||
| 9560 | BEQ dir_found_send ; Yes: proceed to dir change | |||||||||||
| 9562 | LDA #&d6 ; Error number &D6 | |||||||||||
| 9564 | JSR error_inline_log ; Raise 'Not found' error | |||||||||||
| 9567 | EQUS "Not found." ; Store null terminator (A=0 from EOR) Get message length Go to error dispatch | |||||||||||
| 9571 | .dir_found_send←1← 9560 BEQ | |||||||||||
| LDA hazel_fs_context_copy ; Load current FS station byte | ||||||||||||
| 9574 | STA hazel_txcb_data ; Store in TX buffer | |||||||||||
| 9577 | LDX #1 ; X=1: buffer offset | |||||||||||
| 9579 | LDY #7 ; Y=7: change directory command code | |||||||||||
| 957B | JSR save_net_tx_cb ; Send directory change request | |||||||||||
| 957E | LDX #1 ; X=1 | |||||||||||
| 9580 | STX hazel_txcb_data ; Store start marker in buffer | |||||||||||
| 9583 | STX hazel_txcb_flag ; Store start marker in buffer+1 | |||||||||||
| 9586 | INX ; Non-zero: commit state and return | |||||||||||
| 9587 | LDY fs_load_addr ; Restore dir path start position | |||||||||||
| 9589 | JSR copy_arg_validated ; Copy directory path to buffer | |||||||||||
| 958C | LDY #6 ; Y=6: set directory command code | |||||||||||
| 958E | JSR save_net_tx_cb ; Send set directory command | |||||||||||
| 9591 | LDY hazel_txcb_data ; Load reply handle | |||||||||||
| 9594 | JMP fsreply_3_set_csd ; Select FS and return | |||||||||||
| 9597 | .dir_pass_simple←1← 9516 BNE | |||||||||||
| JMP check_urd_prefix ; Simple: pass command to FS | ||||||||||||
| 959A | .print_fs_ps_help | |||||||||||
| LDA (os_text_ptr),y ; Read first command-line char at (os_text_ptr),Y | ||||||||||||
| 959C | CMP #&0d ; Is it CR (no argument supplied)? | |||||||||||
| 959E | BNE dispatch_fs_ps_with_arg ; Non-CR: argument present -- exit via dispatch_fs_ps_with_arg (X=&A0) | |||||||||||
| 95A0 | JSR print_fs_station ; CR: print 'FS ' header | |||||||||||
| 95A3 | JSR print_dir_syntax ; Print '[ |
|||||||||||
| 95A6 | JSR print_station_low ; Print 'PS ' header | |||||||||||
| 95A9 | JSR print_dir_syntax ; Print '[ |
|||||||||||
| 95AC | JSR print_inline ; Print final 'Space\rNoSpace\r' lines | |||||||||||
| 95AF | EQUS "Space.NoSpace." | |||||||||||
| 95BD | NOP ; NOP -- bit-7 terminator + resume opcode for the preceding inline string | |||||||||||
| 95BE | .bra_target_svc_return←1← 9617 BRA | |||||||||||
| JMP svc_return_unclaimed ; JMP to svc_return_unclaimed (long-distance via this 3-byte trampoline) | ||||||||||||
Print 'PS ' 9-column headerCalls |
|
| 95C1 | .print_station_low←2← 95A6 JSR← 963C JSR |
| JSR print_inline ; Print 'P' prefix | |
| 95C4 | EQUS "P" |
| 95C5 | CLV ; CLV -- bit-7 terminator + resume (V flag is irrelevant here, used as 1-byte resume opcode) |
| 95C6 | BVC print_field_tail_s ; BVC: V was just cleared -> always taken; falls into the shared 'S ' tail at &95CD |
| fall through ↓ | |
Print 'FS ' 9-column headerCalls |
|
| 95C8 | .print_fs_station←2← 95A0 JSR← 9636 JSR |
| JSR print_inline ; Print 'F' prefix | |
| 95CB | EQUS "F" |
| 95CC | NOP ; NOP -- bit-7 terminator; falls through into the shared 'S ' tail at &95CD |
| 95CD | .print_field_tail_s←1← 95C6 BVC |
| JSR print_inline ; Print 'S ' (S + 7 spaces) -- the shared 8-char field used by both 'FS' and 'PS' callers | |
| 95D0 | EQUS "S " |
| 95D8 | NOP ; Bit-7 terminator |
| 95D9 | RTS ; Return |
Print '[
|
|
| 95DA | .print_dir_syntax←2← 95A3 JSR← 95A9 JSR |
| JSR print_inline ; Print '[D>.]D>\r' (file-name syntax fragment, shared between *FS/*PS no-arg help and *Dir) | |
| 95DD | EQUS "[<D>.]<D>." |
| 95E7 | NOP ; Bit-7 terminator |
| 95E8 | RTS ; Return |
| 95E9 | .dispatch_fs_ps_with_arg←1← 959E BNE |
| LDX #&a0 ; X=&A0: index into svc4 dispatch table (no-arg path) | |
| 95EB | JMP svc4_dispatch_lookup ; Tail-jump to svc4_dispatch_lookup with X=&A0 |
Write FS/PS station+network to Master 128 CMOS RAMReached via PHA/PHA/RTS dispatch from cmd_table_fs sub-table 4
( The flag byte's low 6 bits (
Pre-reads existing CMOS[idx] and CMOS[idx+1] into Writes the station via
|
||||||||||||||||
| 95EE | .set_fs_or_ps_cmos_station | |||||||||||||||
| LDA cmd_table_fs,x ; Read flag byte for matched cmd entry (syntax idx in bits 0..4) | ||||||||||||||||
| 95F1 | AND #&3f ; Mask off end-marker (bit 7) and V-if-no-arg flag (bit 6) | |||||||||||||||
| 95F3 | TAX ; X = CMOS byte index (1=FS stn, 3=PS stn) | |||||||||||||||
| 95F4 | PHX ; Save CMOS index | |||||||||||||||
| 95F5 | PHY ; Save caller's command-line cursor | |||||||||||||||
| 95F6 | PHX ; Save CMOS index again (consumed by first PLX below) | |||||||||||||||
| 95F7 | JSR osbyte_a1 ; Read existing CMOS[idx] (current station) | |||||||||||||||
| 95FA | STY fs_work_5 ; Default station if user gives no args | |||||||||||||||
| 95FC | PLX ; Recover CMOS index from stack | |||||||||||||||
| 95FD | INX ; X+=1: advance to network byte | |||||||||||||||
| 95FE | JSR osbyte_a1 ; Read existing CMOS[idx+1] (current network) | |||||||||||||||
| 9601 | STY fs_work_6 ; Default network if user gives no args | |||||||||||||||
| 9603 | PLY ; Restore command-line cursor | |||||||||||||||
| 9604 | JSR parse_fs_ps_args ; Parse 'net>.stn>'; updates fs_work_5/6/7 if args present | |||||||||||||||
| 9607 | PLX ; Recover CMOS index from stack | |||||||||||||||
| 9608 | PHX ; Re-save CMOS index for second write | |||||||||||||||
| 9609 | LDY fs_work_5 ; Y = station (parsed or pre-read default) | |||||||||||||||
| 960B | JSR osbyte_a2 ; Write CMOS[idx] = station | |||||||||||||||
| 960E | PLX ; Recover CMOS index from stack | |||||||||||||||
| 960F | INX ; X+=1: advance to network byte | |||||||||||||||
| 9610 | LDY fs_work_7 ; Y = raw parsed network (NOT canonical fs_work_6); fall through into osbyte_a2 to write CMOS[idx+1] | |||||||||||||||
| fall through ↓ | ||||||||||||||||
OSBYTE &A2 (write Master CMOS RAM byte)Three instructions:
Callers:
|
||||||
| 9612 | .osbyte_a2←3← 960B JSR← 962E BRA← A0FE JSR | |||||
| LDA #osbyte_write_cmos_ram ; A=&A2: write CMOS RAM byte via OSBYTE | ||||||
| 9614 | JSR osbyte ; Master and Compact: Write to CMOS RAM/EEPROM byte X with value Y | |||||
| 9617 | BRA bra_target_svc_return ; BRA -91 -> bra_target_svc_return | |||||
| fall through ↓ | ||||||
*Space command: enable space-remaining displayReached via the |
|
| 9619 | .cmd_space |
| LDX #&11 ; X=&11: CMOS RAM byte index | |
| 961B | JSR osbyte_a1 ; Read CMOS &11 via osbyte_a1 |
| 961E | TYA ; A = current CMOS &11 value |
| 961F | ORA #1 ; Set bit 0 in A |
| 9621 | BRA osbyte_a2_value_tya ; BRA osbyte_a2_value_tya: shared write-back tail |
| fall through ↓ | |
*NoSpace command: disable space-remaining displayReached via the |
|
| 9623 | .cmd_nospace |
| LDX #&11 ; X=&11: CMOS RAM byte index | |
| 9625 | JSR osbyte_a1 ; Read CMOS &11 via osbyte_a1 |
| 9628 | TYA ; A = current CMOS &11 value |
| 9629 | AND #&fe ; Clear bit 0 in A |
| fall through ↓ | |
Shared CMOS write-back tailCommon tail used by |
|
| 962B | .osbyte_a2_value_tya←1← 9621 BRA |
| TAY ; New CMOS value to Y | |
| 962C | LDX #&11 ; X=&11: CMOS RAM byte index |
| 962E | BRA osbyte_a2 ; BRA osbyte_a2: write CMOS &11 = Y |
| fall through ↓ | |
Service &29: *STATUS handlerReached via |
|
| 9630 | .svc_29_status |
| LDA (os_text_ptr),y ; Read first command-line char | |
| 9632 | CMP #&0d ; Is it CR (no argument)? |
| 9634 | BNE help_dispatch_setup ; Non-CR: parse the argument at help_dispatch_setup |
| 9636 | JSR print_fs_station ; Print 'FS ' header |
| 9639 | JSR print_fs_address ; Print FS network.station from CMOS &02/&01 |
| 963C | JSR print_station_low ; Print 'PS ' header |
| 963F | JSR print_ps_address ; Print PS network.station from CMOS &04/&03 |
| 9642 | LDX #&11 ; X=&11: CMOS RAM byte index |
| 9644 | JSR osbyte_a1 ; Read CMOS &11 (FS state) |
| 9647 | TYA ; A = CMOS &11 |
| 9648 | AND #1 ; Mask bit 0 (FS-active flag) |
| 964A | BNE parse_object_space_print ; Bit set: skip 'No ' prefix |
| 964C | JSR print_inline ; Print 'No ' prefix via inline |
| 964F | EQUS "No " |
| 9652 | NOP ; Bit-7 terminator + resume |
| 9653 | .parse_object_space_print←1← 964A BNE |
| JSR print_inline ; Print 'Space ' or similar via inline | |
| 9656 | EQUS "Space." |
| 965C | CLV ; Bit-7 terminator + resume opcode |
| 965D | BVC print_cmos_done ; ALWAYS branch |
Print printer-server address from CMOSReads the printer-server's saved network number from CMOS byte
&04, prints it as decimal (no leading zeros), prints a |
|
| 965F | .print_ps_address←1← 963F JSR |
| LDX #4 ; X=4: CMOS RAM byte 4 (network number) | |
| 9661 | JSR osbyte_a1 ; Read CMOS &04 via osbyte_a1 |
| 9664 | TYA ; A = CMOS &04 value |
| 9665 | JSR print_num_no_leading ; Print as decimal (no leading zeros) |
| 9668 | JSR print_inline ; Print '.' separator via inline |
| 966B | EQUS "." |
| 966C | LDX #3 ; X=3: CMOS &03 (PS station) |
| 966E | BRA print_cmos_decimal_nl ; BRA print_cmos_decimal_nl: shared print-and-trail |
| fall through ↓ | |
Print file-server address from CMOSReads the file-server's saved network number from CMOS byte
&02, prints it as decimal (no leading zeros), prints a |
|
| 9670 | .print_fs_address←1← 9639 JSR |
| LDX #2 ; X=2: CMOS &02 (FS network) | |
| 9672 | JSR osbyte_a1 ; Read CMOS &02 via osbyte_a1 |
| 9675 | TYA ; A = CMOS &02 |
| 9676 | JSR print_num_no_leading ; Print as decimal |
| 9679 | JSR print_inline ; Print '.' separator via inline |
| 967C | EQUS "." |
| 967D | LDX #1 ; X=1: CMOS &01 (port) |
| 967F | .print_cmos_decimal_nl←1← 966E BRA |
| JSR osbyte_a1 ; Read CMOS X via osbyte_a1 | |
| 9682 | TYA ; A = CMOS value |
| 9683 | JSR print_num_no_leading ; Print as decimal |
| 9686 | JSR osnewl ; Write newline (characters 10 and 13) |
| 9689 | .print_cmos_done←1← 965D BVC |
| JMP svc_return_unclaimed ; JMP svc_return_unclaimed (release service call) | |
| 968C | .help_dispatch_setup←1← 9634 BNE |
| LDX #&bd ; X=&BD: setup index for the dispatch chain | |
| fall through ↓ | |
Dispatch *HELP-style argument via svc4_dispatch_lookup3-byte trampoline: |
|
| 968E | .dispatch_help_command |
| JMP svc4_dispatch_lookup ; JMP svc4_dispatch_lookup -- shared parser dispatch | |
| 9691 | EQUS "!Help." ; '!Help.' prefix bytes (not used by the matcher; may be visible as a fallback help-message head) |
| 9697 | .on_suffix_pattern←1← 96A9 EOR |
| EQUS "ON " ; 'ON ' -- 3-char pattern read by match_on_suffix at &969A via EOR &9697,X with X=0..2 to detect '... ON ' help-line suffix | |
| 969A | .match_on_suffix |
| PHY | |
| 969B | LDA os_text_ptr ; Copy os_text_ptr lo to work_ae |
| 969D | STA work_ae ; Store -> work_ae |
| 969F | LDA os_text_ptr_hi ; Copy os_text_ptr hi |
| 96A1 | STA addr_work ; Store -> addr_work |
| 96A3 | PLY ; Restore caller Y |
| 96A4 | PHY ; Save Y again (preserve across loop) |
| 96A5 | LDX #0 ; X=0: pattern offset starts at 0 |
| 96A7 | .loop_match_on_suffix←1← 96B6 BCC |
| LDA (work_ae),y ; Read text byte at (work_ae)+Y | |
| 96A9 | EOR on_suffix_pattern,x |
| 96AC | AND #&5f ; Mask bit 5 -- case-insensitive comparison |
| 96AE | BEQ match_char_found ; Equal: continue checking pattern |
| 96B0 | .match_char_loop_cmp←2← 96C2 BEQ← 96D1 BEQ |
| PLY | |
| 96B1 | RTS ; Return (no match) |
| 96B2 | .match_char_found←1← 96AE BEQ |
| INY ; Advance text index | |
| 96B3 | INX ; Advance pattern index |
| 96B4 | CPX #3 ; Done all 3 chars? |
| 96B6 | BCC loop_match_on_suffix ; No: continue |
| 96B8 | PHY ; Match: save Y |
| 96B9 | JSR ensure_fs_selected ; Ensure NFS is selected (auto-select if needed) |
| 96BC | .match_char_process←2← 970D BEQ← 971C BRA |
| PLY | |
| 96BD | .loop_skip_non_spaces←1← 96C6 BNE |
| INY ; Advance Y to next char | |
| 96BE | LDA (work_ae),y ; Read text byte at (work_ae)+Y |
| 96C0 | CMP #&0d ; Is it CR (end-of-line)? |
| 96C2 | BEQ match_char_loop_cmp ; Yes: nothing to load -> return |
| 96C4 | CMP #&20 ; Is it space? |
| 96C6 | BNE loop_skip_non_spaces ; No: continue scanning past non-space |
| 96C8 | .loop_help_skip_spaces←1← 96CD BEQ |
| INY ; Skip space char | |
| 96C9 | LDA (work_ae),y ; Read next byte |
| 96CB | CMP #&20 ; Is it space? |
| 96CD | BEQ loop_help_skip_spaces ; Yes: keep skipping spaces |
| 96CF | CMP #&0d ; Is it CR? |
| 96D1 | BEQ match_char_loop_cmp ; Yes: nothing past spaces -> return |
| 96D3 | STY hazel_txcb_data ; Save Y as hazel_txcb_data (cmd buffer ptr) |
| 96D6 | STY hazel_txcb_flag ; Save Y as hazel_txcb_flag (cmd flag) |
| 96D9 | LDX #1 ; X=1: index for template walk |
| 96DB | .loop_copy_command_suffix←1← 96E4 BNE |
| INX ; Advance template index | |
| 96DC | LDA help_topic_template,x ; Read template byte from help_topic_template+X |
| 96DF | STA hazel_txcb_data,x ; Store at hazel_txcb_data+X |
| 96E2 | CMP #&2e ; Compare with '.' (template terminator) |
| 96E4 | BNE loop_copy_command_suffix ; Not '.': continue copying template |
| 96E6 | PHY ; Save text-buffer index |
| 96E7 | .loop_copy_topic_name←1← 96F4 BNE |
| INX ; Advance dest index | |
| 96E8 | LDA (work_ae),y ; Read topic char at (work_ae),Y |
| 96EA | INY ; Advance source |
| 96EB | .loop_store_topic_char←1← 96F8 BRA |
| STA hazel_txcb_data,x ; Store at hazel_txcb_data+X | |
| 96EE | CMP #&0d ; CR? (end of name) |
| 96F0 | BEQ start_help_file_load ; Yes: take start_help_file_load path (open file) |
| 96F2 | CMP #&20 ; Space? (terminator) |
| 96F4 | BNE loop_copy_topic_name ; No: continue copying |
| 96F6 | LDA #&0d ; A=&0D: replace space with CR |
| 96F8 | BRA loop_store_topic_char ; BRA back to store the CR |
| 96FA | .start_help_file_load←1← 96F0 BEQ |
| INX ; Account for last char | |
| 96FB | LDA hazel_fs_lib_flags ; Read fs_lib_flags (hazel_fs_lib_flags) |
| 96FE | AND #&3f ; Preserve low bits, clear high bits |
| 9700 | ORA #&80 ; Set bit 7 (load-pending flag) |
| 9702 | STA hazel_fs_lib_flags ; Store back to fs_lib_flags |
| 9705 | LDA #&40 ; A=&40: load mode flag |
| 9707 | STA fs_last_byte_flag ; Store as fs_last_byte_flag |
| 9709 | JSR send_open_file_request ; Open the help-topic file |
| 970C | TAY ; Y=file handle |
| 970D | BEQ match_char_process ; Y=0: open failed -> return |
| 970F | .loop_print_help_byte←2← 972C BRA← 973B BRA |
| JSR osbget ; Read a single byte from an open file Y | |
| 9712 | BCC help_print_start ; C clear: byte read OK -> print it |
| 9714 | LDA #osfind_close ; A=0: OSFIND close mode |
| 9716 | JSR osfind ; Close one or all files |
| 9719 | JSR osnewl ; Write newline (characters 10 and 13) |
| 971C | BRA match_char_process ; BRA back to match_char_process (return) |
| 971E | .help_print_start←2← 9712 BCC← 9736 BNE |
| BIT escape_flag | |
| 9720 | BPL help_print_char_check ; Bit 7 clear: not escaping, continue |
| 9722 | JMP escape_error_close ; Escape: jump to error path escape_error_close |
| 9725 | .help_print_char_check←1← 9720 BPL |
| CMP #&0d ; Compare with CR | |
| 9727 | BEQ handle_help_paged_mode ; Z: CR -- handle line-end (newline) |
| 9729 | JSR oswrch ; Write character |
| 972C | BRA loop_print_help_byte ; BRA back to read next byte |
| 972E | .handle_help_paged_mode←1← 9727 BEQ |
| PHY ; Save file handle | |
| 972F | LDA #&da ; A=&DA: OSBYTE &DA = read paged-mode flag |
| 9731 | JSR osbyte_x0 ; Issue OSBYTE &DA (X=0) |
| 9734 | PLY ; Restore handle |
| 9735 | TXA ; Result to A |
| 9736 | BNE help_print_start ; Non-zero: paged mode pending -> handle Escape |
| 9738 | JSR osnewl ; Write newline (characters 10 and 13) |
| 973B | BRA loop_print_help_byte ; BRA back to read next byte |
| fall through ↓ | |
Set up open receive for FS reply on port &90Loads |
|
| 973D | .init_txcb_bye←1← 97CE JSR |
| LDA #&90 ; A=&90: bye command port | |
| fall through ↓ | |
Create open receive control block on specified portCalls
|
||||
| 973F | .init_txcb_port←1← 9DCD JSR | |||
| JSR init_txcb ; Initialise TXCB from template | ||||
| 9742 | STA txcb_port ; Set transmit port | |||
| 9744 | LDA #3 ; A=3: data start offset | |||
| 9746 | STA txcb_start ; Set TXCB start offset | |||
| 9748 | DEC txcb_ctrl ; Open receive: &80->&7F (bit 7 clear = awaiting reply) | |||
| 974A | RTS ; Return | |||
Initialise TX control block from ROM templateCopies 12 bytes from Called by 4 sites including
|
||||||
| 974B | .init_txcb←5← 8E15 JSR← 973F JSR← 97BD JSR← AC55 LDA← BCCB JSR | |||||
| PHA ; Save A | ||||||
| 974C | LDY #&0b ; Y=&0B: template size - 1 | |||||
| 974E | .loop_init_txcb←1← 975F BPL | |||||
| LDA txcb_init_template,y ; Load byte from TXCB template | ||||||
| 9751 | STA txcb_ctrl,y ; Store to TXCB workspace | |||||
| 9754 | CPY #2 ; Index >= 2? | |||||
| 9756 | BPL skip_txcb_dest ; Yes: skip dest station copy | |||||
| 9758 | LDA hazel_fs_station,y ; Load dest station byte | |||||
| 975B | STA txcb_dest,y ; Store to TXCB destination | |||||
| 975E | .skip_txcb_dest←1← 9756 BPL | |||||
| DEY ; Decrement index | ||||||
| 975F | BPL loop_init_txcb ; More bytes: continue | |||||
| 9761 | PLA ; Restore A | |||||
| 9762 | RTS ; Return | |||||
TXCB initialisation template (12 bytes)Copied byte-for-byte by Bytes 2 and 3 (placeholders The |
|
| 9763 | .txcb_init_template←1← 974E LDA |
| EQUB &80 ; Offset 0: txcb_ctrl = &80 (TX command) | |
| 9764 | EQUB &99 ; Offset 1: txcb_port = &99 (FS command port) |
| 9765 | EQUB &00 ; Offset 2: txcb_dest lo placeholder (overwritten with hazel_fs_station[0]) |
| 9766 | EQUB &00 ; Offset 3: txcb_dest hi placeholder (overwritten with hazel_fs_station[1]) |
| 9767 | EQUB &00 ; Offset 4: txcb_start lo = 0 |
| 9768 | EQUB &C1 ; Offset 5: txcb_start hi = &C1 (data buffer starts at &C100 in HAZEL) |
| 9769 | .always_set_v_byte←21← 8C67 BIT← 91FC BIT← 993D BIT← 9A6A BIT← 9E2E BIT← 9FF0 BIT← A3BB BIT← A4B8 BIT← A65A BIT← A685 BIT← A6BC BIT← AE02 BIT← B327 BIT← B3E3 BIT← B563 BIT← B5C3 BIT← B604 BIT← B694 BIT← B8F9 BIT← B937 BIT← BC22 BIT |
| EQUB &FF ; Offset 6: padding &FF; doubles as the always_set_v_byte BIT $abs target | |
| 976A | .bit_test_ff |
| EQUB &FF ; Offset 7: txcb_pos = &FF (also labelled bit_test_ff) | |
| 976B | EQUB &FF ; Offset 8: txcb_end lo = &FF |
| 976C | EQUB &C1 ; Offset 9: txcb_end hi = &C1 (buffer end &C1FF) |
| 976D | EQUB &FF ; Offset 10: extended-addr fill (&FF) |
| 976E | EQUB &FF ; Offset 11: extended-addr fill (&FF) |
Send read-only FS request (carry set)Pushes
|
||||||
| 976F | .send_request_nowrite←1← A231 JSR | |||||
| PHA ; Save A | ||||||
| 9770 | SEC ; Set carry (read-only mode) | |||||
| 9771 | BCS txcb_copy_carry_set ; ALWAYS branch | |||||
Send read-write FS request (V clear)Clears
|
||||||
| 9773 | .send_request_write←2← 9C45 JSR← 9CF9 JSR | |||||
| CLV ; Clear V | ||||||
| 9774 | 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. |
|
| 9776 | .cmd_bye |
| LDY #0 ; Y=0: process_all_fcbs filter (0 = all FCBs) | |
| 9778 | JSR process_all_fcbs ; Walk all 16 FCB slots, calling start_wipe_pass on each |
| 977B | LDA #osbyte_close_spool_exec ; OSBYTE &77 = close *SPOOL and *EXEC files |
| 977D | JSR osbyte ; Close any open *SPOOL/*EXEC handles |
| 9780 | LDA #&40 ; A=&40: bit 6 of fs_flags = 'FS in active session' |
| 9782 | TRB fs_flags ; Clear bit 6: mark FS session inactive |
| 9785 | JSR close_all_net_chans ; Close every Econet client channel |
| 9788 | LDY #&17 ; Y=&17: FS function code 'Bye' (logoff request) |
| fall through ↓ | |
Save FS state and send command to file serverCopies station address and function code (
|
|||||||||
| 978A | .save_net_tx_cb←25← 8E3C JSR← 9558 JSR← 957B JSR← 958E JSR← 9E4A JSR← 9F2F JSR← 9F3F JSR← 9F8D JSR← A018 JSR← A091 JSR← A0C5 JSR← A199 JSR← A1BC JSR← A28C JSR← A347 JSR← A50B JSR← A533 JSR← A895 JSR← B0D2 JMP← B150 JSR← B18E JSR← B1FD JSR← B71B JSR← B7B2 JSR← BCAA JSR | ||||||||
| CLV ; Clear V: standard send mode (callers set V via save_net_tx_cb_vset for the lib-flag variant) | |||||||||
| fall through ↓ | |||||||||
Save and send TXCB with V flag setVariant of Called by
|
|||||||||||
| 978B | .save_net_tx_cb_vset←2← 9E31 JSR← 9FF6 JSR | ||||||||||
| LDA hazel_fs_saved_station ; Read FS station from &C002 (saved from selection time) | |||||||||||
| 978E | STA hazel_txcb_station ; Copy into TX buffer at &C102 (dest station for header) | ||||||||||
| 9791 | .txcb_copy_carry_clr←1← 9774 BVC | ||||||||||
| CLC ; Clear C: caller wants four-way handshake (not disconnect) | |||||||||||
| 9792 | .txcb_copy_carry_set←1← 9771 BCS | ||||||||||
| PHP ; Save flags so we can keep V across the loop | |||||||||||
| 9793 | STY hazel_txcb_func_code ; Save Y -- the entry function code -- into TX[1] | ||||||||||
| 9796 | LDY #1 ; Y=1: copy 2 bytes (network/control) starting at index 1 | ||||||||||
| 9798 | .loop_copy_vset_stn←1← 979F BPL | ||||||||||
| LDA hazel_fs_context_copy,y ; Read source byte at &C003+Y | |||||||||||
| 979B | STA hazel_txcb_network,y ; Write to TX buffer at &C103+Y | ||||||||||
| 979E | DEY ; Step backwards | ||||||||||
| 979F | BPL loop_copy_vset_stn ; Loop while Y >= 0 (covers indices 1, 0) | ||||||||||
| 97A1 | BIT hazel_fs_lib_flags ; Test fs_lib_flags: bit 6 = use library, bit 7 = *-prefix-stripped | ||||||||||
| 97A4 | BVS use_lib_station ; V (bit 6) set: use the library station instead | ||||||||||
| 97A6 | BPL done_vset_station ; Neither bit set: leave the FS station copy intact | ||||||||||
| 97A8 | LDA hazel_fs_prefix_stn ; Bit 7 (FS-prefix) set: substitute the saved-prefix station from &C004 | ||||||||||
| 97AB | STA hazel_txcb_network ; Override TX[3]'s station byte | ||||||||||
| 97AE | BVC done_vset_station ; Always taken: V was clear when we entered (BVS at &97A4 didn't fire) | ||||||||||
| 97B0 | .use_lib_station←1← 97A4 BVS | ||||||||||
| LDA hazel_fs_saved_station ; use_lib_station: substitute the library station from &C002 (the original FS station, but bit 6 of fs_lib_flags redirects via lib path) | |||||||||||
| 97B3 | STA hazel_txcb_network ; Override TX[3] with the library station byte | ||||||||||
| 97B6 | .done_vset_station←2← 97A6 BPL← 97AE BVC | ||||||||||
| PLP ; Restore the saved flags (V/C control downstream init_txcb behaviour) | |||||||||||
| fall through ↓ | |||||||||||
Build TXCB from scratch, send, and receive replyFull send/receive cycle comprising two separate Econet transactions:
|
|||||||||||||||||
| 97B7 | .prep_send_tx_cb←1← A2E5 JSR | ||||||||||||||||
| PHP ; Save flags so C survives the init_txcb call | |||||||||||||||||
| 97B8 | LDA #&90 ; Reply port = &90 (FS reply port) | ||||||||||||||||
| 97BA | STA hazel_txcb_port ; Stash port in TXCB[0] | ||||||||||||||||
| 97BD | JSR init_txcb ; Build the rest of the TXCB (control, dest stn/net, etc.) | ||||||||||||||||
| 97C0 | TXA ; Move TX-buffer end pointer (returned in X) into A | ||||||||||||||||
| 97C1 | ADC #5 ; Add 5 bytes of slack for trailing reply data | ||||||||||||||||
| 97C3 | STA txcb_end ; Stash the resulting end-of-buffer offset | ||||||||||||||||
| 97C5 | PLP ; Restore the original C flag from caller | ||||||||||||||||
| 97C6 | BCS handle_disconnect ; C set: this is a disconnect; jump to handle_disconnect | ||||||||||||||||
| 97C8 | PHP ; Save flags again across the actual TX (TX clobbers them) | ||||||||||||||||
| 97C9 | JSR init_tx_ptr_and_send ; Send the four-way-handshake-initiated command packet | ||||||||||||||||
| 97CC | PLP ; Restore caller's flags before falling into recv_and_process_reply | ||||||||||||||||
| fall through ↓ | |||||||||||||||||
Receive FS reply and dispatch on status codesWaits for a server-initiated reply transaction. After the
command TX completes (a separate client-initiated four-way
handshake), calls Iterates over reply bytes:
Handles disconnect requests (
|
|||||||||||||||
| 97CD | .recv_and_process_reply←2← 9D0C JSR← A285 JSR | ||||||||||||||
| PHP ; Save flags so caller's V/C survive the receive | |||||||||||||||
| 97CE | JSR init_txcb_bye ; Set up open RX on port &90 for the FS reply (TXCB[0] = &90, ctrl = &7F) | ||||||||||||||
| 97D1 | JSR wait_net_tx_ack ; Wait for the reply via the 3-level stack timer | ||||||||||||||
| 97D4 | PLP ; Restore caller's flags | ||||||||||||||
| 97D5 | .loop_next_reply←1← 97E9 BCC | ||||||||||||||
| INY ; Step Y to next reply byte | |||||||||||||||
| 97D6 | LDA (txcb_start),y ; Read reply byte at txcb_start+Y | ||||||||||||||
| 97D8 | TAX ; Stash for the dispatch tests below | ||||||||||||||
| 97D9 | BEQ return_from_recv_reply ; Zero terminates: return | ||||||||||||||
| 97DB | BVC process_reply_code ; V clear (caller's V): use code as-is | ||||||||||||||
| 97DD | ADC #&2a ; V set: shift the code by +&2A (extended-error mapping) | ||||||||||||||
| 97DF | .process_reply_code←1← 97DB BVC | ||||||||||||||
| BNE store_reply_status ; Non-zero: dispatch as an error | |||||||||||||||
| 97E1 | .return_from_recv_reply←1← 97D9 BEQ | ||||||||||||||
| RTS ; Return | |||||||||||||||
| 97E2 | .handle_disconnect←1← 97C6 BCS | ||||||||||||||
| PLA ; Pull caller's pushed return state | |||||||||||||||
| 97E3 | LDX #&c0 ; X=&C0: 'remote disconnect' status | ||||||||||||||
| 97E5 | INY ; Step Y past the disconnect byte | ||||||||||||||
| 97E6 | JSR send_disconnect_reply ; Send disconnect notification to remote | ||||||||||||||
| 97E9 | BCC loop_next_reply ; C clear (success): continue scanning replies | ||||||||||||||
| 97EB | .store_reply_status←1← 97DF BNE | ||||||||||||||
| STX hazel_fs_last_error ; Save the error code into &C009 | |||||||||||||||
| 97EE | LDA hazel_fs_pending_state ; Read FS state byte at &C007 | ||||||||||||||
| 97F1 | PHP ; Save flags so we can branch later | ||||||||||||||
| 97F2 | BNE check_data_loss ; FS state non-zero: data-loss check needed | ||||||||||||||
| 97F4 | CPX #&bf ; Reply was &BF (special: not a real error)? | ||||||||||||||
| 97F6 | BNE build_error_block ; No: build error block | ||||||||||||||
| 97F8 | .check_data_loss←1← 97F2 BNE | ||||||||||||||
| LDA #&40 ; A=&40: 'channel-active' bitmask | |||||||||||||||
| 97FA | PHA ; Push it onto the OR-accumulator | ||||||||||||||
| 97FB | TRB fs_flags ; Clear the FS-active bit (we're losing the connection) | ||||||||||||||
| 97FE | LDX #&f0 ; X=&F0: scan from channel offset &F0 upwards | ||||||||||||||
| 9800 | .loop_scan_channels←1← 980E BMI | ||||||||||||||
| PLA ; Pull current OR accumulator | |||||||||||||||
| 9801 | ORA hazel_chan_status,x ; OR with channel status byte at &C1C8+X | ||||||||||||||
| 9804 | PHA ; Push back updated accumulator | ||||||||||||||
| 9805 | LDA hazel_chan_status,x ; Reload channel byte | ||||||||||||||
| 9808 | AND #&c0 ; Mask to top 2 bits (preserve TX/RX state) | ||||||||||||||
| 980A | STA hazel_chan_status,x ; Write back trimmed status | ||||||||||||||
| 980D | INX ; Step channel index | ||||||||||||||
| 980E | BMI loop_scan_channels ; Loop while X bit 7 set (covers &F0..&FF) | ||||||||||||||
| 9810 | STX hazel_fs_pending_state ; Clear the FS state byte (no longer active) | ||||||||||||||
| 9813 | JSR close_all_net_chans ; Force-close all client channels | ||||||||||||||
| 9816 | PLA ; Pull final OR accumulator | ||||||||||||||
| 9817 | ROR ; Bit 0 (was bit 6 of any &40 byte) -> C | ||||||||||||||
| 9818 | BCC scan_channel_store_reply ; Any channel was active: skip the warning | ||||||||||||||
| 981A | JSR print_inline_no_spool ; No active channels were lost: print 'Data Lost' warning via inline string | ||||||||||||||
| 981D | EQUS "Data Lost." | ||||||||||||||
| 9827 | .scan_channel_store_reply←1← 9818 BCC | ||||||||||||||
| LDX hazel_fs_last_error ; Reload error code from &C009 | |||||||||||||||
| 982A | PLP ; Restore saved flags (was bit 7 of fs_flags) | ||||||||||||||
| 982B | BEQ build_error_block ; Z set (no error): build the error block anyway | ||||||||||||||
| 982D | PLA ; Pull caller's saved return state (3 bytes from PHP earlier) | ||||||||||||||
| 982E | PLA | ||||||||||||||
| 982F | PLA | ||||||||||||||
| 9830 | RTS ; Return -- caller dispatched on a non-error reply | ||||||||||||||
| 9831 | .build_error_block←2← 97F6 BNE← 982B BEQ | ||||||||||||||
| LDY #1 ; Y=1: skip past the leading TXCB control byte | |||||||||||||||
| 9833 | CPX #&a8 ; Error code below &A8 (extended)? | ||||||||||||||
| 9835 | BCS setup_error_copy ; No (>= &A8): proceed to copy | ||||||||||||||
| 9837 | LDA #&a8 ; Yes: clamp to &A8 (truncate range) | ||||||||||||||
| 9839 | STA (txcb_start),y ; Write clamped code back into TXCB | ||||||||||||||
| 983B | .setup_error_copy←1← 9835 BCS | ||||||||||||||
| LDY #&ff ; Y=&FF: INY in loop bumps to 0 | |||||||||||||||
| 983D | .loop_copy_error←1← 9845 BNE | ||||||||||||||
| INY ; Step Y | |||||||||||||||
| 983E | LDA (txcb_start),y ; Read TXCB byte (error block content) | ||||||||||||||
| 9840 | STA error_block,y ; Copy to BRK error block at &0100+Y | ||||||||||||||
| 9843 | EOR #&0d ; EOR with CR; Z set when we just copied the terminator | ||||||||||||||
| 9845 | BNE loop_copy_error ; Not yet at CR: continue copying | ||||||||||||||
| 9847 | STA error_block,y ; Write the CR terminator (Z still set so A=0; ensures cleanly terminated) | ||||||||||||||
| 984A | DEY ; Step Y back so it points at the CR position | ||||||||||||||
| 984B | TYA ; Move Y into A for the BRK | ||||||||||||||
| 984C | TAX ; Move Y into X (caller convention) | ||||||||||||||
| 984D | JMP check_net_error_code ; Tail-jump into the BRK-dispatch error path | ||||||||||||||
Language reply 1: remote-boot init / continueReads the reply byte at |
|
| 9850 | .lang_1_remote_boot |
| LDY #0 ; Y=0: status byte offset | |
| 9852 | LDA (net_rx_ptr),y ; Read RX status byte |
| 9854 | BEQ init_remote_session ; Zero: re-init the session |
| 9856 | .done_commit_state←1← 98AD BNE |
| JMP commit_state_byte ; Non-zero: commit state and continue | |
| 9859 | .init_remote_session←2← 9854 BEQ← 98A3 BEQ |
| ORA #9 ; Mark session as 'remote boot' | |
| 985B | STA (net_rx_ptr),y ; Store updated status byte back to RX[0] |
| 985D | LDX #&80 ; X=&80: caller machine-id byte offset |
| 985F | LDY #&80 ; Y=&80: same offset |
| 9861 | LDA (net_rx_ptr),y ; Read remote machine ID |
| 9863 | PHA ; Push -- save across the workspace store |
| 9864 | INY ; Y=&81 |
| 9865 | LDA (net_rx_ptr),y ; Re-read for the second store target |
| 9867 | LDY #&0f ; Y=&0F: workspace machine-ID lo offset |
| 9869 | STA (nfs_workspace),y ; Store at (nfs_workspace)+&0F |
| 986B | DEY ; Y=&0E |
| 986C | PLA ; Pop saved machine ID |
| 986D | STA (nfs_workspace),y ; Store at (nfs_workspace)+&0F (reuse) |
| 986F | JSR scan_remote_keys ; Scan remote-key flags |
| 9872 | JSR init_ws_copy_narrow ; Initialise narrow workspace template |
| 9875 | LDX #1 ; X=1: enable Econet keyboard |
| 9877 | LDY #0 ; Y=0 |
| 9879 | LDA #osbyte_read_write_econet_keyboard_disable ; OSBYTE &C9: read/write Econet keyboard disable |
| 987B | JSR osbyte ; Disable keyboard (for Econet) |
| fall through ↓ | |
Language reply 3: raise 'Remoted' error at &0100Calls |
|
| 987E | .lang_3_exec_0100 |
| JSR commit_state_byte ; Commit the language-reply state byte | |
| 9881 | LDA #0 ; A=0: 'Bad' error code |
| 9883 | JSR error_inline_log ; Raise via error_inline_log (never returns) |
| 9886 | EQUS "Remoted.." |
| fall through ↓ | |
Acknowledge escape (if pressed) and classify replyIf escape_flag bit 7 is clear OR need_release_tube bit 7 is clear (so AND result has bit 7 clear), returns immediately via return_1. Otherwise acknowledges escape via OSBYTE &7E (clears the escape condition and runs escape effects), loads A=6 (a synthesized 'Escape' error class), and tail-jumps to classify_reply_error to build the 'Escape' BRK error block. Two callers: cmd_pass (&8DEF) for password-entry escape, and send_net_packet (&9B48) for in-flight TX escape.
|
||||
| 988F | .check_escape_and_classify←2← 8DEF JSR← 9B48 JSR | |||
| LDA escape_flag ; Read escape_flag | ||||
| 9891 | AND need_release_tube ; Mask with need_release_tube (escape-disable) | |||
| 9893 | BPL return_3 ; Bit 7 clear: not escaping, return | |||
| fall through ↓ | ||||
Acknowledge escape and raise classified errorIssues OSBYTE &7E (acknowledge_escape -- clears the escape condition and runs any registered escape effects), loads A=6, and tail-jumps to classify_reply_error which builds the Escape error. Reached from &98EF (after recv_and_process_reply detects escape) and &B7DF (cmd_wipe's per-iteration escape check). Never returns -- the classify_reply_error path triggers BRK.
|
||||
| 9895 | .raise_escape_error←2← 98EF BMI← B7DF JMP | |||
| LDA #osbyte_acknowledge_escape ; A=&7E: OSBYTE &7E = acknowledge Escape | ||||
| 9897 | JSR osbyte ; Clear escape condition and perform escape effects | |||
| 989A | LDA #6 ; A=6: error class for 'Escape' | |||
| 989C | JMP classify_reply_error ; JMP classify_reply_error (never returns) | |||
Language reply 4: validate remote session and applyReads the first reply byte at |
|
| 989F | .lang_4_validated |
| LDY #0 ; Y=0: status byte offset | |
| 98A1 | LDA (net_rx_ptr),y ; Read RX status byte |
| 98A3 | BEQ init_remote_session ; Zero status: re-init the session |
| 98A5 | LDY #&80 ; Y=&80: session-ID byte offset in RX |
| 98A7 | LDA (net_rx_ptr),y ; Read remote session-ID |
| 98A9 | LDY #&0e ; Y=&0E: stored session-ID offset in workspace |
| 98AB | CMP (nfs_workspace),y ; Compare with stored ID |
| 98AD | BNE done_commit_state ; Mismatch: skip the commit (treat as foreign) |
| fall through ↓ | |
Language reply 0: insert remote keypressReads the keycode from the reply at
|
||||
| 98AF | .lang_0_insert_key | |||
| LDY #&82 ; Y=&82: keypress byte offset in RX | ||||
| 98B1 | LDA (net_rx_ptr),y ; Read remote keypress code | |||
| 98B3 | TAY ; Y = key code | |||
| 98B4 | LDX #0 ; X=0: keyboard buffer ID | |||
| 98B6 | JSR commit_state_byte ; Commit the language-reply state | |||
| 98B9 | LDA #osbyte_insert_input_buffer ; OSBYTE &99: insert byte into input buffer | |||
| 98BB | JMP osbyte ; Insert character Y into input buffer X | |||
Wait for reply on open receive with timeoutDespite the name, this does not wait for a TX acknowledgment.
It polls an open receive control block (bit 7 of Uses a three-level nested polling loop:
Total: On timeout, branches to |
|||||||||||||||||
| 98BE | .wait_net_tx_ack←6← 97D1 JSR← 9C9F JSR← 9DD7 JSR← ACAA JMP← AF53 JSR← AFF5 JSR | ||||||||||||||||
| LDA rx_wait_timeout ; Read the configurable rx-wait timeout (&0D6E, default &28 = ~22s on 2 MHz) | |||||||||||||||||
| 98C1 | PHA ; Push it as the outermost counter (read back via stack-X indexing later) | ||||||||||||||||
| 98C2 | LDA econet_flags ; Read econet_flags so we can preserve it across the wait | ||||||||||||||||
| 98C5 | PHA ; Push it (we'll temporarily set bit 7 to mark waiting) | ||||||||||||||||
| 98C6 | LDA net_tx_ptr_hi ; Check whether net_tx_ptr_hi is non-zero (TX in flight?) | ||||||||||||||||
| 98C8 | BNE init_poll_counters ; Yes: skip the flag-set; counters initialise either way | ||||||||||||||||
| 98CA | ORA #&80 ; TX idle: set bit 7 of econet_flags (signal RX-only wait) | ||||||||||||||||
| 98CC | STA econet_flags ; Write the modified flags back | ||||||||||||||||
| 98CF | .init_poll_counters←1← 98C8 BNE | ||||||||||||||||
| LDA #0 ; A=0: initial value for inner+middle counters | |||||||||||||||||
| 98D1 | PHA ; Push it -- middle counter at stack[X+2] | ||||||||||||||||
| 98D2 | PHA ; Push it again -- inner counter at stack[X+1] | ||||||||||||||||
| 98D3 | TAY ; Y=0: indirect index for net_tx_ptr poll | ||||||||||||||||
| 98D4 | TSX ; Capture S into X so we can address the stack counters | ||||||||||||||||
| 98D5 | .loop_poll_tx←4← 98DC BNE← 98E1 BNE← 98E6 BNE← 98F4 BNE | ||||||||||||||||
| LDA (net_tx_ptr),y ; Read RX/TX flags through net_tx_ptr -- bit 7 set means complete | |||||||||||||||||
| 98D7 | BMI done_poll_tx ; Bit 7 set: reply received, exit poll | ||||||||||||||||
| 98D9 | DEC error_text,x ; Decrement inner counter at stack[X+1] | ||||||||||||||||
| 98DC | BNE loop_poll_tx ; Inner not zero yet: poll again | ||||||||||||||||
| 98DE | DEC stack_page_2,x ; Inner wrapped: decrement middle at stack[X+2] | ||||||||||||||||
| 98E1 | BNE loop_poll_tx ; Middle not zero: poll again | ||||||||||||||||
| 98E3 | DEC stack_page_4,x ; Middle wrapped: decrement outer at stack[X+4] (the saved timeout value) | ||||||||||||||||
| 98E6 | BNE loop_poll_tx ; Outer not zero: poll again | ||||||||||||||||
| 98E8 | LDA rx_wait_timeout ; Reload the original timeout to test for timeout=0 mode | ||||||||||||||||
| 98EB | BNE done_poll_tx ; Configured timeout was non-zero: declare timeout | ||||||||||||||||
| 98ED | LDA escape_flag ; Timeout=0 (poll forever): check escape flag | ||||||||||||||||
| 98EF | BMI raise_escape_error ; Escape pressed: jump to escape handler at &9895 | ||||||||||||||||
| 98F1 | INC stack_page_4,x ; Reset outer counter so we keep polling | ||||||||||||||||
| 98F4 | BNE loop_poll_tx ; Always taken (INC's result is always non-zero here): back to inner | ||||||||||||||||
| 98F6 | .done_poll_tx←2← 98D7 BMI← 98EB BNE | ||||||||||||||||
| PLA ; done_poll_tx: discard inner counter | |||||||||||||||||
| 98F7 | PLA ; Discard middle counter | ||||||||||||||||
| 98F8 | PLA ; Pull saved econet_flags | ||||||||||||||||
| 98F9 | STA econet_flags ; Restore them (clearing bit 7 if we set it) | ||||||||||||||||
| 98FC | PLA ; Pull saved rx_wait_timeout into A | ||||||||||||||||
| 98FD | BEQ build_no_reply_error ; If timeout reached zero, raise 'No reply' | ||||||||||||||||
| 98FF | .return_3←1← 9893 BPL | ||||||||||||||||
| RTS ; Reply received normally: return | |||||||||||||||||
Conditionally store error code to workspaceTests bit 7 of
This guards against writing error state when no filing system
is active. Called internally by the error-classification chain
and by
|
||||||||||
| 9900 | .cond_save_error_code←6← 9916 JSR← 994F JSR← 996B JSR← 9995 JSR← 99A7 JSR← 99C0 JSR | |||||||||
| BIT fs_flags ; Test bit 7 of fs_flags (FS-active flag) | ||||||||||
| 9903 | BPL return_from_cond_save_err ; FS not active: skip the save | |||||||||
| 9905 | STA hazel_fs_last_error ; FS active: store error code at &C009 (last-error byte) | |||||||||
| 9908 | .return_from_cond_save_err←1← 9903 BPL | |||||||||
| RTS ; Return | ||||||||||
| 9909 | .build_no_reply_error←1← 98FD BEQ | |||||||||
| LDX #8 ; X=8: net_error_lookup_data offset for 'No reply' message | ||||||||||
| 990B | LDY net_error_lookup_data,x ; Y = message offset within the string table (&9AA6 base) | |||||||||
| 990E | LDX #0 ; X=0: error-text buffer index | |||||||||
| 9910 | STX error_block ; Zero the &0100 length byte (length will be filled in later) | |||||||||
| 9913 | LDA error_msg_table,y ; Read first message byte (the error code) | |||||||||
| 9916 | JSR cond_save_error_code ; Conditionally save it as last-error | |||||||||
| 9919 | .loop_copy_no_reply_msg←1← 9923 BNE | |||||||||
| LDA error_msg_table,y ; Read next message byte | ||||||||||
| 991C | STA error_text,x ; Append to error-text buffer at &0101+X | |||||||||
| 991F | BEQ done_no_reply_msg ; Null terminator: message done | |||||||||
| 9921 | INX ; Step buffer index | |||||||||
| 9922 | INY ; Step source offset | |||||||||
| 9923 | BNE loop_copy_no_reply_msg ; Loop while Y != 0 (Y wraps at 256, not expected) | |||||||||
| 9925 | .done_no_reply_msg←1← 991F BEQ | |||||||||
| JSR append_drv_dot_num ; Append ' on drive num>' or similar context | ||||||||||
| 9928 | LDA #0 ; A=0: null terminator | |||||||||
| 992A | STA error_text,x ; Store at end of message | |||||||||
| 992D | JMP check_net_error_code ; Tail-jump to dispatch the BRK error | |||||||||
Substitute 'B' for 'A' in reply status byteReads the FS reply status byte at (net_tx_ptr,X). If it is 'A' (Acknowledge with no error), substitutes 'B' so downstream code treats it as a soft error. CLV before falling through into mask_error_class to ensure the no-extended-error path is taken.
|
|||||||||
| 9930 | .fixup_reply_status_a←1← 9BB3 JMP | ||||||||
| LDA (net_tx_ptr,x) ; Read FS reply status byte at (net_tx_ptr,X) | |||||||||
| 9932 | CMP #&41 ; Status 'A'? (Acknowledge with no error) | ||||||||
| 9934 | BNE skip_if_not_a ; Not 'A': pass through unchanged | ||||||||
| 9936 | LDA #&42 ; Substitute 'B' for 'A' (handle ACK as a soft error) | ||||||||
| 9938 | .skip_if_not_a←1← 9934 BNE | ||||||||
| CLV ; Clear V to take the standard mask path | |||||||||
| 9939 | BVC mask_error_class ; Always taken: use the standard masked-error path | ||||||||
| fall through ↓ | |||||||||
Load reply byte and classify errorSingle-byte prologue to
|
||||
| 993B | .load_reply_and_classify←1← 9B6C JMP | |||
| LDA (net_tx_ptr,x) ; Read FS reply status byte | ||||
| fall through ↓ | ||||
Classify FS reply error codeForces
Two callers:
|
||||||||||
| 993D | .classify_reply_error←2← 989C JMP← A0BD JMP | |||||||||
| BIT always_set_v_byte ; BIT $always_set_v_byte: force V=1 (extended-error path) | ||||||||||
| 9940 | .mask_error_class←1← 9939 BVC | |||||||||
| AND #7 ; Mask to 3 bits (error class 0..7) | ||||||||||
| 9942 | PHA ; Save error class on stack | |||||||||
| 9943 | CMP #2 ; Class 2 = 'station-related' family? | |||||||||
| 9945 | BNE build_simple_error ; No: build a simple one-line error | |||||||||
| 9947 | PHP ; Class 2 yes: save flags so we can branch on V later | |||||||||
| 9948 | TAX ; X = error class (=2) | |||||||||
| 9949 | LDY net_error_lookup_data,x ; Y = lookup-table offset | |||||||||
| 994C | LDA error_msg_table,y ; Read first message byte (error code) | |||||||||
| 994F | JSR cond_save_error_code ; Conditionally save it | |||||||||
| 9952 | LDX #0 ; X=0: text-buffer index | |||||||||
| 9954 | STX error_block ; Zero length byte | |||||||||
| 9957 | .loop_copy_station_msg←1← 9961 BNE | |||||||||
| LDA error_msg_table,y ; Read message byte | ||||||||||
| 995A | STA error_text,x ; Append to buffer | |||||||||
| 995D | BEQ done_station_msg ; Null terminator -- station message done | |||||||||
| 995F | INY ; Advance Y | |||||||||
| 9960 | INX ; Advance X | |||||||||
| 9961 | BNE loop_copy_station_msg ; Loop until X wraps | |||||||||
| 9963 | .done_station_msg←1← 995D BEQ | |||||||||
| JSR append_drv_dot_num ; Append ' on drive |
||||||||||
| 9966 | PLP ; Restore the saved class flags | |||||||||
| 9967 | BVS suffix_not_listening ; V was set: use 'not listening' suffix | |||||||||
| 9969 | LDA #&a4 ; A=&A4: 'station n> not available' error code | |||||||||
| 996B | JSR cond_save_error_code ; Save the alternative error code | |||||||||
| 996E | STA error_text ; Patch error-text buffer length byte | |||||||||
| 9971 | LDY #&0b ; Y=&0B: lookup index for the listening-station suffix | |||||||||
| 9973 | BNE load_suffix_offset ; Always taken (Y is non-zero); jump to load_suffix_offset | |||||||||
| 9975 | .suffix_not_listening←1← 9967 BVS | |||||||||
| LDY #9 ; V was clear: 'not listening' suffix variant | ||||||||||
| 9977 | .load_suffix_offset←1← 9973 BNE | |||||||||
| LDA net_error_lookup_data,y ; Read suffix offset from lookup | ||||||||||
| 997A | TAY ; Y = suffix offset | |||||||||
| 997B | .loop_copy_suffix←1← 9985 BNE | |||||||||
| LDA error_msg_table,y ; Read suffix byte | ||||||||||
| 997E | STA error_text,x ; Append | |||||||||
| 9981 | BEQ done_suffix ; Null: suffix done | |||||||||
| 9983 | INY ; Step Y | |||||||||
| 9984 | .suffix_copy_loop←1← A874 BIT | |||||||||
| INX ; Step X | ||||||||||
| 9985 | BNE loop_copy_suffix ; Loop while X != 0 (max 255 chars) | |||||||||
| 9987 | .done_suffix←1← 9981 BEQ | |||||||||
| BEQ check_msg_terminator ; Always taken (Z still set from BEQ): final terminator check | ||||||||||
| 9989 | .build_simple_error←2← 8B42 JMP← 9945 BNE | |||||||||
| TAX ; X = error class | ||||||||||
| 998A | LDY net_error_lookup_data,x ; Y = lookup-table offset | |||||||||
| 998D | LDX #0 ; X=0: buffer index | |||||||||
| 998F | STX error_block ; Zero length | |||||||||
| 9992 | LDA error_msg_table,y ; Read first message byte (error code) | |||||||||
| 9995 | JSR cond_save_error_code ; Conditionally save it | |||||||||
| 9998 | .loop_copy_error_msg←1← 99A2 BNE | |||||||||
| LDA error_msg_table,y ; Read next message byte | ||||||||||
| 999B | STA error_text,x ; Append to buffer | |||||||||
| 999E | .check_msg_terminator←1← 9987 BEQ | |||||||||
| BEQ check_net_error_code ; Null terminator -> dispatch | ||||||||||
| 99A0 | INY ; Step Y | |||||||||
| 99A1 | INX ; Step X | |||||||||
| 99A2 | .bad_str_anchor | |||||||||
| BNE loop_copy_error_msg ; Loop while X != 0 | ||||||||||
| 99A4 | 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.
|
||||
| 99A7 | .error_bad_inline←11← 90B7 JSR← 934C JSR← 9359 JSR← 936D JSR← 9379 JSR← 9388 JSR← 9439 JSR← 94BB JSR← 94DC JSR← A5A3 JSR← BF48 JSR | |||
| JSR cond_save_error_code ; Conditionally log error code to workspace | ||||
| 99AA | TAY ; Save error number in Y | |||
| 99AB | PLA ; Pop return address (low) — points to last byte of JSR | |||
| 99AC | STA fs_load_addr ; Store return address low | |||
| 99AE | PLA ; Pop return address (high) | |||
| 99AF | STA fs_load_addr_hi ; Store return address high | |||
| 99B1 | LDX #0 ; X=0: start of prefix string | |||
| 99B3 | .loop_copy_bad_prefix←1← 99BC BNE | |||
| INX ; Copy 'Bad ' prefix from lookup table | ||||
| 99B4 | LDA bad_prefix_table,x ; Get next prefix character | |||
| 99B7 | STA error_text,x ; Store in error text buffer | |||
| 99BA | CMP #&20 ; Is it space (end of 'Bad ')? | |||
| 99BC | BNE loop_copy_bad_prefix ; No: copy next prefix character | |||
| 99BE | 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 cond_save_error_code before building the error block.
|
||||
| 99C0 | .error_inline_log←11← 9564 JSR← 9883 JSR← A5BA JSR← AF82 JSR← AF94 JSR← B81F JSR← B895 JSR← B8E6 JSR← BB79 JSR← BBB3 JSR← BBFD 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.
|
||||
| 99C3 | .error_inline←4← A444 JSR← BD37 JSR← BEDB JSR← BF91 JSR | |||
| TAY ; Save error number in Y | ||||
| 99C4 | PLA ; Pop return address (low) — points to last byte of JSR | |||
| 99C5 | STA fs_load_addr ; Store return address low | |||
| 99C7 | PLA ; Pop return address (high) | |||
| 99C8 | STA fs_load_addr_hi ; Store return address high | |||
| 99CA | LDX #0 ; X=0: error text index | |||
| 99CC | .write_error_num_and_str←1← 99BE BEQ | |||
| STY error_text ; Store error number in error block | ||||
| 99CF | TYA ; Copy error number to A | |||
| 99D0 | PHA ; Push error number on stack | |||
| 99D1 | LDY #0 ; Y=0: inline string index | |||
| 99D3 | STY error_block ; Zero the BRK byte at &0100 | |||
| 99D6 | .loop_copy_inline_str←1← 99DD BNE | |||
| INX ; Copy inline string into error block | ||||
| 99D7 | INY ; Advance string index | |||
| 99D8 | LDA (fs_load_addr),y ; Read next byte from inline string | |||
| 99DA | STA error_text,x ; Store byte in error block | |||
| 99DD | BNE loop_copy_inline_str ; Loop until null terminator | |||
| fall through ↓ | ||||
Translate net error: 'OK' → return, 'FS error' → appendReads the receive-attribute byte:
|
|||||||||
| 99DF | .check_net_error_code←4← 984D JMP← 992D JMP← 999E BEQ← BD02 JMP | ||||||||
| JSR read_rx_attribute ; Read receive attribute byte | |||||||||
| 99E2 | BNE handle_net_error ; Non-zero: network returned an error | ||||||||
| 99E4 | PLA ; Pop saved error number | ||||||||
| 99E5 | CMP #&de ; Was it &DE (file server error)? | ||||||||
| 99E7 | BEQ append_error_number ; Yes: append error number and trigger BRK | ||||||||
| 99E9 | .trigger_brk←1← 9A38 BEQ | ||||||||
| JMP error_block ; Jump to BRK via error block | |||||||||
| 99EC | .handle_net_error←1← 99E2 BNE | ||||||||
| STA hazel_fs_error_code ; Store error code in workspace | |||||||||
| 99EF | PHA ; Push error code | ||||||||
| 99F0 | TXA ; Save X (error text index) | ||||||||
| 99F1 | PHA ; Push X | ||||||||
| 99F2 | JSR read_rx_attribute ; Read receive attribute byte | ||||||||
| 99F5 | STA fs_load_addr ; Save to fs_load_addr as spool handle | ||||||||
| 99F7 | LDA #0 ; A=0: clear error code in RX buffer | ||||||||
| 99F9 | STA (net_rx_ptr),y ; Zero the error code byte in buffer | ||||||||
| 99FB | LDA #&c6 ; A=&C6: OSBYTE read spool handle | ||||||||
| 99FD | JSR osbyte_x0 ; Read current spool file handle | ||||||||
| 9A00 | CPY fs_load_addr ; Compare Y result with saved handle | ||||||||
| 9A02 | BEQ net_error_close_spool ; Match: close the spool file | ||||||||
| 9A04 | CPX fs_load_addr ; Compare X result with saved handle | ||||||||
| 9A06 | BNE done_close_files ; No match: skip spool close | ||||||||
| 9A08 | PHA ; Push A (preserved) | ||||||||
| 9A09 | LDA #&c6 ; A=&C6: disable spool with OSBYTE | ||||||||
| 9A0B | BNE close_spool_exec ; ALWAYS branch to close spool | ||||||||
| 9A0D | .net_error_close_spool←1← 9A02 BEQ | ||||||||
| PHY | |||||||||
| 9A0E | LDA #&c7 ; A=&C7: OSBYTE 'flush input buffer' | ||||||||
| 9A10 | .close_spool_exec←1← 9A0B BNE | ||||||||
| JSR osbyte_x0_y0 ; Tail-call OSBYTE with X=0/Y=0 | |||||||||
| 9A13 | PLY | ||||||||
| 9A14 | LDA #osfind_close ; A=0: close file | ||||||||
| 9A16 | JSR osfind ; Close the spool/exec file | ||||||||
| 9A19 | .done_close_files←1← 9A06 BNE | ||||||||
| PLA ; Pull saved X (error text index) | |||||||||
| 9A1A | TAX ; Restore X | ||||||||
| 9A1B | LDY #&0a ; Y=&0A: lookup index for 'on channel' | ||||||||
| 9A1D | LDA net_error_lookup_data,y ; Load message offset from lookup table | ||||||||
| 9A20 | TAY ; Transfer offset to Y | ||||||||
| 9A21 | .loop_copy_channel_msg←1← 9A2B BNE | ||||||||
| LDA error_msg_table,y ; Load error message byte | |||||||||
| 9A24 | STA error_text,x ; Append to error text buffer | ||||||||
| 9A27 | BEQ append_error_number ; Null terminator: done copying | ||||||||
| 9A29 | INX ; Advance error text index | ||||||||
| 9A2A | INY ; Advance message index | ||||||||
| 9A2B | BNE loop_copy_channel_msg ; Loop until full message copied | ||||||||
| 9A2D | .append_error_number←2← 99E7 BEQ← 9A27 BEQ | ||||||||
| STX fs_load_addr_2 ; Save error text end position | |||||||||
| 9A2F | PLA ; Pull saved error number | ||||||||
| 9A30 | JSR append_space_and_num ; Append ' nnn' error number suffix | ||||||||
| 9A33 | LDA #0 ; A=0: null terminator | ||||||||
| 9A35 | STA stack_page_2,x ; Terminate error text string | ||||||||
| 9A38 | BEQ trigger_brk ; ALWAYS branch to trigger BRK error | ||||||||
Append 'net.station' decimal string to error textReads network and station numbers from the TX control block at offsets 3 and 2. Writes:
into the error-text buffer at the current position.
|
|||||||
| 9A3A | .append_drv_dot_num←2← 9925 JSR← 9963 JSR | ||||||
| LDA #&20 ; A=' ': space separator | |||||||
| 9A3C | STA error_text,x ; Append space to error text | ||||||
| 9A3F | INX ; Advance error text index | ||||||
| 9A40 | STX fs_load_addr_2 ; Save position for number formatting | ||||||
| 9A42 | LDY #3 ; Y=3: offset to network number in TX CB | ||||||
| 9A44 | LDA (net_tx_ptr),y ; Load network number | ||||||
| 9A46 | BEQ append_station_num ; Zero: skip network part (local) | ||||||
| 9A48 | JSR append_decimal_num ; Append network number as decimal | ||||||
| 9A4B | LDX fs_load_addr_2 ; Reload error text position | ||||||
| 9A4D | LDA #&2e ; A='.': dot separator | ||||||
| 9A4F | STA error_text,x ; Append dot to error text | ||||||
| 9A52 | INC fs_load_addr_2 ; Advance past dot | ||||||
| 9A54 | .append_station_num←1← 9A46 BEQ | ||||||
| LDY #2 ; Y=2: offset to station number in TX CB | |||||||
| 9A56 | LDA (net_tx_ptr),y ; Load station number | ||||||
| 9A58 | JSR append_decimal_num ; Append station number as decimal | ||||||
| 9A5B | LDX fs_load_addr_2 ; Reload error text position | ||||||
| 9A5D | 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.
|
||||
| 9A5E | .append_space_and_num←2← 9A30 JSR← B874 JSR | |||
| TAY ; Save number in Y | ||||
| 9A5F | LDA #&20 ; A=' ': space prefix | |||
| 9A61 | LDX fs_load_addr_2 ; Load current error text position | |||
| 9A63 | STA error_text,x ; Append space to error text | |||
| 9A66 | INC fs_load_addr_2 ; Advance position past space | |||
| 9A68 | 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.
|
||||
| 9A69 | .append_decimal_num←2← 9A48 JSR← 9A58 JSR | |||
| TAY ; Save number in Y for division | ||||
| 9A6A | BIT always_set_v_byte ; Set V: suppress leading zeros | |||
| 9A6D | LDA #&64 ; A=100: hundreds digit divisor | |||
| 9A6F | JSR append_decimal_digit ; Extract and append hundreds digit | |||
| 9A72 | LDA #&0a ; A=10: tens digit divisor | |||
| 9A74 | JSR append_decimal_digit ; Extract and append tens digit | |||
| 9A77 | LDA #1 ; A=1: units digit (remainder) | |||
| 9A79 | 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.
|
|||||||||||||
| 9A7A | .append_decimal_digit←2← 9A6F JSR← 9A74 JSR | ||||||||||||
| STA fs_load_addr_3 ; Store divisor | |||||||||||||
| 9A7C | TYA ; Copy number to A for division | ||||||||||||
| 9A7D | LDX #&2f ; X='0'-1: digit counter (ASCII offset) | ||||||||||||
| 9A7F | PHP ; Save V flag (leading zero suppression) | ||||||||||||
| 9A80 | SEC ; Set carry for subtraction | ||||||||||||
| 9A81 | .loop_count_digit←1← 9A84 BCS | ||||||||||||
| INX ; Increment digit counter | |||||||||||||
| 9A82 | SBC fs_load_addr_3 ; Subtract divisor | ||||||||||||
| 9A84 | BCS loop_count_digit ; Not negative yet: continue counting | ||||||||||||
| 9A86 | ADC fs_load_addr_3 ; Add back divisor (restore remainder) | ||||||||||||
| 9A88 | PLP ; Restore V flag | ||||||||||||
| 9A89 | TAY ; Save remainder back to Y | ||||||||||||
| 9A8A | TXA ; Digit counter to A (ASCII digit) | ||||||||||||
| 9A8B | CMP #&30 ; Is digit '0'? | ||||||||||||
| 9A8D | BNE store_digit ; Non-zero: always print | ||||||||||||
| 9A8F | BVS return_from_store_digit ; V set (suppress leading zeros): skip | ||||||||||||
| 9A91 | .store_digit←1← 9A8D BNE | ||||||||||||
| CLV ; Clear V: first non-zero digit seen | |||||||||||||
| 9A92 | LDX fs_load_addr_2 ; Load current text position | ||||||||||||
| 9A94 | STA error_text,x ; Store ASCII digit in error text | ||||||||||||
| 9A97 | INC fs_load_addr_2 ; Advance text position | ||||||||||||
| 9A99 | .return_from_store_digit←1← 9A8F BVS | ||||||||||||
| RTS ; Return | |||||||||||||
Net-error class -> error_msg_table offset (12 bytes)Maps Econet network-error classes to byte offsets into
Each byte is computed as |
|
| 9A9A | .net_error_lookup_data←5← 990B LDY← 9949 LDY← 9977 LDA← 998A LDY← 9A1D LDA |
| EQUB &00 | |
| 9A9B | EQUB msg_net_error - error_msg_table |
| 9A9C | EQUB msg_station - error_msg_table |
| 9A9D | EQUB msg_no_clock - error_msg_table |
| 9A9E | EQUB msg_escape - error_msg_table |
| 9A9F | EQUB msg_escape - error_msg_table |
| 9AA0 | EQUB msg_escape - error_msg_table |
| 9AA1 | EQUB msg_bad_option - error_msg_table |
| 9AA2 | EQUB msg_no_reply - error_msg_table |
| 9AA3 | EQUB msg_not_listening - error_msg_table |
| 9AA4 | EQUB msg_on_channel - error_msg_table |
| 9AA5 | EQUB msg_not_present - error_msg_table |
Net-error message stringsBody of error-text fragments referenced by
Per-byte inline comments below name each error code and message;
the bytes from this table are read by |
|
| 9AA6 | .error_msg_table←8← 9913 LDA← 9919 LDA← 994C LDA← 9957 LDA← 997B LDA← 9992 LDA← 9998 LDA← 9A21 LDA |
| EQUB &A0 | |
| 9AA7 | EQUS "Line jammed" ; err_line_jammed = &A0 |
| 9AB2 | EQUB &00 ; Null terminator |
| 9AB3 | .msg_net_error |
| EQUB &A1 ; Error &A1: Net error | |
| 9AB4 | EQUS "Net error" ; err_net_error = &A1 |
| 9ABD | EQUB &00 ; Null terminator |
| 9ABE | .msg_station |
| EQUB &A2 ; Error &A2: Station | |
| 9ABF | EQUS "Station" |
| 9AC6 | EQUB &00 |
| 9AC7 | .msg_no_clock |
| EQUB &A3 | |
| 9AC8 | EQUS "No clock" |
| 9AD0 | EQUB &00 ; Null terminator |
| 9AD1 | .msg_escape |
| EQUB &11 ; Error &11: Escape | |
| 9AD2 | EQUS "Escape" |
| 9AD8 | EQUB &00 |
| 9AD9 | .msg_bad_option |
| EQUB &CB ; Error &CB: Bad option | |
| 9ADA | EQUS "Bad option" |
| 9AE4 | EQUB &00 ; Null terminator + Error &A5: No reply from station |
| 9AE5 | .msg_no_reply |
| EQUB &A5 | |
| 9AE6 | EQUS "No reply from station" ; err_no_reply = &A5 message body |
| 9AFB | EQUB &00 ; Null terminator |
| 9AFC | .msg_not_listening |
| EQUS " not listening" ; Suffix string (offset &56 in lookup) | |
| 9B0A | EQUB &00 ; Null terminator |
| 9B0B | .msg_on_channel |
| EQUS " on channel" ; Suffix: " on channel" | |
| 9B16 | EQUB &00 ; Null terminator |
| 9B17 | .msg_not_present |
| EQUS " not present" ; Suffix: " not present" | |
| 9B23 | 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.
|
||||
| 9B24 | .init_tx_ptr_and_send←2← 97C9 JSR← 9DC8 JSR | |||
| LDX #&c0 ; X=&C0: TX control block base (low) | ||||
| 9B26 | STX net_tx_ptr ; Set TX pointer low | |||
| 9B28 | LDX #0 ; X=0: TX control block base (high) | |||
| 9B2A | STX net_tx_ptr_hi ; Set TX pointer high (page 0) | |||
| fall through ↓ | ||||
Transmit Econet packet with retryTwo-phase transmit with retry. Loads retry count from
With default
|
|||||||||||||
| 9B2C | .send_net_packet←6← ACF9 JMP← AD56 JSR← AF48 JSR← AFD3 JSR← B419 JSR← B5F8 JSR | ||||||||||||
| LDA tx_retry_count ; Load retry count from workspace | |||||||||||||
| 9B2F | BNE set_timeout ; Non-zero: use configured retry count | ||||||||||||
| 9B31 | LDA #&ff ; A=&FF: default retry count (255) | ||||||||||||
| 9B33 | .set_timeout←1← 9B2F BNE | ||||||||||||
| LDY #&60 ; Y=&60: timeout value | |||||||||||||
| 9B35 | PHA ; Push retry count | ||||||||||||
| 9B36 | TYA ; A=&60: copy timeout to A | ||||||||||||
| 9B37 | PHA ; Push timeout | ||||||||||||
| 9B38 | LDX #0 ; X=0: TX pointer index | ||||||||||||
| 9B3A | LDA (net_tx_ptr,x) ; Load first byte of TX control block | ||||||||||||
| 9B3C | .start_tx_attempt←1← 9B5E BEQ | ||||||||||||
| STA (net_tx_ptr,x) ; Restore control byte (overwritten by result code on retry) | |||||||||||||
| 9B3E | PHA ; Push control byte | ||||||||||||
| 9B3F | JSR poll_adlc_tx_status ; Poll ADLC until line idle | ||||||||||||
| 9B42 | ASL ; Bit 6 (error flag) into N | ||||||||||||
| 9B43 | BPL tx_success ; N=0 (bit 6 clear): success | ||||||||||||
| 9B45 | ASL ; Shift away error flag, keep error type | ||||||||||||
| 9B46 | BEQ tx_send_error ; Z=1 (no type bits): fatal; Z=0: retryable | ||||||||||||
| 9B48 | JSR check_escape_and_classify ; Check for escape condition | ||||||||||||
| 9B4B | PLA ; Pull control byte | ||||||||||||
| 9B4C | TAX ; Restore to X | ||||||||||||
| 9B4D | PLA ; Pull timeout | ||||||||||||
| 9B4E | TAY ; Restore to Y | ||||||||||||
| 9B4F | PLA ; Pull retry count | ||||||||||||
| 9B50 | BEQ try_alternate_phase ; Zero retries remaining: try alternate | ||||||||||||
| 9B52 | .loop_retry_tx←1← 9B69 BNE | ||||||||||||
| SBC #1 ; Decrement retry counter | |||||||||||||
| 9B54 | PHA ; Push updated retry count | ||||||||||||
| 9B55 | TYA ; Copy timeout to A | ||||||||||||
| 9B56 | PHA ; Push timeout for delay loop | ||||||||||||
| 9B57 | TXA ; Copy control byte to A | ||||||||||||
| 9B58 | .loop_tx_delay←2← 9B59 BNE← 9B5C BNE | ||||||||||||
| DEX ; Inner delay: decrement X | |||||||||||||
| 9B59 | BNE loop_tx_delay ; Loop until X=0 | ||||||||||||
| 9B5B | DEY ; Decrement outer counter Y | ||||||||||||
| 9B5C | BNE loop_tx_delay ; Loop until Y=0 | ||||||||||||
| 9B5E | BEQ start_tx_attempt ; ALWAYS branch: retry transmission | ||||||||||||
| 9B60 | .try_alternate_phase←1← 9B50 BEQ | ||||||||||||
| CMP tx_retry_count ; Compare retry count with alternate | |||||||||||||
| 9B63 | BNE tx_send_error ; Different: go to error handling | ||||||||||||
| 9B65 | LDA #&80 ; A=&80: set escapable flag | ||||||||||||
| 9B67 | STA need_release_tube ; Mark as escapable for second phase | ||||||||||||
| 9B69 | BNE loop_retry_tx ; ALWAYS branch: retry with escapable | ||||||||||||
| 9B6B | .tx_send_error←2← 9B46 BEQ← 9B63 BNE | ||||||||||||
| TAX ; Result code to X | |||||||||||||
| 9B6C | JMP load_reply_and_classify ; Jump to classify reply and return | ||||||||||||
| 9B6F | .tx_success←1← 9B43 BPL | ||||||||||||
| PLA ; Pull control byte | |||||||||||||
| 9B70 | PLA ; Pull timeout | ||||||||||||
| 9B71 | PLA ; Pull retry count | ||||||||||||
| 9B72 | JMP clear_escapable ; Clear escapable flag and return | ||||||||||||
Pass-through TX buffer template (12 bytes)Overlaid onto the TX control block by
The buffer spans |
|||||||||||||||||
| 9B75 | .pass_txbuf_init_table←2← 9B8B LDX← 9BE5 LDX | ||||||||||||||||
| EQUB &88 ; Offset 0: ctrl = &88 (immediate TX) | |||||||||||||||||
| 9B76 | EQUB &00 ; Offset 1: port = &00 (immediate op) | ||||||||||||||||
| 9B77 | EQUB &FD ; Offset 2: &FD skip (preserve dest stn) | ||||||||||||||||
| 9B78 | EQUB &FD ; Offset 3: &FD skip (preserve dest net) | ||||||||||||||||
| 9B79 | EQUB &3A ; Offset 4: buf start lo (&3A) -> &0D3A | ||||||||||||||||
| 9B7A | EQUB &0D ; Offset 5: buf start hi (&0D) -> &0D3A | ||||||||||||||||
| 9B7B | EQUB &FF ; Offset 6: extended-addr fill (&FF) | ||||||||||||||||
| 9B7C | EQUB &FF ; Offset 7: extended-addr fill (&FF) | ||||||||||||||||
| 9B7D | EQUB &3E ; Offset 8: buf end lo (&3E) -> &0D3E | ||||||||||||||||
| 9B7E | EQUB &0D ; Offset 9: buf end hi (&0D) -> &0D3E | ||||||||||||||||
| 9B7F | EQUB &FF ; Offset 10: extended-addr fill (&FF) | ||||||||||||||||
| 9B80 | 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.
|
||||
| 9B81 | .init_tx_ptr_for_pass←1← 8E18 JSR | |||
| LDY #&c0 ; Y=&C0: TX control block base (low) | ||||
| 9B83 | STY net_tx_ptr ; Set TX pointer low byte | |||
| 9B85 | LDY #0 ; Y=0: TX control block base (high) | |||
| 9B87 | 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.
|
||||
| 9B89 | .setup_pass_txbuf←1← AF45 JSR | |||
| LDY #&0b ; Y=&0B: 12 bytes to process (0-11) | ||||
| 9B8B | .loop_copy_template←1← 9B99 BPL | |||
| LDX pass_txbuf_init_table,y ; Load template byte for this offset | ||||
| 9B8E | CPX #&fd ; Is it &FD (skip marker)? | |||
| 9B90 | BEQ skip_template_byte ; Yes: skip this offset, don't modify | |||
| 9B92 | LDA (net_tx_ptr),y ; Load existing TX buffer byte | |||
| 9B94 | PHA ; Save original value on stack | |||
| 9B95 | TXA ; Copy template value to A | |||
| 9B96 | STA (net_tx_ptr),y ; Store template value to TX buffer | |||
| 9B98 | .skip_template_byte←1← 9B90 BEQ | |||
| DEY ; Next offset (descending) | ||||
| 9B99 | BPL loop_copy_template ; Loop until all 12 bytes processed | |||
| 9B9B | LDA peek_retry_count ; Load pass-through control value | |||
| 9B9E | PHA ; Push control value | |||
| 9B9F | TYA ; A=&FF (Y is &FF after loop) | |||
| 9BA0 | PHA ; Push &FF as timeout | |||
| 9BA1 | LDX #0 ; X=0: TX pointer index | |||
| 9BA3 | LDA (net_tx_ptr,x) ; Load control byte from TX CB | |||
| 9BA5 | .start_pass_tx←1← 9BDE BEQ | |||
| STA (net_tx_ptr,x) ; Write control byte to start TX | ||||
| 9BA7 | PHA ; Save control byte on stack | |||
| 9BA8 | JSR poll_adlc_tx_status ; Poll ADLC until line idle | |||
| 9BAB | ASL ; Shift result: check bit 6 (success) | |||
| 9BAC | BPL pass_tx_success ; Bit 6 clear: transmission complete | |||
| 9BAE | ASL ; Shift result: check bit 5 (fatal) | |||
| 9BAF | BNE restore_retry_state ; Non-zero (not fatal): retry | |||
| 9BB1 | .done_pass_retries←1← 9BD0 BEQ | |||
| LDX #0 ; X=0: clear error status | ||||
| 9BB3 | JMP fixup_reply_status_a ; Jump to fix up reply status | |||
Wait for TX ready, then start new transmission
Result in
|
||||||||||||||||
| 9BB6 | .poll_adlc_tx_status←3← 9B3F JSR← 9BA8 JSR← 9BB9 BCC | |||||||||||||||
| ASL tx_complete_flag ; Shift ws_0d60 left to poll ADLC | ||||||||||||||||
| 9BB9 | BCC poll_adlc_tx_status ; Bit not set: keep polling | |||||||||||||||
| 9BBB | LDA net_tx_ptr ; Copy TX pointer low to NMI TX block | |||||||||||||||
| 9BBD | STA nmi_tx_block ; Store in NMI TX block low | |||||||||||||||
| 9BBF | LDA net_tx_ptr_hi ; Copy TX pointer high | |||||||||||||||
| 9BC1 | STA nmi_tx_block_hi ; Store in NMI TX block high | |||||||||||||||
| 9BC3 | JSR tx_begin ; Begin Econet frame transmission | |||||||||||||||
| 9BC6 | .loop_poll_pass_tx←1← 9BC8 BMI | |||||||||||||||
| LDA (net_tx_ptr,x) ; Read TX status byte | ||||||||||||||||
| 9BC8 | BMI loop_poll_pass_tx ; Bit 7 set: still transmitting | |||||||||||||||
| 9BCA | RTS ; Return with result in A | |||||||||||||||
| 9BCB | .restore_retry_state←1← 9BAF BNE | |||||||||||||||
| PLA ; Pull control byte | ||||||||||||||||
| 9BCC | TAX ; Restore to X | |||||||||||||||
| 9BCD | PLA ; Pull timeout | |||||||||||||||
| 9BCE | TAY ; Restore to Y | |||||||||||||||
| 9BCF | PLA ; Pull retry count | |||||||||||||||
| 9BD0 | BEQ done_pass_retries ; Zero retries: go to error handling | |||||||||||||||
| 9BD2 | SBC #1 ; Decrement retry counter | |||||||||||||||
| 9BD4 | PHA ; Push updated retry count | |||||||||||||||
| 9BD5 | TYA ; Copy timeout to A | |||||||||||||||
| 9BD6 | PHA ; Push timeout | |||||||||||||||
| 9BD7 | TXA ; Copy control byte to A | |||||||||||||||
| 9BD8 | .loop_pass_tx_delay←2← 9BD9 BNE← 9BDC BNE | |||||||||||||||
| DEX ; Inner delay loop: decrement X | ||||||||||||||||
| 9BD9 | BNE loop_pass_tx_delay ; Loop until X=0 | |||||||||||||||
| 9BDB | DEY ; Decrement outer counter Y | |||||||||||||||
| 9BDC | BNE loop_pass_tx_delay ; Loop until Y=0 | |||||||||||||||
| 9BDE | BEQ start_pass_tx ; ALWAYS branch: retry transmission | |||||||||||||||
| 9BE0 | .pass_tx_success←1← 9BAC BPL | |||||||||||||||
| PLA ; Pull control byte (discard) | ||||||||||||||||
| 9BE1 | PLA ; Pull timeout (discard) | |||||||||||||||
| 9BE2 | PLA ; Pull retry count (discard) | |||||||||||||||
| 9BE3 | LDY #0 ; Y=0: start restoring from offset 0 | |||||||||||||||
| 9BE5 | .loop_restore_txbuf←1← 9BF2 BNE | |||||||||||||||
| LDX pass_txbuf_init_table,y ; Load template byte for this offset | ||||||||||||||||
| 9BE8 | CPX #&fd ; Is it &FD (skip marker)? | |||||||||||||||
| 9BEA | BEQ skip_restore_byte ; Yes: don't restore this offset | |||||||||||||||
| 9BEC | PLA ; Pull original value from stack | |||||||||||||||
| 9BED | STA (net_tx_ptr),y ; Restore original TX buffer byte | |||||||||||||||
| 9BEF | .skip_restore_byte←1← 9BEA BEQ | |||||||||||||||
| INY ; Next offset (ascending) | ||||||||||||||||
| 9BF0 | CPY #&0c ; Processed all 12 bytes? | |||||||||||||||
| 9BF2 | BNE loop_restore_txbuf ; No: continue restoring | |||||||||||||||
| 9BF4 | RTS ; Return with TX buffer restored | |||||||||||||||
Copy text pointer from FS options and parse stringReads a 2-byte address from (fs_options)+0/1 into os_text_ptr (&00F2), resets Y to zero, then falls through to gsread_to_buf to parse the string at that address into the &0E30 buffer.
|
||||
| 9BF5 | .load_text_ptr_and_parse←1← 9C25 JSR | |||
| LDY #1 ; Y=1: start at second byte of pointer | ||||
| 9BF7 | .loop_copy_text_ptr←1← 9BFD BPL | |||
| LDA (fs_options),y ; Load pointer byte from FS options | ||||
| 9BF9 | STA os_text_ptr,y ; Store in OS text pointer | |||
| 9BFC | DEY ; Decrement index | |||
| 9BFD | BPL loop_copy_text_ptr ; Loop until both bytes copied | |||
| 9BFF | INY ; Y=0: reset index for string reading | |||
| fall through ↓ | ||||
Parse command line via GSINIT/GSREAD into hazel_parse_bufCalls GSINIT to initialise string reading, then loops calling
GSREAD to copy characters into
|
|||||||
| 9C00 | .gsread_to_buf←1← B22C JSR | ||||||
| LDX #&ff ; X=&FF: pre-increment for buffer index | |||||||
| 9C02 | CLC ; C=0: initialise for string input | ||||||
| 9C03 | JSR gsinit ; GSINIT: initialise string reading | ||||||
| 9C06 | BEQ terminate_buf ; Z set (empty string): store terminator | ||||||
| 9C08 | .loop_gsread_char←1← 9C11 BCC | ||||||
| JSR gsread ; GSREAD: read next character | |||||||
| 9C0B | BCS terminate_buf ; C set: end of string reached | ||||||
| 9C0D | INX ; Advance buffer index | ||||||
| 9C0E | STA hazel_parse_buf,x ; Store character in fs_filename_buf buffer | ||||||
| 9C11 | BCC loop_gsread_char ; ALWAYS branch: read next character | ||||||
| 9C13 | .terminate_buf←2← 9C06 BEQ← 9C0B BCS | ||||||
| INX ; Advance past last character | |||||||
| 9C14 | LDA #&0d ; A=CR: terminate filename | ||||||
| 9C16 | STA hazel_parse_buf,x ; Store CR terminator in buffer | ||||||
| 9C19 | LDA #&30 ; A=&30: low byte of fs_filename_buf buffer | ||||||
| 9C1B | STA fs_crc_lo ; Set command text pointer low | ||||||
| 9C1D | LDA #&c0 ; A=&0E: high byte of fs_filename_buf buffer | ||||||
| 9C1F | STA fs_crc_hi ; Set command text pointer high | ||||||
| 9C21 | RTS ; Return with buffer filled | ||||||
FILEV vector handler: OSFILEReached via the FILEV vector at
|
||||||
| 9C22 | .filev_handler | |||||
| JSR set_xfer_params ; Set up transfer parameters | ||||||
| 9C25 | JSR load_text_ptr_and_parse ; Load text pointer and parse filename | |||||
| 9C28 | JSR mask_owner_access ; Set owner-only access mask | |||||
| 9C2B | JSR parse_access_prefix ; Parse access prefix from filename | |||||
| 9C2E | LDA fs_last_byte_flag ; Load last byte flag | |||||
| 9C30 | BPL check_display_type ; Positive (not last): display file info | |||||
| 9C32 | CMP #&ff ; Is it &FF (last entry)? | |||||
| 9C34 | BEQ copy_arg_and_enum ; Yes: copy arg and iterate | |||||
| 9C36 | JMP return_with_last_flag ; Other value: return with flag | |||||
| 9C39 | .copy_arg_and_enum←1← 9C34 BEQ | |||||
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer at X=0 | ||||||
| 9C3C | 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.
|
|||||||
| 9C3E | .do_fs_cmd_iteration←1← A615 JSR | ||||||
| LDA #&92 ; A=&92: FS port number | |||||||
| 9C40 | STA need_release_tube ; Set escapable flag to &92 | ||||||
| 9C42 | STA hazel_txcb_station ; Store port number in TX buffer | ||||||
| 9C45 | JSR send_request_write ; Send request to file server | ||||||
| 9C48 | LDY #6 ; Y=6: offset to response cycle flag | ||||||
| 9C4A | LDA (fs_options),y ; Load cycle flag from FS options | ||||||
| 9C4C | BNE copy_ws_then_fsopts ; Non-zero: already initialised | ||||||
| 9C4E | JSR copy_fsopts_to_zp ; Copy FS options to zero page first | ||||||
| 9C51 | JSR copy_workspace_to_fsopts ; Then copy workspace to FS options | ||||||
| 9C54 | BCC setup_txcb_addrs ; Branch to continue (C clear from JSR) | ||||||
| 9C56 | .copy_ws_then_fsopts←1← 9C4C BNE | ||||||
| JSR copy_workspace_to_fsopts ; Copy workspace to FS options first | |||||||
| 9C59 | JSR copy_fsopts_to_zp ; Then copy FS options to zero page | ||||||
| 9C5C | .setup_txcb_addrs←1← 9C54 BCC | ||||||
| LDY #4 ; Y=4: loop counter | |||||||
| 9C5E | .loop_copy_addrs←1← 9C69 BNE | ||||||
| LDA fs_load_addr,x ; Load address byte from zero page | |||||||
| 9C60 | STA txcb_end,x ; Save to TXCB end pointer | ||||||
| 9C62 | ADC hazel_txcb_addr_lo,x ; Add offset from buffer | ||||||
| 9C65 | STA fs_work_4,x ; Store sum in fs_work area | ||||||
| 9C67 | INX ; Advance to next byte | ||||||
| 9C68 | DEY ; Decrement counter | ||||||
| 9C69 | BNE loop_copy_addrs ; Loop for all 4 bytes | ||||||
| 9C6B | SEC ; Set carry for subtraction | ||||||
| 9C6C | SBC hazel_txcb_addr_hi ; Subtract high offset | ||||||
| 9C6F | STA fs_work_7 ; Store result in fs_work_7 | ||||||
| 9C71 | JSR format_filename_field ; Format filename for display | ||||||
| 9C74 | JSR send_txcb_swap_addrs ; Send TXCB and swap addresses | ||||||
| 9C77 | LDX #2 ; X=2: copy 3 offset bytes | ||||||
| 9C79 | .loop_copy_offsets←1← 9C80 BPL | ||||||
| LDA hazel_txcb_addr_hi,x ; Load offset byte from fs_file_len_3 | |||||||
| 9C7C | STA hazel_txcb_data,x ; Store in fs_cmd_data for next iteration | ||||||
| 9C7F | DEX ; Decrement counter | ||||||
| 9C80 | BPL loop_copy_offsets ; Loop until all bytes copied | ||||||
| 9C82 | 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.
|
||||
| 9C85 | .send_txcb_swap_addrs←2← 9C74 JSR← A26F JSR | |||
| JSR cmp_5byte_handle ; Compare 5-byte handle with current | ||||
| 9C88 | BEQ return_from_txcb_swap ; Match: no need to send, return | |||
| 9C8A | LDA #&92 ; A=&92: FS reply port number | |||
| 9C8C | STA txcb_port ; Set TXCB port | |||
| 9C8E | .loop_swap_and_send←1← 9CAA BNE | |||
| LDX #3 ; X=3: copy 4 bytes | ||||
| 9C90 | .loop_copy_start_end←1← 9C99 BPL | |||
| LDA txcb_end,x ; Load TXCB end pointer byte | ||||
| 9C92 | STA txcb_start,x ; Store in TXCB start pointer | |||
| 9C94 | LDA fs_work_4,x ; Load new end address from fs_work | |||
| 9C96 | STA txcb_end,x ; Store in TXCB end pointer | |||
| 9C98 | DEX ; Decrement counter | |||
| 9C99 | BPL loop_copy_start_end ; Loop for all 4 bytes | |||
| 9C9B | LDA #&7f ; A=&7F: control byte for data transfer | |||
| 9C9D | STA txcb_ctrl ; Set TXCB control byte | |||
| 9C9F | JSR wait_net_tx_ack ; Wait for network TX acknowledgement | |||
| 9CA2 | LDY #3 ; Y=3: compare 4 bytes | |||
| 9CA4 | .loop_verify_addrs←1← 9CAD BPL | |||
| LDA txcb_end,y ; Load TXCB end byte | ||||
| 9CA7 | EOR fs_work_4,y ; Compare with expected end address | |||
| 9CAA | BNE loop_swap_and_send ; Mismatch: resend from start | |||
| 9CAC | DEY ; Decrement counter | |||
| 9CAD | BPL loop_verify_addrs ; Loop until all 4 bytes match | |||
| 9CAF | .return_from_txcb_swap←1← 9C88 BEQ | |||
| RTS ; Return (all bytes match) | ||||
| 9CB0 | .check_display_type←1← 9C30 BPL | |||
| BEQ setup_dir_display ; Z set: directory entry display | ||||
| 9CB2 | JMP dispatch_osword_op ; Non-zero: jump to OSWORD dispatch | |||
Compute display deltas and prep FS info requestIterates 4 times over paired (lo, hi) address words in the FS options block at offsets &0E and &0A (loop body advances Y by 5 each pass). For each pair, computes (high - low), saves both originals to workspace at &00A6+Y (port_ws_offset region), and overwrites the options entry with the difference so the caller can render 'load addr', 'exec addr', 'length', etc. without redoing the subtraction. Then copies 9 bytes of FS-options metadata into the TX buffer at &C103, sets need_release_tube as the escapable flag, and stores FS port &91 (info request) at &C102. Final tail-call dispatches the request via send_request_write.
|
||||||
| 9CB5 | .setup_dir_display←2← 9CB0 BEQ← 9DE5 JMP | |||||
| LDX #4 ; X=4: loop counter for 4 iterations | ||||||
| 9CB7 | LDY #&0e ; Y=&0E: FS options offset for addresses | |||||
| 9CB9 | SEC ; Set carry for subtraction | |||||
| 9CBA | .loop_compute_diffs←1← 9CD4 BNE | |||||
| LDA (fs_options),y ; Load address byte from FS options | ||||||
| 9CBC | STA port_ws_offset,y ; Save to workspace (port_ws_offset) | |||||
| 9CBF | JSR retreat_y_by_4 ; Y -= 4 to point to paired offset | |||||
| 9CC2 | SBC (fs_options),y ; Subtract paired value | |||||
| 9CC4 | STA hazel_txcb_network,y ; Store difference in fs_cmd_csd buffer | |||||
| 9CC7 | PHA ; Push difference | |||||
| 9CC8 | LDA (fs_options),y ; Load paired value from FS options | |||||
| 9CCA | STA port_ws_offset,y ; Save to workspace | |||||
| 9CCD | PLA ; Pull difference back | |||||
| 9CCE | STA (fs_options),y ; Store in FS options for display | |||||
| 9CD0 | JSR skip_one_and_advance5 ; Advance Y by 5 for next field | |||||
| 9CD3 | DEX ; Decrement loop counter | |||||
| 9CD4 | BNE loop_compute_diffs ; Loop for all 4 address pairs | |||||
| 9CD6 | LDY #9 ; Y=9: copy 9 bytes of options data | |||||
| 9CD8 | .loop_copy_fs_options←1← 9CDE BNE | |||||
| LDA (fs_options),y ; Load FS options byte | ||||||
| 9CDA | STA hazel_txcb_network,y ; Store in fs_cmd_csd buffer | |||||
| 9CDD | DEY ; Decrement index | |||||
| 9CDE | BNE loop_copy_fs_options ; Loop until all 9 bytes copied | |||||
| 9CE0 | LDA #&91 ; A=&91: FS port for info request | |||||
| 9CE2 | STA need_release_tube ; Set escapable flag | |||||
| 9CE4 | STA hazel_txcb_station ; Store port in TX buffer | |||||
| 9CE7 | STA fs_error_ptr ; Store in fs_error_ptr | |||||
| 9CE9 | LDX #&0b ; X=&0B: copy argument at offset 11 | |||||
| 9CEB | JSR copy_arg_to_buf ; Copy argument to TX buffer | |||||
| 9CEE | LDY #1 ; Y=1: info sub-command | |||||
| 9CF0 | LDA fs_last_byte_flag ; Load last byte flag | |||||
| 9CF2 | CMP #7 ; Is it 7 (catalogue info)? | |||||
| 9CF4 | PHP ; Save comparison result | |||||
| 9CF5 | BNE send_info_request ; Not 7: keep Y=1 | |||||
| 9CF7 | LDY #&1d ; Y=&1D: extended info command | |||||
| 9CF9 | .send_info_request←1← 9CF5 BNE | |||||
| JSR send_request_write ; Send request to file server | ||||||
| 9CFC | JSR format_filename_field ; Format filename for display | |||||
| 9CFF | PLP ; Restore comparison flags | |||||
| 9D00 | BNE setup_txcb_transfer ; Not catalogue info: show short format | |||||
| 9D02 | LDX #0 ; X=0: start at first byte | |||||
| 9D04 | BEQ store_result ; ALWAYS branch to store and display | |||||
| 9D06 | .setup_txcb_transfer←1← 9D00 BNE | |||||
| LDA hazel_txcb_data ; Load file handle from fs_cmd_data | ||||||
| 9D09 | JSR check_and_setup_txcb ; Check and set up TXCB for transfer | |||||
| fall through ↓ | ||||||
Receive FS reply and stash result byteJSRs recv_and_process_reply, then falls through to store_result (STX hazel_txcb_result; LDY #&0E to point at the protection-bits offset). Single caller (the dispatch at &9C82).
|
||||||
| 9D0C | .recv_reply←1← 9C82 JMP | |||||
| JSR recv_and_process_reply ; Receive and process reply | ||||||
| 9D0F | .store_result←1← 9D04 BEQ | |||||
| STX hazel_txcb_result ; Store result byte in fs_reply_cmd | ||||||
| 9D12 | LDY #&0e ; Y=&0E: protection bits offset | |||||
| 9D14 | LDA hazel_txcb_data ; Load access byte from fs_cmd_data | |||||
| 9D17 | JSR get_prot_bits ; Extract protection bit flags | |||||
| 9D1A | BEQ store_prot_byte ; Zero: use reply buffer data | |||||
| 9D1C | .loop_copy_file_info←1← 9D24 BNE | |||||
| LDA hazel_fs_reply_byte,y ; Load file info byte from fs_reply_data | ||||||
| 9D1F | .store_prot_byte←1← 9D1A BEQ | |||||
| STA (fs_options),y ; Store in FS options at offset Y | ||||||
| 9D21 | INY ; Advance to next byte | |||||
| 9D22 | CPY #&12 ; Y=&12: end of protection fields? | |||||
| 9D24 | BNE loop_copy_file_info ; No: copy next byte | |||||
| 9D26 | LDY hazel_fs_messages_flag ; Load display flag from fs_messages_flag | |||||
| 9D29 | BEQ return_from_advance_y ; Zero: skip display, return | |||||
| 9D2B | LDY #&f4 ; Y=&F4: index into hazel_display_buf for filename | |||||
| 9D2D | .loop_print_filename←1← 9D34 BNE | |||||
| LDA hazel_display_buf_minusF4,y ; Load filename character from filename_buf | ||||||
| 9D30 | JSR print_char_no_spool ; Print character via OSASCI | |||||
| 9D33 | INY ; Advance to next character | |||||
| 9D34 | BNE loop_print_filename ; Printed all 12 characters? | |||||
| 9D36 | LDY #5 ; Y=5: offset for access string | |||||
| 9D38 | JSR print_5_hex_bytes ; Print 5 hex bytes (access info) | |||||
| 9D3B | JSR print_load_exec_addrs ; Print load and exec addresses | |||||
| 9D3E | JSR print_newline_no_spool ; Print newline | |||||
| 9D41 | 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.
|
||||
| 9D44 | .print_load_exec_addrs←1← 9D3B JSR | |||
| LDY #9 ; Y=9: offset for exec address | ||||
| 9D46 | JSR print_5_hex_bytes ; Print 5 hex bytes (exec address) | |||
| 9D49 | LDY #&0c ; Y=&0C: offset for length (3 bytes) | |||
| 9D4B | LDX #3 ; X=3: print 3 bytes only | |||
| 9D4D | BNE loop_print_hex_byte ; ALWAYS branch to print routine | |||
Print hex byte sequence from FS optionsOutputs The default entry with
|
||||||
| 9D4F | .print_5_hex_bytes←2← 9D38 JSR← 9D46 JSR | |||||
| LDX #4 ; X=4: print 5 bytes (4 to 0) | ||||||
| 9D51 | .loop_print_hex_byte←2← 9D4D BNE← 9D58 BNE | |||||
| LDA (fs_options),y ; Load byte from FS options at offset Y | ||||||
| 9D53 | JSR print_hex_byte_no_spool ; Print as 2-digit hex | |||||
| 9D56 | DEY ; Decrement byte offset | |||||
| 9D57 | DEX ; Decrement byte count | |||||
| 9D58 | BNE loop_print_hex_byte ; Loop until all bytes printed | |||||
| 9D5A | LDA #&20 ; A=' ': space separator | |||||
| 9D5C | JMP print_char_no_spool ; Print space via OSASCI and return | |||||
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.
|
|||||||||
| 9D5F | .copy_fsopts_to_zp←2← 9C4E JSR← 9C59 JSR | ||||||||
| LDY #5 ; Y=5: copy 4 bytes (offsets 2-5) | |||||||||
| 9D61 | .loop_copy_fsopts_byte←1← 9D69 BCS | ||||||||
| LDA (fs_options),y ; Load byte from FS options | |||||||||
| 9D63 | STA work_ae,y ; Store in zero page at work_ae+Y | ||||||||
| 9D66 | DEY ; Decrement index | ||||||||
| 9D67 | CPY #2 ; Below offset 2? | ||||||||
| 9D69 | 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.
|
|||||||||
| 9D6B | .skip_one_and_advance5←1← 9CD0 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.
|
|||||||
| 9D6C | .advance_y_by_4←2← A24C JSR← B432 JSR | ||||||
| INY ; Y += 4 | |||||||
| 9D6D | INY ; (continued) | ||||||
| 9D6E | INY ; (continued) | ||||||
| 9D6F | INY ; (continued) | ||||||
| 9D70 | .return_from_advance_y←1← 9D29 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.
|
|||||||||
| 9D71 | .copy_workspace_to_fsopts←2← 9C51 JSR← 9C56 JSR | ||||||||
| LDY #&0d ; Y=&0D: copy bytes from offset &0D down | |||||||||
| 9D73 | TXA ; Transfer X to A | ||||||||
| 9D74 | .loop_copy_ws_byte←1← 9D7C BCS | ||||||||
| STA (fs_options),y ; Store byte in FS options at offset Y | |||||||||
| 9D76 | LDA hazel_txcb_station,y ; Load next workspace byte from fs_cmd_urd+Y | ||||||||
| 9D79 | DEY ; Decrement index | ||||||||
| 9D7A | CPY #2 ; Below offset 2? | ||||||||
| 9D7C | 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.
|
|||||||
| 9D7E | .retreat_y_by_4←1← 9CBF 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.
|
|||||||
| 9D7F | .retreat_y_by_3←2← 9DFB JSR← A254 JSR | ||||||
| DEY ; Y -= 3 | |||||||
| 9D80 | DEY ; (continued) | ||||||
| 9D81 | DEY ; (continued) | ||||||
| 9D82 | RTS ; Return | ||||||
| 9D83 | .discard_handle_match←2← 9D8B BEQ← 9DD1 BCS | ||||||
| PLA ; Discard stacked value | |||||||
| 9D84 | LDY fs_block_offset ; Restore Y from fs_block_offset | ||||||
| 9D86 | RTS ; Return (handle already matches) | ||||||
Set up data-transfer TXCB and dispatch replyCompares the 5-byte handle via
|
||||
| 9D87 | .check_and_setup_txcb←2← 9D09 JSR← A26A JSR | |||
| PHA ; Save port/sub-function on stack | ||||
| 9D88 | JSR cmp_5byte_handle ; Compare 5-byte handle with current | |||
| 9D8B | BEQ discard_handle_match ; Match: discard port and return | |||
| 9D8D | .init_transfer_addrs←1← 9DDA BNE | |||
| LDX #0 ; X=0: loop start | ||||
| 9D8F | LDY #4 ; Y=4: copy 4 bytes | |||
| 9D91 | STX hazel_txcb_result ; Clear fs_reply_cmd (transfer size low) | |||
| 9D94 | STX hazel_exec_addr ; Clear fs_load_vector (transfer size high) | |||
| 9D97 | CLC ; Clear carry for addition | |||
| 9D98 | .loop_copy_addr_offset←1← 9DA5 BNE | |||
| LDA fs_load_addr,x ; Load address byte from zero page | ||||
| 9D9A | STA txcb_start,x ; Store in TXCB start pointer | |||
| 9D9C | ADC hazel_txcb_flag,x ; Add offset from fs_func_code | |||
| 9D9F | STA txcb_end,x ; Store sum in TXCB end pointer | |||
| 9DA1 | STA fs_load_addr,x ; Also update load address | |||
| 9DA3 | INX ; Advance to next byte | |||
| 9DA4 | DEY ; Decrement counter | |||
| 9DA5 | BNE loop_copy_addr_offset ; Loop for all 4 bytes | |||
| 9DA7 | BCS clamp_end_to_limit ; Carry set: overflow, use limit | |||
| 9DA9 | SEC ; Set carry for subtraction | |||
| 9DAA | .loop_check_vs_limit←1← 9DB2 BNE | |||
| LDA fs_load_addr,y ; Load computed end address | ||||
| 9DAD | SBC fs_work_4,y ; Subtract maximum from fs_work_4 | |||
| 9DB0 | INY ; Advance to next byte | |||
| 9DB1 | DEX ; Decrement counter | |||
| 9DB2 | BNE loop_check_vs_limit ; Loop for all bytes | |||
| 9DB4 | BCC set_port_and_ctrl ; Below limit: keep computed end | |||
| 9DB6 | .clamp_end_to_limit←1← 9DA7 BCS | |||
| LDX #3 ; X=3: copy 4 bytes of limit | ||||
| 9DB8 | .loop_copy_limit←1← 9DBD BPL | |||
| LDA fs_work_4,x ; Load limit from fs_work_4 | ||||
| 9DBA | STA txcb_end,x ; Store as TXCB end | |||
| 9DBC | DEX ; Decrement counter | |||
| 9DBD | BPL loop_copy_limit ; Loop for all 4 bytes | |||
| 9DBF | .set_port_and_ctrl←1← 9DB4 BCC | |||
| PLA ; Pull port from stack | ||||
| 9DC0 | PHA ; Push back (keep for later) | |||
| 9DC1 | PHP ; Save flags (carry = overflow state) | |||
| 9DC2 | STA txcb_port ; Set TXCB port number | |||
| 9DC4 | LDA #&80 ; A=&80: control byte for data request | |||
| 9DC6 | STA txcb_ctrl ; Set TXCB control byte | |||
| 9DC8 | JSR init_tx_ptr_and_send ; Init TX pointer and send packet | |||
| 9DCB | LDA fs_error_ptr ; Load error pointer | |||
| 9DCD | JSR init_txcb_port ; Init TXCB port from error pointer | |||
| 9DD0 | PLP ; Restore overflow flags | |||
| 9DD1 | BCS discard_handle_match ; Carry set: discard and return | |||
| 9DD3 | LDA #&91 ; A=&91: FS reply port | |||
| 9DD5 | STA txcb_port ; Set TXCB port for reply | |||
| 9DD7 | JSR wait_net_tx_ack ; Wait for TX acknowledgement | |||
| 9DDA | BNE init_transfer_addrs ; Non-zero (not done): retry send | |||
| fall through ↓ | ||||
OSWORD &13 sub-operation triage (1-7)Stores the sub-operation code in
Single caller (
|
||||||||||||
| 9DDC | .dispatch_osword_op←1← 9CB2 JMP | |||||||||||
| STA hazel_txcb_data ; Store sub-operation code | ||||||||||||
| 9DDF | CMP #7 ; Compare with 7 | |||||||||||
| 9DE1 | BCC dispatch_ops_1_to_6 ; Below 7: handle operations 1-6 | |||||||||||
| 9DE3 | BNE skip_if_error ; Above 7: jump to handle via finalise | |||||||||||
| 9DE5 | JMP setup_dir_display ; Equal to 7: jump to directory display | |||||||||||
| 9DE8 | .dispatch_ops_1_to_6←1← 9DE1 BCC | |||||||||||
| CMP #6 ; Compare with 6 | ||||||||||||
| 9DEA | BEQ send_delete_request ; 6: delete file operation | |||||||||||
| 9DEC | CMP #5 ; Compare with 5 | |||||||||||
| 9DEE | BEQ read_cat_info ; 5: read catalogue info | |||||||||||
| 9DF0 | CMP #4 ; Compare with 4 | |||||||||||
| 9DF2 | BEQ setup_write_access ; 4: write file attributes | |||||||||||
| 9DF4 | CMP #1 ; Compare with 1 | |||||||||||
| 9DF6 | BEQ setup_save_access ; 1: read file info | |||||||||||
| 9DF8 | ASL ; Shift left twice: A*4 | |||||||||||
| 9DF9 | ASL ; A*4 | |||||||||||
| 9DFA | TAY ; Copy to Y as index | |||||||||||
| 9DFB | JSR retreat_y_by_3 ; Y -= 3 to get FS options offset | |||||||||||
| 9DFE | LDX #3 ; X=3: copy 4 bytes | |||||||||||
| 9E00 | .loop_copy_fsopts_4←1← 9E07 BPL | |||||||||||
| LDA (fs_options),y ; Load byte from FS options at offset Y | ||||||||||||
| 9E02 | STA hazel_txcb_flag,x ; Store in fs_func_code buffer | |||||||||||
| 9E05 | DEY ; Decrement source offset | |||||||||||
| 9E06 | DEX ; Decrement byte count | |||||||||||
| 9E07 | BPL loop_copy_fsopts_4 ; Loop for all 4 bytes | |||||||||||
| 9E09 | LDX #5 ; X=5: copy arg to buffer at offset 5 | |||||||||||
| 9E0B | BNE send_save_or_access ; ALWAYS branch to copy and send | |||||||||||
| 9E0D | .setup_save_access←1← 9DF6 BEQ | |||||||||||
| JSR get_access_bits ; Get access bits for file | ||||||||||||
| 9E10 | STA hazel_txcb_access ; Store access byte in fs_file_attrs | |||||||||||
| 9E13 | LDY #9 ; Y=9: source offset in FS options | |||||||||||
| 9E15 | LDX #8 ; X=8: copy 8 bytes to buffer | |||||||||||
| 9E17 | .loop_copy_fsopts_8←1← 9E1E BNE | |||||||||||
| LDA (fs_options),y ; Load FS options byte | ||||||||||||
| 9E19 | STA hazel_txcb_data,x ; Store in fs_cmd_data buffer | |||||||||||
| 9E1C | DEY ; Decrement source offset | |||||||||||
| 9E1D | DEX ; Decrement byte count | |||||||||||
| 9E1E | BNE loop_copy_fsopts_8 ; Loop for all 8 bytes | |||||||||||
| 9E20 | LDX #&0a ; X=&0A: buffer offset for argument | |||||||||||
| 9E22 | .send_save_or_access←2← 9E0B BNE← 9E41 BNE | |||||||||||
| JSR copy_arg_to_buf ; Copy argument to buffer | ||||||||||||
| 9E25 | LDY #&13 ; Y=&13: OSWORD &13 (NFS operation) | |||||||||||
| 9E27 | BNE send_request_vset ; ALWAYS branch to send request | |||||||||||
| 9E29 | .send_delete_request←1← 9DEA BEQ | |||||||||||
| JSR copy_arg_to_buf_x0 ; Copy argument to buffer at X=0 | ||||||||||||
| 9E2C | LDY #&14 ; Y=&14: delete file command | |||||||||||
| 9E2E | .send_request_vset←1← 9E27 BNE | |||||||||||
| BIT always_set_v_byte ; Set V flag (no directory check) | ||||||||||||
| 9E31 | JSR save_net_tx_cb_vset ; Send request with V set | |||||||||||
| 9E34 | .skip_if_error←1← 9DE3 BNE | |||||||||||
| BCS done_osword_op ; Carry set: error, jump to finalise | ||||||||||||
| 9E36 | JMP return_with_last_flag ; No error: return with last flag | |||||||||||
| 9E39 | .setup_write_access←1← 9DF2 BEQ | |||||||||||
| JSR get_access_bits ; Get access bits for file | ||||||||||||
| 9E3C | STA hazel_txcb_flag ; Store in fs_func_code | |||||||||||
| 9E3F | LDX #2 ; X=2: buffer offset | |||||||||||
| 9E41 | BNE send_save_or_access ; ALWAYS branch to copy and send | |||||||||||
| 9E43 | .read_cat_info←1← 9DEE BEQ | |||||||||||
| LDX #1 ; X=1: buffer offset | ||||||||||||
| 9E45 | JSR copy_arg_to_buf ; Copy argument to buffer | |||||||||||
| 9E48 | LDY #&12 ; Y=&12: open file command | |||||||||||
| 9E4A | JSR save_net_tx_cb ; Send open file request | |||||||||||
| 9E4D | LDA hazel_txcb_len ; Load reply handle from fs_obj_type | |||||||||||
| 9E50 | STX hazel_txcb_len ; Clear fs_obj_type | |||||||||||
| 9E53 | STX hazel_txcb_cycle ; Clear fs_len_clear | |||||||||||
| 9E56 | JSR get_prot_bits ; Get protection bits | |||||||||||
| 9E59 | LDX hazel_txcb_data ; Load file handle from fs_cmd_data | |||||||||||
| 9E5C | BEQ return_with_handle ; Zero: file not found, return | |||||||||||
| 9E5E | LDY #&0e ; Y=&0E: store access bits | |||||||||||
| 9E60 | STA (fs_options),y ; Store access byte in FS options | |||||||||||
| 9E62 | DEY ; Y=&0D | |||||||||||
| 9E63 | LDX #&0c ; X=&0C: copy 12 bytes of file info | |||||||||||
| 9E65 | .loop_copy_cat_info←1← 9E6C BNE | |||||||||||
| LDA hazel_txcb_data,x ; Load reply byte from fs_cmd_data+X | ||||||||||||
| 9E68 | STA (fs_options),y ; Store in FS options at offset Y | |||||||||||
| 9E6A | DEY ; Decrement destination offset | |||||||||||
| 9E6B | DEX ; Decrement source counter | |||||||||||
| 9E6C | BNE loop_copy_cat_info ; Loop for all 12 bytes | |||||||||||
| 9E6E | INX ; X=1 (INX from 0) | |||||||||||
| 9E6F | INX ; X=2 | |||||||||||
| 9E70 | LDY #&11 ; Y=&11: FS options offset | |||||||||||
| 9E72 | .loop_copy_ext_info←1← 9E79 BPL | |||||||||||
| LDA hazel_txcb_type,x ; Load extended info byte from fs_access_level | ||||||||||||
| 9E75 | STA (fs_options),y ; Store in FS options | |||||||||||
| 9E77 | DEY ; Decrement destination offset | |||||||||||
| 9E78 | DEX ; Decrement source counter | |||||||||||
| 9E79 | BPL loop_copy_ext_info ; Loop until all copied | |||||||||||
| 9E7B | LDX hazel_txcb_data ; Reload file handle | |||||||||||
| 9E7E | .return_with_handle←1← 9E5C BEQ | |||||||||||
| TXA ; Transfer to A | ||||||||||||
| 9E7F | .done_osword_op←1← 9E34 BCS | |||||||||||
| JMP finalise_and_return ; Jump to finalise and return | ||||||||||||
Format filename into fixed-width display fieldBuilds a 12-character space-padded filename at
|
||||
| 9E82 | .format_filename_field←2← 9C71 JSR← 9CFC JSR | |||
| LDY #0 ; Y=0: start writing at filename_buf[0] | ||||
| 9E84 | LDX hazel_txcb_network ; Load source offset from fs_cmd_csd | |||
| 9E87 | BNE copy_from_buf_entry ; Non-zero: copy from fs_cmd_data buffer | |||
| 9E89 | .loop_copy_cmdline_char←1← 9E93 BNE | |||
| LDA (fs_crc_lo),y ; Load character from command line | ||||
| 9E8B | CMP #&21 ; Below '!' (control/space)? | |||
| 9E8D | BCC pad_with_spaces ; Yes: pad with spaces | |||
| 9E8F | STA hazel_display_buf,y ; Store printable character in filename_buf | |||
| 9E92 | INY ; Advance to next character | |||
| 9E93 | BNE loop_copy_cmdline_char ; Loop for more characters | |||
| 9E95 | .pad_with_spaces←2← 9E8D BCC← 9E9D BCC | |||
| LDA #&20 ; A=' ': space for padding | ||||
| 9E97 | STA hazel_display_buf,y ; Store space in display buffer | |||
| 9E9A | INY ; Advance index | |||
| 9E9B | CPY #&0c ; Filled all 12 characters? | |||
| 9E9D | BCC pad_with_spaces ; No: pad more spaces | |||
| 9E9F | RTS ; Return with field formatted | |||
| 9EA0 | .loop_copy_buf_char←1← 9EA8 BPL | |||
| INX ; Advance source and destination | ||||
| 9EA1 | INY | |||
| 9EA2 | .copy_from_buf_entry←1← 9E87 BNE | |||
| LDA hazel_txcb_data,x ; Load byte from fs_cmd_data buffer | ||||
| 9EA5 | STA hazel_display_buf,y ; Store in filename_buf | |||
| 9EA8 | BPL loop_copy_buf_char ; Bit 7 clear: more characters | |||
| 9EAA | RTS ; Return (bit 7 set = terminator) | |||
ARGSV vector handler: OSARGSReached via the ARGSV vector at
|
||||||||
| 9EAB | .argsv_handler | |||||||
| JSR verify_ws_checksum ; Verify workspace checksum | ||||||||
| 9EAE | STA fs_last_byte_flag ; Store result as last byte flag | |||||||
| 9EB0 | JSR set_options_ptr ; Set FS options pointer | |||||||
| 9EB3 | ORA #0 ; OR with 0 to set flags | |||||||
| 9EB5 | BPL dispatch_osfind_op ; Positive: handle sub-operations | |||||||
| 9EB7 | ASL ; Shift left to check bit 6 | |||||||
| 9EB8 | BEQ validate_chan_close ; Zero (was &80): close channel | |||||||
| 9EBA | JMP close_all_fcbs ; Other: process all FCBs first | |||||||
| 9EBD | .validate_chan_close←1← 9EB8 BEQ | |||||||
| TYA ; Transfer Y to A | ||||||||
| 9EBE | CMP #&20 ; Compare with &20 (space) | |||||||
| 9EC0 | BCS check_chan_range ; Above &20: check further | |||||||
| 9EC2 | .error_invalid_chan←1← 9EC7 BCS | |||||||
| JMP err_net_chan_invalid ; Below &20: invalid channel char | ||||||||
| 9EC5 | .check_chan_range←1← 9EC0 BCS | |||||||
| CMP #&30 ; Compare with '0' | ||||||||
| 9EC7 | BCS error_invalid_chan ; Above '0': invalid channel char | |||||||
| 9EC9 | JSR process_all_fcbs ; Process all matching FCBs | |||||||
| 9ECC | TYA ; Transfer Y to A (FCB index) | |||||||
| 9ECD | PHA ; Push FCB index | |||||||
| 9ECE | TAX ; Copy to X | |||||||
| 9ECF | LDY #0 ; Y=0: clear counter | |||||||
| 9ED1 | STY fs_last_byte_flag ; Clear last byte flag | |||||||
| 9ED3 | STY fs_block_offset ; Clear block offset | |||||||
| 9ED5 | .loop_copy_fcb_fields←1← 9EE0 BNE | |||||||
| LDA hazel_fcb_addr_mid,x ; Load channel data from fcb_attr_or_count_mid+X | ||||||||
| 9ED8 | STA (fs_options),y ; Store in FS options at Y | |||||||
| 9EDA | JSR advance_x_by_8 ; Advance X by 8 (next FCB field) | |||||||
| 9EDD | INY ; Advance destination index | |||||||
| 9EDE | CPY #4 ; Copied all 4 channel fields? | |||||||
| 9EE0 | BNE loop_copy_fcb_fields ; No: copy next field | |||||||
| 9EE2 | PLA ; Pull saved FCB index | |||||||
| 9EE3 | STA fs_block_offset ; Restore to fs_block_offset | |||||||
| 9EE5 | .dispatch_osfind_op←1← 9EB5 BPL | |||||||
| CMP #5 ; Compare with 5 | ||||||||
| 9EE7 | BCS done_return_flag ; 5 or above: return with last flag | |||||||
| 9EE9 | CPY #0 ; Compare Y with 0 | |||||||
| 9EEB | BNE osfind_with_channel ; Non-zero: handle OSFIND with channel | |||||||
| 9EED | JMP osfind_close_or_open ; Y=0 (close): jump to OSFIND open | |||||||
| 9EF0 | .osfind_with_channel←1← 9EEB BNE | |||||||
| PHA ; Push sub-function | ||||||||
| 9EF1 | TXA ; Transfer X to A | |||||||
| 9EF2 | PHA ; Push X (FCB slot) | |||||||
| 9EF3 | TYA ; Transfer Y to A | |||||||
| 9EF4 | PHA ; Push Y (channel char) | |||||||
| 9EF5 | JSR check_not_dir ; Check file is not a directory | |||||||
| 9EF8 | PLA ; Pull channel char | |||||||
| 9EF9 | JSR store_rx_attribute ; Store channel char as receive attribute | |||||||
| 9EFC | LDA hazel_fcb_slot_attr,x ; Load FCB flag byte from fcb_net_or_port | |||||||
| 9EFF | STA hazel_txcb_data ; Store in fs_cmd_data | |||||||
| 9F02 | PLA ; Pull X (FCB slot) | |||||||
| 9F03 | TAX ; Restore X | |||||||
| 9F04 | PLA ; Pull sub-function | |||||||
| 9F05 | LSR ; Shift right: check bit 0 | |||||||
| 9F06 | BEQ osargs_ptr_dispatch ; Zero (OSFIND close): handle close | |||||||
| 9F08 | PHP ; Save flags (carry from LSR) | |||||||
| 9F09 | PHA ; Push sub-function | |||||||
| 9F0A | LDX fs_options ; Load FS options pointer low | |||||||
| 9F0C | LDY fs_block_offset ; Load block offset | |||||||
| 9F0E | JSR process_all_fcbs ; Process all matching FCBs | |||||||
| 9F11 | LDA hazel_fcb_addr_mid,y ; Load updated data from fcb_attr_or_count_mid | |||||||
| 9F14 | STA hazel_txcb_data ; Store in fs_cmd_data | |||||||
| 9F17 | PLA ; Pull sub-function | |||||||
| 9F18 | STA hazel_txcb_flag ; Store in fs_func_code | |||||||
| 9F1B | PLP ; Restore flags | |||||||
| 9F1C | TYA ; Transfer Y to A | |||||||
| 9F1D | PHA ; Push Y (offset) | |||||||
| 9F1E | BCC osargs_read_op ; Carry clear: read operation | |||||||
| 9F20 | LDY #3 ; Y=3: copy 4 bytes | |||||||
| 9F22 | .loop_copy_zp_to_buf←1← 9F29 BPL | |||||||
| LDA zp_work_3,x ; Load zero page data | ||||||||
| 9F24 | STA hazel_txcb_count,y ; Store in fs_data_count buffer | |||||||
| 9F27 | DEX ; Decrement source | |||||||
| 9F28 | DEY ; Decrement counter | |||||||
| 9F29 | BPL loop_copy_zp_to_buf ; Loop for all 4 bytes | |||||||
| 9F2B | LDY #&0d ; Y=&0D: TX buffer size | |||||||
| 9F2D | LDX #5 ; X=5: argument offset | |||||||
| 9F2F | JSR save_net_tx_cb ; Send TX control block to server | |||||||
| 9F32 | STX fs_last_byte_flag ; Store X in last byte flag | |||||||
| 9F34 | PLA ; Pull saved offset | |||||||
| 9F35 | JSR set_conn_active ; Set connection active flag | |||||||
| 9F38 | .done_return_flag←1← 9EE7 BCS | |||||||
| JMP return_with_last_flag ; Return with last flag | ||||||||
| 9F3B | .osargs_read_op←1← 9F1E BCC | |||||||
| LDY #&0c ; Y=&0C: TX buffer size (smaller) | ||||||||
| 9F3D | LDX #2 ; X=2: argument offset | |||||||
| 9F3F | JSR save_net_tx_cb ; Send TX control block | |||||||
| 9F42 | STA fs_last_byte_flag ; Store A in last byte flag | |||||||
| 9F44 | LDX fs_options ; Load FS options pointer low | |||||||
| 9F46 | LDY #2 ; Y=2: zero page offset | |||||||
| 9F48 | STA zp_work_3,x ; Store A in zero page | |||||||
| 9F4A | .loop_copy_reply_to_zp←1← 9F51 BPL | |||||||
| LDA hazel_txcb_data,y ; Load buffer byte from fs_cmd_data+Y | ||||||||
| 9F4D | STA zp_work_2,x ; Store in zero page at offset | |||||||
| 9F4F | DEX ; Decrement source X | |||||||
| 9F50 | DEY ; Decrement counter Y | |||||||
| 9F51 | BPL loop_copy_reply_to_zp ; Loop until all bytes copied | |||||||
| 9F53 | PLA ; Pull saved offset | |||||||
| 9F54 | JMP return_with_last_flag ; Return with last flag | |||||||
| 9F57 | .osargs_ptr_dispatch←1← 9F06 BEQ | |||||||
| BCS osargs_write_ptr ; Carry set: write file pointer | ||||||||
| 9F59 | LDA fs_block_offset ; Load block offset | |||||||
| 9F5B | JSR attr_to_chan_index ; Convert attribute to channel index | |||||||
| 9F5E | LDY fs_options ; Load FS options pointer | |||||||
| 9F60 | LDA hazel_fcb_addr_lo,x ; Load FCB low byte from fcb_count_lo | |||||||
| 9F63 | STA zp_ptr_lo,y ; Store in zero page pointer low | |||||||
| 9F66 | LDA hazel_fcb_addr_mid,x ; Load FCB high byte from fcb_attr_or_count_mid | |||||||
| 9F69 | STA zp_ptr_hi,y ; Store in zero page pointer high | |||||||
| 9F6C | LDA hazel_fcb_addr_hi,x ; Load FCB extent from fcb_station_or_count_hi | |||||||
| 9F6F | STA zp_work_2,y ; Store in zero page work area | |||||||
| 9F72 | LDA #0 ; A=0: clear high byte | |||||||
| 9F74 | STA zp_work_3,y ; Store zero in work area high | |||||||
| 9F77 | BEQ return_with_last_flag ; ALWAYS branch to return with flag | |||||||
| 9F79 | .osargs_write_ptr←1← 9F57 BCS | |||||||
| STA hazel_txcb_flag ; Store write value in fs_func_code | ||||||||
| 9F7C | TXA ; Transfer X to A | |||||||
| 9F7D | PHA ; Push X (zero page offset) | |||||||
| 9F7E | LDY #3 ; Y=3: copy 4 bytes | |||||||
| 9F80 | .loop_copy_ptr_to_buf←1← 9F87 BPL | |||||||
| LDA zp_work_3,x ; Load zero page data at offset | ||||||||
| 9F82 | STA hazel_txcb_count,y ; Store in fs_data_count buffer | |||||||
| 9F85 | DEX ; Decrement source | |||||||
| 9F86 | DEY ; Decrement counter | |||||||
| 9F87 | BPL loop_copy_ptr_to_buf ; Loop for all 4 bytes | |||||||
| 9F89 | LDY #&0d ; Y=&0D: TX buffer size | |||||||
| 9F8B | LDX #5 ; X=5: argument offset | |||||||
| 9F8D | JSR save_net_tx_cb ; Send TX control block | |||||||
| 9F90 | STX fs_last_byte_flag ; Store X in last byte flag | |||||||
| 9F92 | PLA ; Pull saved zero page offset | |||||||
| 9F93 | TAY ; Transfer to Y | |||||||
| 9F94 | LDA fs_block_offset ; Load block offset (attribute) | |||||||
| 9F96 | JSR clear_conn_active ; Clear connection active flag | |||||||
| 9F99 | JSR attr_to_chan_index ; Convert attribute to channel index | |||||||
| 9F9C | LDA zp_ptr_lo,y ; Load zero page pointer low | |||||||
| 9F9F | STA hazel_fcb_addr_lo,x ; Store back to FCB fcb_count_lo | |||||||
| 9FA2 | LDA zp_ptr_hi,y ; Load zero page pointer high | |||||||
| 9FA5 | STA hazel_fcb_addr_mid,x ; Store back to FCB fcb_attr_or_count_mid | |||||||
| 9FA8 | LDA zp_work_2,y ; Load zero page work byte | |||||||
| 9FAB | STA hazel_fcb_addr_hi,x ; Store back to FCB fcb_station_or_count_hi | |||||||
| 9FAE | JMP return_with_last_flag ; Return with last flag | |||||||
Close all FCBs (process_all_fcbs + finalise)Single-instruction wrapper: JSR process_all_fcbs to walk every FCB slot and close each open file in turn, then fall through to return_with_last_flag (which loads fs_last_byte_flag and finalises caller state). Single caller (the OSFIND close-all path at &9EBA).
|
||||
| 9FB1 | .close_all_fcbs←1← 9EBA JMP | |||
| JSR process_all_fcbs ; Process all matching FCBs first | ||||
| fall through ↓ | ||||
Load last-byte flag and finaliseLoads fs_last_byte_flag (&BD) into A and falls through to finalise_and_return, which clears the receive-attribute byte and restores caller's X/Y. The 12 inbound refs are mostly fall-through exits from FS reply handlers that need to return the last-byte status to their caller; only one site (&9FAE) reaches it via JSR.
|
||||||||
| 9FB4 | .return_with_last_flag←12← 9C36 JMP← 9D41 JMP← 9E36 JMP← 9F38 JMP← 9F54 JMP← 9F77 BEQ← 9FAE JMP← A09C JMP← A15F JMP← A63B JMP← A641 JMP← A6FB JMP | |||||||
| LDA fs_last_byte_flag ; Load last byte flag | ||||||||
| fall through ↓ | ||||||||
Clear receive-attribute and restore caller's X/YCommon 7-byte exit sequence used at the end of format_filename_field, several FS reply handlers, and match_fs_cmd. Saves A across a call to store_rx_attribute(0) (which clears the receive-attribute byte), then restores X from fs_options and Y from fs_block_offset before returning. Effectively: 'finish processing, clear network state, restore caller's pointers'. One JSR caller (match_fs_cmd at &A599) plus 6 branch entries from format_filename_field's various exit paths.
|
|||||||||||
| 9FB6 | .finalise_and_return←7← 9E7F JMP← 9FCB BNE← 9FD2 BPL← A068 JMP← A1EC JMP← A53B JMP← A599 JSR | ||||||||||
| PHA ; Push result on stack | |||||||||||
| 9FB7 | LDA #0 ; A=0: clear error flag | ||||||||||
| 9FB9 | JSR store_rx_attribute ; Clear receive attribute (A=0) | ||||||||||
| 9FBC | PLA ; Pull result back | ||||||||||
| 9FBD | LDX fs_options ; Restore X from FS options pointer | ||||||||||
| 9FBF | LDY fs_block_offset ; Restore Y from block offset | ||||||||||
| 9FC1 | RTS ; Return to caller | ||||||||||
OSFIND dispatch: close-all, close-one, or openTriages the OSFIND function code in
Single caller (the OSFIND vector table at
|
||||||||||||||||
| 9FC2 | .osfind_close_or_open←1← 9EED JMP | |||||||||||||||
| CMP #2 ; Compare with 2 (open for output) | ||||||||||||||||
| 9FC4 | BCS done_file_open ; 2 or above: handle file open | |||||||||||||||
| 9FC6 | TAY ; Transfer to Y (Y=0 or 1) | |||||||||||||||
| 9FC7 | BNE done_file_open ; Non-zero (1 = read pointer): copy data | |||||||||||||||
| 9FC9 | LDA #5 ; A=5: return code for close-all | |||||||||||||||
| 9FCB | BNE finalise_and_return ; ALWAYS branch to finalise | |||||||||||||||
| 9FCD | .done_file_open←2← 9FC4 BCS← 9FC7 BNE | |||||||||||||||
| BEQ shift_and_finalise ; Z set: jump to clear A and return | ||||||||||||||||
| fall through ↓ | ||||||||||||||||
Set A=0 and finaliseLoads A=0 and falls through to shift_and_finalise (LSR A / BPL finalise_and_return). The LSR-then-BPL is the standard FS-handler 'success exit with carry clear' idiom. Two callers: the post- return path at &9FD6 and the catalogue tail at tail_update_ catalogue (&A329).
|
||||||
| 9FCF | .clear_result←2← 9FD6 BNE← A329 JMP | |||||
| LDA #0 ; A=0: clear result | ||||||
| 9FD1 | .shift_and_finalise←1← 9FCD BEQ | |||||
| LSR ; Shift right (always positive) | ||||||
| 9FD2 | BPL finalise_and_return ; Positive: jump to finalise | |||||
| 9FD4 | .alloc_fcb_for_open←1← A042 BEQ | |||||
| AND #&3f ; Mask to 6-bit access value | ||||||
| 9FD6 | BNE clear_result ; Non-zero: clear A and finalise | |||||
| 9FD8 | TXA ; Transfer X to A (options pointer) | |||||
| 9FD9 | JSR alloc_fcb_or_error ; Allocate FCB slot or raise error | |||||
| 9FDC | EOR #&80 ; Toggle bit 7 | |||||
| 9FDE | ASL ; Shift left: build open mode | |||||
| 9FDF | STA hazel_txcb_data ; Store open mode in fs_cmd_data | |||||
| 9FE2 | ROL ; Rotate to complete mode byte | |||||
| 9FE3 | STA hazel_txcb_flag ; Store in fs_func_code | |||||
| 9FE6 | JSR parse_cmd_arg_y0 ; Parse command argument (Y=0) | |||||
| 9FE9 | LDX #2 ; X=2: buffer offset | |||||
| 9FEB | JSR copy_arg_to_buf ; Copy argument to TX buffer | |||||
| fall through ↓ | ||||||
| ; Send file open request with V flag set for directory check. | ||||||
| 9FEE | .send_open_file_request←1← 9709 JSR | |||||
| LDY #6 ; Y=6: open file command | ||||||
| 9FF0 | BIT always_set_v_byte ; Set V flag (skip directory check) | |||||
| 9FF3 | SEC ; Set carry | |||||
| 9FF4 | ROR need_release_tube ; Rotate carry into escapable flag bit 7 | |||||
| 9FF6 | JSR save_net_tx_cb_vset ; Send open request with V set | |||||
| 9FF9 | BCS done_osfind ; Carry set (error): jump to finalise | |||||
| 9FFB | LDA #&ff ; A=&FF: mark as newly opened | |||||
| 9FFD | JSR store_rx_attribute ; Store &FF as receive attribute | |||||
| A000 | LDA hazel_txcb_data ; Load handle from fs_cmd_data | |||||
| A003 | PHA ; Push handle | |||||
| A004 | LDA #4 ; A=4: file info sub-command | |||||
| A006 | STA hazel_txcb_data ; Store sub-command | |||||
| A009 | LDX #1 ; X=1: shift filename | |||||
| A00B | .loop_shift_filename←1← A014 BNE | |||||
| LDA hazel_txcb_flag,x ; Load filename byte from fs_func_code+X | ||||||
| A00E | STA hazel_txcb_data,x ; Shift down to fs_cmd_data+X | |||||
| A011 | INX ; Advance source index | |||||
| A012 | CMP #&0d ; Is it CR (end of filename)? | |||||
| A014 | BNE loop_shift_filename ; No: continue shifting | |||||
| A016 | LDY #&12 ; Y=&12: file info request | |||||
| A018 | JSR save_net_tx_cb ; Send file info request | |||||
| A01B | LDA fs_last_byte_flag ; Load last byte flag | |||||
| A01D | AND #&bf ; Clear bit 6 (read/write bits) | |||||
| A01F | ORA hazel_txcb_data ; OR with reply access byte | |||||
| A022 | ORA #1 ; Set bit 0 (file is open) | |||||
| A024 | TAY ; Transfer to Y (access flags) | |||||
| A025 | AND #2 ; Check bit 1 (write access) | |||||
| A027 | BEQ check_open_mode ; No write access: check read-only | |||||
| A029 | PLA ; Pull handle from stack | |||||
| A02A | JSR alloc_fcb_slot ; Allocate FCB slot for channel | |||||
| A02D | BNE store_fcb_flags ; Non-zero: FCB allocated, store flags | |||||
| A02F | .findv_handler←1← 8E95 JMP | |||||
| JSR verify_ws_checksum ; Verify workspace checksum | ||||||
| A032 | JSR set_xfer_params ; Set up transfer parameters | |||||
| A035 | TAX ; Transfer A to X | |||||
| A036 | JSR mask_owner_access ; Set owner-only access mask | |||||
| A039 | TXA ; Transfer X back to A | |||||
| A03A | BEQ close_all_channels ; Zero: close file, process FCBs | |||||
| A03C | JSR save_ptr_to_os_text ; Save text pointer for OS | |||||
| A03F | LDY hazel_cur_dir_handle ; Load current directory handle | |||||
| A042 | BEQ alloc_fcb_for_open ; Zero: allocate new FCB | |||||
| A044 | TYA ; Transfer Y to A | |||||
| A045 | LDX #0 ; X=0: clear directory handle | |||||
| A047 | STX hazel_cur_dir_handle ; Store zero (clear handle) | |||||
| A04A | BEQ done_osfind ; ALWAYS branch to finalise | |||||
| A04C | .check_open_mode←1← A027 BEQ | |||||
| LDA hazel_txcb_flag ; Load access/open mode byte | ||||||
| A04F | ROR ; Rotate right: check bit 0 | |||||
| A050 | BCS alloc_fcb_with_flags ; Carry set (bit 0): check read permission | |||||
| A052 | ROR ; Rotate right: check bit 1 | |||||
| A053 | BCC alloc_fcb_with_flags ; Carry clear (no write): skip | |||||
| A055 | BIT hazel_txcb_count ; Test bit 7 of fs_data_count (lock flag) | |||||
| A058 | BPL alloc_fcb_with_flags ; Not locked: skip | |||||
| A05A | TYA ; Transfer Y to A (flags) | |||||
| A05B | ORA #&20 ; Set bit 5 (locked file flag) | |||||
| A05D | TAY ; Transfer back to Y | |||||
| A05E | .alloc_fcb_with_flags←3← A050 BCS← A053 BCC← A058 BPL | |||||
| PLA ; Pull handle from stack | ||||||
| A05F | JSR alloc_fcb_slot ; Allocate FCB slot for channel | |||||
| A062 | .store_fcb_flags←1← A02D BNE | |||||
| TAX ; Transfer to X | ||||||
| A063 | TYA ; Transfer Y to A (flags) | |||||
| A064 | STA hazel_fcb_state_byte,x ; Store flags in FCB table fcb_flags | |||||
| A067 | TXA ; Transfer X back to A (handle) | |||||
| A068 | .done_osfind←2← 9FF9 BCS← A04A BEQ | |||||
| JMP finalise_and_return ; Jump to finalise and return | ||||||
| A06B | .close_all_channels←1← A03A BEQ | |||||
| JSR process_all_fcbs ; Process all matching FCBs | ||||||
| A06E | TYA ; Transfer Y to A | |||||
| A06F | BNE close_specific_chan ; Non-zero channel: close specific | |||||
| A071 | LDA fs_options ; Load FS options pointer low | |||||
| A073 | PHA ; Push (save for restore) | |||||
| A074 | LDA #osbyte_close_spool_exec ; A=&77: OSBYTE close spool/exec files | |||||
| A076 | JSR osbyte ; Close any *SPOOL and *EXEC files | |||||
| A079 | PLA ; Pull saved options pointer | |||||
| A07A | STA fs_options ; Restore FS options pointer | |||||
| A07C | LDA #0 ; A=0: clear flags | |||||
| A07E | STA fs_last_byte_flag ; Save to fs_work_5 | |||||
| A080 | STA fs_block_offset ; Load current FS station low | |||||
| A082 | BEQ send_close_request ; ALWAYS branch to send close request Save to fs_work_6 | |||||
| A084 | .close_specific_chan←1← A06F BNE | |||||
| JSR check_chan_char ; Validate channel character | ||||||
| A087 | LDA hazel_fcb_slot_attr,x ; Is it CR (no argument)? | |||||
| A08A | .send_close_request←1← A082 BEQ | |||||
| STA hazel_txcb_data ; Store as fs_cmd_data (file handle) | ||||||
| A08D | LDX #1 ; X=1: argument size | |||||
| A08F | LDY #7 ; Y=7: close file command | |||||
| A091 | JSR save_net_tx_cb ; Send close file request | |||||
| A094 | LDY fs_block_offset ; Parameter block low | |||||
| A096 | BNE clear_single_fcb ; Parameter block high | |||||
| A098 | CLV ; Clear V flag | |||||
| A099 | JSR scan_fcb_flags ; Scan and clear all FCB flags | |||||
| A09C | .done_close←4← A0A7 BEQ← A0B9 BCC← A0CD BPL← A101 BRA | |||||
| JMP return_with_last_flag ; Return with last flag | ||||||
| A09F | .clear_single_fcb←1← A096 BNE | |||||
| LDA #0 ; A=0: clear FCB entry | ||||||
| A0A1 | STA hazel_fcb_addr_mid,y ; Clear hazel_fcb_addr_mid for slot Y | |||||
| A0A4 | STA hazel_fcb_state_byte,y ; Clear hazel_fcb_state_byte for slot Y | |||||
| A0A7 | BEQ done_close ; Z still set from LDA #0: always branch to done_close | |||||
| fall through ↓ | ||||||
FSCV reason 0: read OSARGSHandles OSARGS via the FSCV vector. If
|
||||||
| A0A9 | .fscv_0_opt_entry | |||||
| BEQ store_display_flag ; A=0 (init sub-code): jump to store_display_flag | ||||||
| A0AB | CPX #4 ; Non-zero A: X==4? (read OSARGS args) | |||||
| A0AD | BNE osargs_dispatch ; X != 4: take normal OSARGS dispatch | |||||
| A0AF | CPY #4 ; X==4 path: Y < 4? | |||||
| A0B1 | BCC send_osargs_request ; Yes: send OSARGS request via TXCB | |||||
| A0B3 | .osargs_dispatch←1← A0AD BNE | |||||
| DEX ; X-- (osargs_dispatch entry): step sub-code down | ||||||
| A0B4 | BNE osargs_store_ptr_lo ; X != 1: take store-ptr-lo path | |||||
| A0B6 | .store_display_flag←1← A0A9 BEQ | |||||
| STY hazel_fs_messages_flag ; Store Y as hazel_fs_messages_flag (display control) | ||||||
| A0B9 | BCC done_close ; Tail-branch to done_close | |||||
| A0BB | .error_osargs←2← A0D1 BCS← A0DD BCS | |||||
| LDA #7 ; A=7: error code (out-of-range OSARGS sub-code) | ||||||
| A0BD | JMP classify_reply_error ; Raise BRK error | |||||
| A0C0 | .send_osargs_request←1← A0B1 BCC | |||||
| STY hazel_txcb_data ; Store Y as TXCB data byte (OSARGS payload) | ||||||
| A0C3 | LDY #&16 ; Y=&16: TXCB function code (OSARGS request) | |||||
| A0C5 | JSR save_net_tx_cb ; Send OSARGS request via TX control block | |||||
| A0C8 | LDY fs_block_offset ; Reload Y from fs_block_offset | |||||
| A0CA | STY hazel_fs_flags ; Update hazel_fs_flags from OSARGS reply | |||||
| A0CD | BPL done_close ; No error (positive): tail to done_close | |||||
| A0CF | .osargs_store_ptr_lo←1← A0B4 BNE | |||||
| CPX #8 ; X >= 8? | ||||||
| A0D1 | BCS error_osargs ; Yes: out-of-range OSARGS sub-code | |||||
| A0D3 | CPX #4 ; X == 4? | |||||
| A0D5 | BEQ osargs_check_length ; Yes: take fast read path (osargs_check_length) | |||||
| A0D7 | CPY #4 ; Y < 4? | |||||
| A0D9 | BCC osopt_check_cmos_protect ; Yes: take CMOS-protect path | |||||
| A0DB | .osargs_check_length←1← A0D5 BEQ | |||||
| CPY #2 ; Y >= 2? | ||||||
| A0DD | BCS error_osargs ; Yes: argument out of range | |||||
| A0DF | .osopt_check_cmos_protect←1← A0D9 BCC | |||||
| PHY | ||||||
| A0E0 | PHX ; Save sub-code across the CMOS read | |||||
| A0E1 | LDX #&11 ; X=&11: CMOS RAM byte index | |||||
| A0E3 | JSR osbyte_a1 ; Read CMOS &11 (Econet status) -> Y | |||||
| A0E6 | PLX ; Restore sub-code | |||||
| A0E7 | TYA ; Read CMOS &11 result to A | |||||
| A0E8 | AND cmos_opt_mask_table,x ; Mask CMOS &11 with cmos_opt_mask_table[X] | |||||
| A0EB | PLY | |||||
| A0EC | PHA ; Push CMOS value | |||||
| A0ED | LDA cmos_attr_table,x ; Load shift count from cmos_attr_table[X] | |||||
| A0F0 | TAX ; Value to X | |||||
| A0F1 | TYA ; Caller's Y back to A as the value to shift | |||||
| A0F2 | .loop_extract_attribute_bits←1← A0F4 BNE | |||||
| ASL ; Shift CMOS bits | ||||||
| A0F3 | DEX ; Count down shift iterations | |||||
| A0F4 | BNE loop_extract_attribute_bits ; Loop until X reaches 0 | |||||
| A0F6 | STA fs_load_addr ; Stash shifted value in fs_load_addr scratch | |||||
| A0F8 | PLA ; Pop saved value | |||||
| A0F9 | ORA fs_load_addr ; OR with the CMOS-masked value | |||||
| A0FB | TAY | |||||
| A0FC | LDX #&11 ; X=&11: target CMOS byte for write-back | |||||
| A0FE | .osopt_cmos_writeback_jsr | |||||
| JSR osbyte_a2 ; Write CMOS RAM byte (Y) to byte index (X) | ||||||
| A101 | BRA done_close ; Tail-branch into the OSARGS done path | |||||
| fall through ↓ | ||||||
CMOS &11 bit-field masks for OSARGS / *OPT 4 (8 bytes)Used by the OSARGS-via-FSCV / *OPT 4 path
(
A second indexed-base trick reads the same eight bytes through
|
|
| A103 | .cmos_opt_mask_table←1← A0E8 AND |
| EQUB &01 ; Idx 0: AND mask = &01 (extract CMOS &11 bit 0) | |
| A104 | EQUB &02 ; Idx 1: AND mask = &02 (extract CMOS &11 bit 1) |
| A105 | EQUB &04 ; Idx 2: AND mask = &04 (extract CMOS &11 bit 2) |
| A106 | EQUB &06 ; Idx 3: AND mask = &06 (extract CMOS &11 bits 1,2) |
| A107 | EQUB &FD ; Idx 4: AND mask = &FD (clear CMOS &11 bit 1) |
| A108 | EQUB &F3 ; Idx 5: AND mask = &F3 (clear CMOS &11 bits 2,3) |
| A109 | EQUB &CF ; Idx 6: AND mask = &CF (clear CMOS &11 bits 4,5) |
| A10A | EQUB &3F ; Idx 7: AND mask = &3F (clear CMOS &11 bits 6,7) |
FSCV reason 1: EOF checkVerifies the FS workspace checksum, then loads the channel's
block-offset byte (
|
|||||||
| A10B | .fscv_1_eof | ||||||
| JSR verify_ws_checksum ; Verify workspace checksum | |||||||
| A10E | PHA ; Push checksum-verify result -- preserve it across the FCB lookups below | ||||||
| A10F | LDA fs_block_offset ; Load block offset | ||||||
| A111 | PHA ; Push block offset | ||||||
| A112 | STX hazel_chan_attr ; Store X in cur_chan_attr | ||||||
| A115 | JSR find_matching_fcb ; Find matching FCB entry | ||||||
| A118 | BEQ mark_not_found ; Zero: no match found | ||||||
| A11A | LDA hazel_fcb_addr_lo,y ; Load FCB low byte from fcb_count_lo | ||||||
| A11D | CMP hazel_fcb_offset_save,x ; Compare with stored offset fcb_buf_offset | ||||||
| A120 | BCC mark_not_found ; FCB lo-byte below stored offset -> not the matching FCB; mark_not_found | ||||||
| A122 | LDX #&ff ; X=&FF: mark as found (all bits set) | ||||||
| A124 | BMI restore_and_return ; ALWAYS branch (negative) | ||||||
| A126 | .mark_not_found←2← A118 BEQ← A120 BCC | ||||||
| LDX #0 ; X=0: mark as not found | |||||||
| A128 | .restore_and_return←1← A124 BMI | ||||||
| PLA ; Restore block offset from stack | |||||||
| A129 | TAY ; Generate 'Syntax' error | ||||||
| A12A | PLA ; Restore result from stack | ||||||
| A12B | RTS ; Return | ||||||
Update both address fields in FS optionsCalls
|
||||
| A12C | .update_addr_from_offset9←1← A277 JSR | |||
| LDY #9 ; Y=9: FS options offset for high address | ||||
| A12E | 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.
|
||||
| A131 | .update_addr_from_offset1←1← A370 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.
|
||||
| A133 | .add_workspace_to_fsopts←1← A12E JSR | |||
| CLC ; Clear carry for the upcoming 4-byte add | ||||
| fall through ↓ | ||||
Add or subtract 4 workspace bytes from FS optionsProcesses 4 consecutive bytes at The direction is controlled by bit 7 of
Carry propagates across all 4 bytes for correct multi-byte arithmetic.
|
||||||||||||
| A134 | .adjust_fsopts_4bytes←2← A27D JSR← A37C JSR | |||||||||||
| LDX #&fc ; X=&FC: loop counter (-4 to -1) | ||||||||||||
| A136 | .loop_adjust_byte←1← A149 BNE | |||||||||||
| LDA (fs_options),y ; Load FS options byte at offset Y | ||||||||||||
| A138 | BIT fs_load_addr_2 ; Test fs_load_addr_2 bit 7 (add/subtract) | |||||||||||
| A13A | BMI subtract_ws_byte ; Push high byte | |||||||||||
| A13C | ADC hazel_fs_opts_addend,x ; Add workspace byte to FS options | |||||||||||
| A13F | JMP store_adjusted_byte ; RTS dispatches to command handler | |||||||||||
| A142 | .subtract_ws_byte←1← A13A BMI | |||||||||||
| SBC hazel_fs_opts_addend,x ; Subtract workspace byte from FS options | ||||||||||||
| fall through ↓ | ||||||||||||
Store adjusted byte and step the loopTail of the address-adjustment 4-byte loop: STA (fs_options),Y / INY / INX / BNE loop_adjust_byte / RTS. The BNE retries until X has cycled through all 4 bytes; once X overflows back to 0 the loop exits and the RTS returns. Single caller (the loop-body fall- through at &A13F).
|
||||||||
| A145 | .store_adjusted_byte←1← A13F JMP | |||||||
| STA (fs_options),y ; Store result back to FS options | ||||||||
| A147 | INY ; Advance to next byte | |||||||
| A148 | INX ; Advance counter | |||||||
| A149 | BNE loop_adjust_byte ; Loop until 4 bytes processed | |||||||
| A14B | RTS ; Return | |||||||
GBPBV vector handler: OSGBPBReached via the GBPBV vector at
|
||||||||||||||||||||||||
| A14C | .gbpbv_handler | |||||||||||||||||||||||
| JSR verify_ws_checksum ; Verify workspace checksum | ||||||||||||||||||||||||
| A14F | JSR set_xfer_params ; Set up transfer parameters | |||||||||||||||||||||||
| A152 | PHA ; Push transfer type on stack | |||||||||||||||||||||||
| A153 | JSR mask_owner_access ; Set owner-only access mask | |||||||||||||||||||||||
| A156 | PLA ; Pull transfer type | |||||||||||||||||||||||
| A157 | TAX ; Transfer to X | |||||||||||||||||||||||
| A158 | BEQ skip_if_out_of_range ; Zero: no valid operation, return | |||||||||||||||||||||||
| A15A | DEX ; Decrement (convert 1-based to 0-based) | |||||||||||||||||||||||
| A15B | CPX #8 ; Compare with 8 (max operation) | |||||||||||||||||||||||
| A15D | BCC valid_osgbpb_op ; Below 8: valid operation | |||||||||||||||||||||||
| A15F | .skip_if_out_of_range←1← A158 BEQ | |||||||||||||||||||||||
| JMP return_with_last_flag ; Out of range: return with flag | ||||||||||||||||||||||||
| A162 | .valid_osgbpb_op←1← A15D BCC | |||||||||||||||||||||||
| TXA ; Transfer operation code to A | ||||||||||||||||||||||||
| A163 | LDY #0 ; Y=0: buffer offset | |||||||||||||||||||||||
| A165 | PHA ; Push operation code | |||||||||||||||||||||||
| A166 | CMP #4 ; Compare with 4 (write operations) | |||||||||||||||||||||||
| A168 | BCC load_chan_handle ; Below 4: read operation | |||||||||||||||||||||||
| A16A | JMP write_block_entry ; 4 or above: write data block | |||||||||||||||||||||||
| A16D | .load_chan_handle←1← A168 BCC | |||||||||||||||||||||||
| LDA (fs_options),y ; Load channel handle from FS options | ||||||||||||||||||||||||
| A16F | PHA ; Push handle | |||||||||||||||||||||||
| A170 | JSR check_not_dir ; Check file is not a directory | |||||||||||||||||||||||
| A173 | PLA ; Pull handle | |||||||||||||||||||||||
| A174 | TAY ; Transfer to Y | |||||||||||||||||||||||
| A175 | JSR process_all_fcbs ; Process all matching FCBs | |||||||||||||||||||||||
| A178 | LDA hazel_fcb_slot_attr,x ; Load FCB flag byte from fcb_net_or_port | |||||||||||||||||||||||
| A17B | STA hazel_txcb_data ; Store file handle in fs_cmd_data | |||||||||||||||||||||||
| A17E | LDA #0 ; A=0: clear direction flag | |||||||||||||||||||||||
| A180 | STA hazel_txcb_flag ; Store in fs_func_code | |||||||||||||||||||||||
| A183 | LDA hazel_fcb_addr_lo,x ; Load FCB low byte (position) | |||||||||||||||||||||||
| A186 | STA hazel_txcb_count ; Store in fs_data_count | |||||||||||||||||||||||
| A189 | LDA hazel_fcb_addr_mid,x ; Load FCB high byte | |||||||||||||||||||||||
| A18C | STA hazel_txcb_result ; Store in fs_reply_cmd | |||||||||||||||||||||||
| A18F | LDA hazel_fcb_addr_hi,x ; Load FCB extent byte | |||||||||||||||||||||||
| A192 | STA hazel_exec_addr ; Store in fs_load_vector | |||||||||||||||||||||||
| A195 | LDY #&0d ; Y=&0D: TX buffer size | |||||||||||||||||||||||
| A197 | LDX #5 ; X=5: argument count | |||||||||||||||||||||||
| A199 | JSR save_net_tx_cb ; Send TX control block to server | |||||||||||||||||||||||
| A19C | PLA ; Pull operation code | |||||||||||||||||||||||
| A19D | JSR setup_transfer_workspace ; Set up transfer workspace | |||||||||||||||||||||||
| A1A0 | PHP ; Save flags (carry from setup) | |||||||||||||||||||||||
| A1A1 | LDY #0 ; Y=0: index for channel handle | |||||||||||||||||||||||
| A1A3 | LDA (fs_options),y ; Load channel handle from FS options | |||||||||||||||||||||||
| A1A5 | BCS set_write_active ; Carry set (write): set active | |||||||||||||||||||||||
| A1A7 | JSR clear_conn_active ; Read: clear connection active | |||||||||||||||||||||||
| A1AA | BPL setup_gbpb_request ; Branch to continue (always positive) | |||||||||||||||||||||||
| A1AC | .set_write_active←1← A1A5 BCS | |||||||||||||||||||||||
| JSR set_conn_active ; Write: set connection active | ||||||||||||||||||||||||
| A1AF | .setup_gbpb_request←1← A1AA BPL | |||||||||||||||||||||||
| STY hazel_txcb_flag ; Clear fs_func_code (Y=0) | ||||||||||||||||||||||||
| A1B2 | JSR lookup_cat_slot_data ; Look up channel slot data | |||||||||||||||||||||||
| A1B5 | STA hazel_txcb_data ; Store flag byte in fs_cmd_data | |||||||||||||||||||||||
| A1B8 | LDY #&0c ; Y=&0C: TX buffer size (short) | |||||||||||||||||||||||
| A1BA | LDX #2 ; X=2: argument count | |||||||||||||||||||||||
| A1BC | JSR save_net_tx_cb ; Send TX control block | |||||||||||||||||||||||
| A1BF | JSR lookup_cat_entry_0 ; Look up channel entry at Y=0 | |||||||||||||||||||||||
| A1C2 | LDY #9 ; Y=9: FS options offset for position | |||||||||||||||||||||||
| A1C4 | LDA hazel_txcb_data ; Load new position low from fs_cmd_data | |||||||||||||||||||||||
| A1C7 | STA hazel_fcb_addr_lo,x ; Update FCB low byte in fcb_count_lo | |||||||||||||||||||||||
| A1CA | STA (fs_options),y ; Store in FS options at Y=9 | |||||||||||||||||||||||
| A1CC | INY ; Y=&0A | |||||||||||||||||||||||
| A1CD | LDA hazel_txcb_flag ; Load new position high from fs_func_code | |||||||||||||||||||||||
| A1D0 | STA hazel_fcb_addr_mid,x ; Update FCB high byte in fcb_attr_or_count_mid | |||||||||||||||||||||||
| A1D3 | STA (fs_options),y ; Store in FS options at Y=&0A | |||||||||||||||||||||||
| A1D5 | INY ; Y=&0B | |||||||||||||||||||||||
| A1D6 | LDA hazel_txcb_count ; Load new extent from fs_data_count | |||||||||||||||||||||||
| A1D9 | STA hazel_fcb_addr_hi,x ; Update FCB extent in fcb_station_or_count_hi | |||||||||||||||||||||||
| A1DC | STA (fs_options),y ; Store in FS options at Y=&0B | |||||||||||||||||||||||
| A1DE | LDA #0 ; A=0: clear high byte of extent | |||||||||||||||||||||||
| A1E0 | INY ; Y=&0C | |||||||||||||||||||||||
| A1E1 | STA (fs_options),y ; Store zero in FS options at Y=&0C | |||||||||||||||||||||||
| A1E3 | PLP ; Restore flags | |||||||||||||||||||||||
| A1E4 | BCC return_success ; Carry clear: skip last-byte check | |||||||||||||||||||||||
| A1E6 | LDA fs_last_byte_flag ; Load last-byte-of-transfer flag | |||||||||||||||||||||||
| A1E8 | CMP #3 ; Is transfer still pending (flag=3)? | |||||||||||||||||||||||
| A1EA | .return_success←1← A1E4 BCC | |||||||||||||||||||||||
| LDA #0 ; A=0: success | ||||||||||||||||||||||||
| A1EC | 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.
|
||||||
| A1EF | .lookup_cat_entry_0←2← A1BF JSR← A1FB JSR | |||||
| LDY #0 ; Y=0: offset for channel handle | ||||||
| A1F1 | LDA (fs_options),y ; Load channel handle from FS options | |||||
| fall through ↓ | ||||||
Look up channel and return FCB flag byteCalls
|
|||||||||
| A1F3 | .lookup_cat_slot_data←1← A1B2 JSR | ||||||||
| JSR lookup_chan_by_char ; Look up channel by character | |||||||||
| A1F6 | LDA hazel_fcb_slot_attr,x ; Load slot-attribute byte from hazel_fcb_slot_attr,X | ||||||||
| A1F9 | RTS ; Return with flag in A | ||||||||
Prepare workspace for OSGBPB data transferOrchestrates the setup for OSGBPB (get/put multiple bytes) operations:
|
|||||||||||||
| A1FA | .setup_transfer_workspace←2← A19D JSR← BD18 JMP | ||||||||||||
| PHA ; Push operation code on stack | |||||||||||||
| A1FB | JSR lookup_cat_entry_0 ; Look up channel entry at Y=0 | ||||||||||||
| A1FE | STA hazel_txcb_data ; Store flag byte in fs_cmd_data | ||||||||||||
| A201 | LDY #&0b ; Y=&0B: source offset in FS options | ||||||||||||
| A203 | LDX #6 ; X=6: copy 6 bytes | ||||||||||||
| A205 | .loop_copy_opts_to_buf←1← A211 BNE | ||||||||||||
| LDA (fs_options),y ; Load FS options byte | |||||||||||||
| A207 | STA hazel_txcb_flag,x ; Store in fs_func_code buffer | ||||||||||||
| A20A | DEY ; Decrement source index | ||||||||||||
| A20B | CPY #8 ; Skip offset 8? | ||||||||||||
| A20D | BNE skip_struct_hole ; No: continue copy | ||||||||||||
| A20F | DEY ; Skip offset 8 (hole in structure) | ||||||||||||
| A210 | .skip_struct_hole←1← A20D BNE | ||||||||||||
| DEX ; Decrement destination counter | |||||||||||||
| A211 | BNE loop_copy_opts_to_buf ; Loop until all 6 bytes copied | ||||||||||||
| A213 | PLA ; Pull operation code | ||||||||||||
| A214 | LSR ; Shift right: check bit 0 (direction) | ||||||||||||
| A215 | PHA ; Push updated code | ||||||||||||
| A216 | BCC store_direction_flag ; Carry clear: OSBGET (read) | ||||||||||||
| A218 | INX ; Carry set: OSBPUT (write), X=1 | ||||||||||||
| A219 | .store_direction_flag←1← A216 BCC | ||||||||||||
| STX hazel_txcb_flag ; Store direction flag in fs_func_code | |||||||||||||
| A21C | LDY #&0b ; Y=&0B: TX buffer size | ||||||||||||
| A21E | LDX #&91 ; X=&91: port for OSBGET | ||||||||||||
| A220 | PLA ; Pull operation code | ||||||||||||
| A221 | PHA ; Push back (keep on stack) | ||||||||||||
| A222 | BEQ store_port_and_send ; Zero (OSBGET): keep port &91 | ||||||||||||
| A224 | LDX #&92 ; X=&92: port for OSBPUT | ||||||||||||
| A226 | DEY ; Y=&0A: adjusted buffer size | ||||||||||||
| A227 | .store_port_and_send←1← A222 BEQ | ||||||||||||
| STX hazel_txcb_station ; Store port in fs_cmd_urd | |||||||||||||
| A22A | STX fs_error_ptr ; Store port in fs_error_ptr | ||||||||||||
| A22C | LDX #8 ; X=8: argument count | ||||||||||||
| A22E | LDA hazel_txcb_data ; Load file handle from fs_cmd_data | ||||||||||||
| A231 | JSR send_request_nowrite ; Send request (no write data) | ||||||||||||
| A234 | LDX #0 ; X=0: index | ||||||||||||
| A236 | LDA (fs_options,x) ; Load channel handle from FS options | ||||||||||||
| A238 | TAX ; Transfer to X as index | ||||||||||||
| A239 | LDA hazel_fcb_state_byte,x ; Load FCB flags from fcb_flags | ||||||||||||
| A23C | EOR #1 ; Toggle bit 0 (transfer direction) | ||||||||||||
| A23E | STA hazel_fcb_state_byte,x ; Store updated flags | ||||||||||||
| A241 | CLC ; Clear carry for addition | ||||||||||||
| A242 | LDX #4 ; X=4: process 4 address bytes | ||||||||||||
| A244 | .loop_setup_addr_bytes←1← A258 BNE | ||||||||||||
| LDA (fs_options),y ; Load FS options address byte | |||||||||||||
| A246 | STA addr_work,y ; Store in zero page address area | ||||||||||||
| A249 | STA txcb_pos,y ; Store in TXCB position | ||||||||||||
| A24C | JSR advance_y_by_4 ; Advance Y by 4 | ||||||||||||
| A24F | ADC (fs_options),y ; Add offset from FS options | ||||||||||||
| A251 | STA addr_work,y ; Store computed end address | ||||||||||||
| A254 | JSR retreat_y_by_3 ; Retreat Y by 3 for next pair | ||||||||||||
| A257 | DEX ; Decrement byte counter | ||||||||||||
| A258 | BNE loop_setup_addr_bytes ; Loop for all 4 address bytes | ||||||||||||
| A25A | INX ; X=1 (INX from 0) | ||||||||||||
| A25B | .loop_copy_offset←1← A262 BPL | ||||||||||||
| LDA hazel_txcb_network,x ; Load offset from fs_cmd_csd | |||||||||||||
| A25E | STA hazel_txcb_flag,x ; Copy to fs_func_code | ||||||||||||
| A261 | DEX ; Decrement counter | ||||||||||||
| A262 | BPL loop_copy_offset ; Loop until both bytes copied | ||||||||||||
| A264 | PLA ; Pull operation code | ||||||||||||
| A265 | BNE send_with_swap ; Non-zero (OSBPUT): swap addresses | ||||||||||||
| A267 | LDA hazel_txcb_station ; Load port from fs_cmd_urd | ||||||||||||
| A26A | JSR check_and_setup_txcb ; Check and set up TXCB | ||||||||||||
| A26D | BCS recv_and_update ; Carry set: skip swap | ||||||||||||
| A26F | .send_with_swap←1← A265 BNE | ||||||||||||
| JSR send_txcb_swap_addrs ; Send TXCB and swap start/end addresses | |||||||||||||
| A272 | .recv_and_update←1← A26D BCS | ||||||||||||
| JSR recv_reply_preserve_flags ; Receive and process reply | |||||||||||||
| A275 | STX fs_load_addr_2 ; Store result in fs_load_addr_2 | ||||||||||||
| A277 | JSR update_addr_from_offset9 ; Update addresses from offset 9 | ||||||||||||
| A27A | DEC fs_load_addr_2 ; Decrement fs_load_addr_2 | ||||||||||||
| A27C | SEC ; Set carry for subtraction | ||||||||||||
| A27D | JSR adjust_fsopts_4bytes ; Adjust FS options by 4 bytes | ||||||||||||
| A280 | ASL hazel_txcb_data ; Shift fs_cmd_data left (update status) | ||||||||||||
| A283 | 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.
|
||||||
| A284 | .recv_reply_preserve_flags←1← A272 JSR | |||||
| PHP ; Save flags before reply processing | ||||||
| A285 | JSR recv_and_process_reply ; Process server reply | |||||
| A288 | PLP ; Restore flags after reply processing | |||||
| A289 | RTS ; Return | |||||
Send OSBPUT data block to file serverSets |
|
| A28A | .send_osbput_data←1← A2BA BEQ |
| LDY #&15 ; Y=&15: TX buffer size for OSBPUT data | |
| A28C | JSR save_net_tx_cb ; Send TX control block |
| A28F | LDA hazel_fs_flags ; Load display flag from hazel_fs_flags |
| A292 | STA hazel_txcb_byte_16 ; Store in hazel_txcb_byte_16 |
| A295 | STX fs_load_addr ; Clear fs_load_addr (X=0) |
| A297 | STX fs_load_addr_hi ; Clear fs_load_addr_hi |
| A299 | LDA #&12 ; A=&12: byte count for data block |
| A29B | STA fs_load_addr_2 ; Store in fs_load_addr_2 |
| A29D | BNE write_data_block ; ALWAYS branch to write data block |
Pre-write Tube-station check, fall into write_data_blockY=4 (FS-options offset for station). If tube_present is zero (no Tube co-pro), branch forward to store_station_result and skip the next compare; otherwise CMP (fs_options),Y to validate the caller's station matches the saved Tube station. Falls through to write_data_block. Single caller (&A16A in the OSWORD write path).
|
||||
| A29F | .write_block_entry←1← A16A JMP | |||
| LDY #4 ; Y=4: offset for station comparison | ||||
| A2A1 | LDA tube_present ; Load stored station from tube_present | |||
| A2A4 | BEQ store_station_result ; Zero: skip station check | |||
| A2A6 | CMP (fs_options),y ; Compare with FS options station | |||
| A2A8 | BNE store_station_result ; Mismatch: skip subtraction | |||
| A2AA | DEY ; Y=3 | |||
| A2AB | SBC (fs_options),y ; Subtract FS options value | |||
| A2AD | .store_station_result←2← A2A4 BEQ← A2A8 BNE | |||
| STA svc_state ; Store result in svc_state | ||||
| A2AF | .loop_copy_opts_to_ws←1← A2B5 BNE | |||
| LDA (fs_options),y ; Load FS options byte at Y | ||||
| A2B1 | STA fs_last_byte_flag,y ; Store in workspace at fs_last_byte_flag+Y | |||
| A2B4 | DEY ; Decrement index | |||
| A2B5 | BNE loop_copy_opts_to_ws ; Loop until all bytes copied | |||
| A2B7 | PLA ; Pull operation code | |||
| A2B8 | AND #3 ; Mask to 2-bit sub-operation | |||
| A2BA | BEQ send_osbput_data ; Zero: send OSBPUT data | |||
| A2BC | LSR ; Shift right: check bit 0 | |||
| A2BD | BEQ handle_cat_update ; Zero (bit 0 clear): handle read | |||
| A2BF | BCS update_cat_position ; Carry set: handle catalogue update | |||
| A2C1 | .handle_cat_update←1← A2BD BEQ | |||
| TAY ; Transfer to Y (Y=0) | ||||
| A2C2 | LDA hazel_fs_context_copy,y ; Load data byte from fs_csd_handle | |||
| A2C5 | STA hazel_txcb_network ; Store in fs_cmd_csd | |||
| A2C8 | LDA hazel_fs_prefix_stn ; Load high data byte from fs_lib_handle | |||
| A2CB | STA hazel_txcb_lib ; Store in fs_cmd_lib | |||
| A2CE | LDA hazel_fs_saved_station ; Load port from fs_urd_handle | |||
| A2D1 | STA hazel_txcb_station ; Store in fs_cmd_urd | |||
| A2D4 | LDX #&12 ; X=&12: buffer size marker | |||
| A2D6 | STX hazel_txcb_func_code ; Store in fs_cmd_y_param | |||
| A2D9 | LDA #&0d ; A=&0D: count value | |||
| A2DB | STA hazel_txcb_flag ; Store in fs_func_code | |||
| A2DE | STA fs_load_addr_2 ; Store in fs_load_addr_2 | |||
| A2E0 | LSR ; Shift right (A=6) | |||
| A2E1 | STA hazel_txcb_data ; Store in fs_cmd_data | |||
| A2E4 | CLC ; Clear carry for addition | |||
| A2E5 | JSR prep_send_tx_cb ; Prepare and send TX control block | |||
| A2E8 | STX fs_load_addr_hi ; Store X in fs_load_addr_hi (X=0) | |||
| A2EA | INX ; X=1 (after INX) | |||
| A2EB | STX fs_load_addr ; Store X in fs_load_addr | |||
| fall through ↓ | ||||
Write data block to destination or Tube
|
||||||||||
| A2ED | .write_data_block←2← A29D BNE← A365 JSR | |||||||||
| LDA svc_state ; Load svc_state (tube flag) | ||||||||||
| A2EF | BNE tube_write_setup ; Non-zero: write via tube | |||||||||
| A2F1 | LDX fs_load_addr ; Load source index from fs_load_addr | |||||||||
| A2F3 | LDY fs_load_addr_hi ; Load destination index from fs_load_addr_hi | |||||||||
| A2F5 | .loop_copy_to_host←1← A2FE BNE | |||||||||
| LDA hazel_txcb_data,x ; Load data byte from fs_cmd_data buffer | ||||||||||
| A2F8 | STA (fs_crc_lo),y ; Store to destination via fs_crc pointer | |||||||||
| A2FA | INX ; Advance source index | |||||||||
| A2FB | INY ; Advance destination index | |||||||||
| A2FC | DEC fs_load_addr_2 ; Decrement byte counter | |||||||||
| A2FE | BNE loop_copy_to_host ; Loop until all bytes transferred | |||||||||
| A300 | BEQ tail_update_catalogue ; X=&10: scan 16 slots (15 to 0) | |||||||||
| A302 | .tube_write_setup←1← A2EF BNE | |||||||||
| JSR tube_claim_c3 ; Clear V | ||||||||||
| A305 | LDA #1 ; A=1: tube transfer type (write) | |||||||||
| A307 | LDX fs_options ; Load destination low from fs_options | |||||||||
| A309 | LDY fs_block_offset ; No match: try next | |||||||||
| A30B | INX ; Load slot status byte | |||||||||
| A30C | BNE set_tube_addr ; No wrap: skip high increment | |||||||||
| A30E | INY ; Test bit 2 (PS active flag)? | |||||||||
| A30F | .set_tube_addr←1← A30C BNE | |||||||||
| JSR tube_addr_data_dispatch ; Set up tube transfer address | ||||||||||
| A312 | LDX fs_load_addr ; Transfer Y to A | |||||||||
| A314 | .loop_write_to_tube←1← A322 BNE | |||||||||
| LDA hazel_txcb_data,x ; Load data byte from buffer | ||||||||||
| A317 | STA tube_data_register_3 ; Write to tube data register 3 Store Y to fs_urd_handle | |||||||||
| A31A | INX ; Advance source index | |||||||||
| A31B | LDY #6 ; Y=6: tube write delay | |||||||||
| A31D | .loop_tube_delay←1← A31E BNE | |||||||||
| DEY ; Delay loop: decrement Y | ||||||||||
| A31E | BNE loop_tube_delay ; Transfer Y to A | |||||||||
| A320 | DEC fs_load_addr_2 ; Decrement byte counter | |||||||||
| A322 | BNE loop_write_to_tube ; Store allocation result | |||||||||
| A324 | LDA #&83 ; A=&83: release tube claim | |||||||||
| A326 | JSR tube_addr_data_dispatch ; Release tube | |||||||||
| fall through ↓ | ||||||||||
Catalogue-update exit (JMP clear_result)Single-instruction tail: JMP clear_result -- shared exit for the catalogue-update paths after they have finished writing the new entry. Two callers: &A300 (the success path) and &A38D (the no-change path). Never returns directly (clear_result loads A=0 and tail-falls into finalise_and_return). |
|
| A329 | .tail_update_catalogue←2← A300 BEQ← A38D JMP |
| JMP clear_result ; Jump to clear A and finalise return | |
| A32C | .update_cat_position←1← A2BF BCS |
| LDY #9 ; Y=9: offset for position byte | |
| A32E | LDA (fs_options),y ; Try next slot |
| A330 | STA hazel_txcb_flag ; Store in fs_func_code |
| A333 | LDY #5 ; Y=5: offset for extent byte |
| A335 | LDA (fs_options),y ; Load extent byte from FS options |
| A337 | STA hazel_txcb_count ; Store in fs_data_count |
| A33A | LDX #&0d ; X=&0D: byte count |
| A33C | STX hazel_txcb_result ; Store in fs_reply_cmd |
| A33F | LDY #2 ; Y=2: command sub-type |
| A341 | STY fs_load_addr ; Set V (found match) |
| A343 | STY hazel_txcb_data ; Store in fs_cmd_data Store Y to fs_csd_handle |
| A346 | INY ; Y=3: TX buffer command byte |
| A347 | JSR save_net_tx_cb ; V set: found, skip allocation |
| A34A | STX fs_load_addr_hi ; Allocate FCB slot |
| A34C | LDA hazel_txcb_flag ; Load data offset from fs_func_code |
| A34F | STA (fs_options,x) ; Store as first byte of FS options |
| A351 | LDA hazel_txcb_data ; Load data count from fs_cmd_data |
| A354 | LDY #9 ; Y=9: position offset in FS options |
| A356 | ADC (fs_options),y ; Add to current position |
| A358 | STA (fs_options),y ; Store updated position |
| A35A | LDA txcb_end ; Load TXCB end byte |
| A35C | SBC #7 ; Subtract 7 (header overhead) |
| A35E | STA hazel_txcb_flag ; Store remaining data size |
| A361 | STA fs_load_addr_2 ; Store in fs_load_addr_2 (byte count) |
| A363 | BEQ clear_buf_after_write ; Zero bytes: skip write |
| A365 | JSR write_data_block ; Write data block to host/tube |
| A368 | .clear_buf_after_write←1← A363 BEQ |
| LDX #2 ; X=2: clear 3 bytes (indices 0-2) | |
| A36A | .loop_clear_buf←1← A36E BPL |
| STA hazel_txcb_count,x ; Clear fs_data_count+X | |
| A36D | DEX ; Decrement index |
| A36E | BPL loop_clear_buf ; Loop until all cleared |
| A370 | JSR update_addr_from_offset1 ; Update addresses from offset 1 |
| A373 | SEC ; Set carry for subtraction |
| A374 | DEC fs_load_addr_2 ; Decrement fs_load_addr_2 |
| A376 | LDA hazel_txcb_data ; Load data count from fs_cmd_data |
| A379 | STA hazel_txcb_flag ; Copy to fs_func_code |
| A37C | JSR adjust_fsopts_4bytes ; Adjust FS options by 4 bytes (subtract) |
| A37F | LDX #3 ; X=3: check 4 bytes |
| A381 | LDY #5 ; Y=5: starting offset |
| A383 | SEC ; Set carry for comparison |
| A384 | .loop_check_remaining←1← A38A BPL |
| LDA (fs_options),y ; Load FS options byte | |
| A386 | BNE done_write_block ; Non-zero: more data remaining |
| A388 | INY ; Advance to next byte |
| A389 | DEX ; Decrement counter |
| A38A | BPL loop_check_remaining ; Loop until all bytes checked |
| A38C | CLC ; All zero: clear carry (transfer complete) |
| A38D | .done_write_block←1← A386 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.
|
||||||
| A390 | .tube_claim_c3←3← A302 JSR← A395 BCC← A627 JSR | |||||
| LDA #&c3 ; A=&C3: tube claim protocol | ||||||
| A392 | JSR tube_addr_data_dispatch ; Dispatch tube address/data claim | |||||
| A395 | BCC tube_claim_c3 ; Carry clear: claim failed, retry | |||||
| A397 | 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.
|
||||
| A398 | .cmd_fs | |||
| LDA hazel_fs_station ; Read current FS station from workspace | ||||
| A39B | STA fs_work_5 ; Save in fs_work_5 (so 'no-arg' path can print it) | |||
| A39D | LDA hazel_fs_network ; Read current FS network | |||
| A3A0 | STA fs_work_6 ; Save in fs_work_6 | |||
| A3A2 | LDA (fs_crc_lo),y ; Look at the first command-line byte | |||
| A3A4 | CMP #&0d ; Is it CR (no argument)? | |||
| A3A6 | BEQ print_current_fs ; Yes: print the current FS address | |||
| A3A8 | JSR parse_fs_ps_args ; Parse 'net.station' arg into fs_work_5/6 | |||
| A3AB | LDA #1 ; A=1: OSWORD &13 sub-function 1 = set file server station | |||
| A3AD | STA fs_work_4 ; Store sub-function in PB[0] | |||
| A3AF | LDA #&13 ; A=&13: OSWORD &13 | |||
| A3B1 | LDX #<(fs_work_4) ; X = lo of PB pointer (fs_work_4 = &B4) | |||
| A3B3 | LDY #>(fs_work_4) ; Y = hi of PB pointer (=0, since fs_work_4 is in zero page) | |||
| A3B5 | JMP osword ; Tail-jump into OSWORD; the OS routes us back through osword_13_set_station | |||
| A3B8 | .print_current_fs←1← A3A6 BEQ | |||
| JSR print_file_server_is ; Print 'File server is ' fragment | ||||
| 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.
|
||||
| A3BB | .print_fs_info_newline←1← B474 JSR | |||
| BIT always_set_v_byte ; Set V so print_station_addr suppresses the leading '0.' when the network number is zero | ||||
| A3BE | JSR print_station_addr ; Print the station/network address | |||
| A3C1 | JMP osnewl ; Tail-call OSNEWL for the trailing CR/LF | |||
Parse station address from *FS/*PS argumentsReads a station address in
|
|||||||
| A3C4 | .parse_fs_ps_args←4← 9604 JSR← A3A8 JSR← B3CF JSR← B5A6 JSR | ||||||
| PHX ; Save caller's X (command-line offset cursor) | |||||||
| A3C5 | LDA #0 ; A=0: clear the dot-seen flag for parse_addr_arg | ||||||
| A3C7 | STA fs_work_4 ; Store cleared dot-seen flag | ||||||
| A3C9 | JSR parse_addr_arg ; Parse first number (network or standalone station) | ||||||
| A3CC | BCS skip_if_no_station ; C set: parse_addr_arg saw an empty argument -- skip station storage | ||||||
| A3CE | STA fs_work_7 ; Save the network number in fs_work_7 | ||||||
| A3D0 | PHY ; Save Y (current command-line cursor) for after the bridge poll | ||||||
| A3D1 | JSR init_bridge_poll ; Populate the bridge routing table -- returns local network number in A | ||||||
| A3D4 | EOR fs_load_addr_2 ; EOR with parsed network: Z set iff parse matched local | ||||||
| A3D6 | BEQ store_station_lo ; Match: keep A=0 to mark local network | ||||||
| A3D8 | LDA fs_load_addr_2 ; Mismatch: A = parsed network number | ||||||
| A3DA | .store_station_lo←1← A3D6 BEQ | ||||||
| STA fs_work_6 ; Store network number into fs_work_6 (the canonical form: 0=local, non-zero=remote) | |||||||
| A3DC | PLY ; Restore Y | ||||||
| A3DD | INY ; Step Y past the dot separator | ||||||
| A3DE | JSR parse_addr_arg ; Parse station number after the dot | ||||||
| A3E1 | .skip_if_no_station←1← A3CC BCS | ||||||
| BEQ no_station_loop ; C set: no station after dot -- leave fs_work_5 alone | |||||||
| A3E3 | STA fs_work_5 ; Store parsed station in fs_work_5 | ||||||
| A3E5 | .no_station_loop←1← A3E1 BEQ | ||||||
| PLX ; Restore caller's X | |||||||
| A3E6 | 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.
|
||||||
| A3E7 | .get_pb_ptr_as_index←2← A405 JSR← A415 JSR | |||||
| LDA osword_pb_ptr ; Read PB[0] (the OSWORD sub-function code in most calls); fall into byte_to_2bit_index | ||||||
| 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.
|
|||||||
| A3E9 | .byte_to_2bit_index←4← 8FAB JSR← A93D JSR← A956 JSR← B4C6 JSR | ||||||
| ASL ; Multiply A by 2 | |||||||
| A3EA | ASL ; Multiply A by 2 again -- A is now A_orig * 4 | ||||||
| A3EB | PHA ; Stash A_orig * 4 on the stack | ||||||
| A3EC | ASL ; Multiply A by 2 -- A is now A_orig * 8 (C = bit 7 of A_orig*4) | ||||||
| A3ED | TSX ; Capture S so we can read the just-pushed value | ||||||
| A3EE | PHP ; Save the C flag from the third ASL | ||||||
| A3EF | ADC error_text,x ; ADC stack[X+1] = A_origem>4 (with C from the ASL): A = A_orig/em>8 + A_origem>4 + C = A_orig/em>12 + C | ||||||
| A3F2 | ROR ; Halve the result, putting the new C as bit 7 | ||||||
| A3F3 | PLP ; Restore the saved C (from the third ASL) | ||||||
| A3F4 | ASL ; ASL doubles the halved value (effectively undoes the ROR's divide while reusing C) | ||||||
| A3F5 | TAY ; Y = A_orig * 12 (the 12-byte-aligned index) | ||||||
| A3F6 | PLA ; Recover A_orig * 4 (left on the stack at &A3EB) | ||||||
| A3F7 | CMP #&48 ; Above &48 (i.e. A_orig * 4 >= 72, A_orig >= 18)? | ||||||
| A3F9 | BCC return_from_2bit_index ; No: keep computed Y | ||||||
| A3FB | LDY #0 ; Yes: clamp Y to 0 (out of range) | ||||||
| A3FD | TYA ; Mirror Y -> A so callers can test Z | ||||||
| A3FE | .return_from_2bit_index←1← A3F9 BCC | ||||||
| RTS ; Return; Y holds 12-byte-aligned offset, A is non-zero on success | |||||||
FS reply: read handle byte (no workspace lookup)Reads the inline handle byte directly from the RX buffer at
|
||||
| A3FF | .net_1_read_handle | |||
| LDY #&6f ; Y=&6F: net_rx_ptr offset for the 'inline' handle byte | ||||
| A401 | LDA (net_rx_ptr),y ; Read handle byte directly from RX buffer | |||
| A403 | BCC store_pb_result ; C clear: read-handle path -- store directly to PB | |||
| fall through ↓ | ||||
FS reply: read handle byte from workspace tableCalls |
|
| A405 | .net_2_read_entry |
| JSR get_pb_ptr_as_index ; Convert PB pointer to workspace table offset | |
| A408 | BCS return_zero_uninit ; Out of range: return zero (uninitialised) |
| A40A | LDA (nfs_workspace),y ; Read workspace handle byte |
| A40C | CMP #&3f ; Slot marked '?' (uninitialised)? |
| A40E | BNE store_pb_result ; Has a real handle: keep it and store |
| A410 | .return_zero_uninit←1← A408 BCS |
| LDA #0 ; Force result to zero (uninitialised marker) | |
| A412 | .store_pb_result←2← A403 BCC← A40E BNE |
| STA osword_pb_ptr ; Write into PB[0] (handle return slot) | |
| A414 | RTS ; Return |
FS reply: close handle entryCalls |
|
| A415 | .net_3_close_handle |
| JSR get_pb_ptr_as_index ; Convert PB pointer to workspace table offset | |
| A418 | BCC mark_ws_uninit ; Out of range: mark as uninitialised |
| A41A | ROR fs_flags ; Shift bit 0 of fs_flags into C (save state) |
| A41D | LDA osword_pb_ptr ; Read PB[0] (the handle to close) |
| A41F | ROL ; Shift bit 7 of A into C |
| A420 | ROL fs_flags ; Restore C into bit 0 of fs_flags |
| A423 | RTS ; Return; the close action is dispatched elsewhere based on the saved C state |
| A424 | .mark_ws_uninit←1← A418 BCC |
| ROR econet_flags ; Save bit 0 of econet_flags | |
| A427 | LDA #&3f ; A='?': uninitialised marker |
| A429 | STA (nfs_workspace),y ; Write '?' to workspace[Y] (the slot is now free) |
| A42B | ROL econet_flags ; Restore bit 0 of econet_flags |
| A42E | RTS ; Return |
FSCV reason 3: process *
|
|
| A42F | .fscv_3_star_cmd←1← 8CFA JMP |
| JSR set_text_and_xfer_ptr ; Set text/transfer pointers from FS context | |
| A432 | LDY #&ff ; Y=&FF -- mark spool/Tube state inactive |
| A434 | STY fs_spool_handle ; Store fs_spool_handle = &FF |
| A436 | STY need_release_tube ; Store need_release_tube = &FF |
| A438 | INY ; Y=&00 |
| A439 | LDX #&35 ; X=&35: NFS-commands sub-table offset |
| A43B | JSR match_fs_cmd ; Match against the NFS sub-table |
| A43E | BCS dispatch_fs_cmd ; C set: no match -> dispatch via fall-through |
| fall through ↓ | |
FS-command re-entry guard (BVC dispatch_fs_cmd)Single-instruction prologue: BVC dispatch_fs_cmd. Reached as the fall-through target after a *RUN failure -- if V is clear (the re-entry path is permitted) it branches into dispatch_fs_cmd to re-attempt the command; otherwise falls through to error_syntax to raise 'Syntax'. Single caller (the FS dispatch table at &8C4E). |
|
| A440 | .cmd_fs_reentry←1← 8C4E JMP |
| BVC dispatch_fs_cmd ; V clear: re-enter dispatch_fs_cmd | |
| A442 | .error_syntax |
| LDA #&dc ; Error code &DC | |
| A444 | JSR error_inline ; Raise 'Syntax' error |
| A447 | EQUS "Syntax." |
| A44E | .dispatch_fs_cmd←2← A43E BCS← A440 BVC |
| LDA #0 ; A=0: clear svc_state | |
| A450 | STA svc_state ; Store -> svc_state |
| A452 | LDA cmd_dispatch_hi_table,x ; Load dispatch hi byte from cmd_dispatch_hi_table+X |
| A455 | PHA ; Push hi for RTS dispatch |
| A456 | LDA cmd_dispatch_lo_table,x ; Load dispatch lo byte from cmd_dispatch_lo_table+X |
| A459 | PHA ; Push lo for RTS dispatch |
| A45A | RTS ; RTS -> dispatched command handler |
Match command name against FS command tableCase-insensitive compare of the command line against
|
|||||||||||
| A45B | .match_fs_cmd←3← 8C49 JSR← 8C77 JSR← A43B JSR | ||||||||||
| TYA ; Save command-line offset Y on stack | |||||||||||
| A45C | PHA ; Push for save/restore | ||||||||||
| A45D | .restart_table_scan←1← A483 BNE | ||||||||||
| PLA ; Reload saved Y (peek without popping) | |||||||||||
| A45E | PHA ; Push it back to keep on stack | ||||||||||
| A45F | TAY ; Y = saved command-line offset | ||||||||||
| A460 | LDA cmd_table_fs,x ; First char of current entry name | ||||||||||
| A463 | BMI check_char_type ; Bit 7 set already: end of table | ||||||||||
| A465 | .loop_match_char←1← A472 BNE | ||||||||||
| LDA cmd_table_fs,x ; Next char from table | |||||||||||
| A468 | BMI check_separator ; Bit 7 set: name fully matched | ||||||||||
| A46A | EOR (fs_crc_lo),y | ||||||||||
| A46C | AND #&df ; Mask off case bit (5) | ||||||||||
| A46E | BNE skip_entry_chars ; Mismatch (after case mask): skip entry | ||||||||||
| A470 | INY ; Advance command-line offset | ||||||||||
| A471 | INX ; Advance table offset | ||||||||||
| A472 | BNE loop_match_char ; ALWAYS branch: continue matching | ||||||||||
| A474 | .skip_entry_chars←2← A46E BNE← A478 BPL | ||||||||||
| INX ; Skip remaining name chars | |||||||||||
| A475 | LDA cmd_table_fs,x ; Load next table byte | ||||||||||
| A478 | BPL skip_entry_chars ; Bit 7 clear: continue skipping | ||||||||||
| A47A | LDA (fs_crc_lo),y ; Char on command line at current Y | ||||||||||
| A47C | CMP #&2e ; Is it . (abbreviation)? |
||||||||||
| A47E | BEQ skip_dot_and_spaces ; Yes: accept abbreviated match | ||||||||||
| A480 | .loop_skip_to_next←1← A495 BNE | ||||||||||
| INX ; Skip 3-byte handler trailer (flag, lo, hi) | |||||||||||
| A481 | INX ; (continued) | ||||||||||
| A482 | INX ; (continued) | ||||||||||
| A483 | BNE restart_table_scan ; ALWAYS branch: try next entry | ||||||||||
| A485 | .check_separator←1← A468 BMI | ||||||||||
| TYA ; Save matched-name length on stack | |||||||||||
| A486 | PHA ; Push for stack-based comparison | ||||||||||
| A487 | LDA (fs_crc_lo),y ; Char on command line just past name | ||||||||||
| A489 | LDY #9 ; Y=9: separator-table size - 1 | ||||||||||
| A48B | .loop_check_sep_table←1← A491 BPL | ||||||||||
| CMP sep_table_data,y ; Compare with separator | |||||||||||
| A48E | BEQ separator_char_table ; Match: valid command boundary | ||||||||||
| A490 | DEY ; Try next separator | ||||||||||
| A491 | BPL loop_check_sep_table ; Loop through 10 separators | ||||||||||
| A493 | PLA ; Restore matched-name length | ||||||||||
| A494 | TAY ; A = matched offset, save in Y | ||||||||||
| A495 | BNE loop_skip_to_next ; ALWAYS branch: try next entry | ||||||||||
| A497 | .sep_table_data←1← A48B CMP | ||||||||||
| JSR separator_parse_dispatch ; Dispatch helper (sep_table_data path) | |||||||||||
| A49A | BIT parse_separator_flag ; Check separator flag (zp_0026) | ||||||||||
| A49C | ROL | ||||||||||
| A49D | DEC | ||||||||||
| A49E | RTI ; Effective unconditional jump | ||||||||||
| A49F | EQUB &0D ; CR (carriage return) | ||||||||||
| A4A0 | .separator_char_table←1← A48E BEQ | ||||||||||
| PLA ; Restore matched-name length | |||||||||||
| A4A1 | TAY ; Y = matched-name length | ||||||||||
| fall through ↓ | |||||||||||
Skip trailing spaces from FS command-line argsReads (fs_crc_lo),Y; on space, falls through to the per-char advance; non-space exits to check_cmd_flags. Shared body with skip_dot_and_spaces at &A4A8 (alt-entry that also accepts dots). Single caller (the BNE retry at &A4A9).
|
||||
| A4A2 | .loop_skip_trail_spaces←1← A4A9 JMP | |||
| LDA (fs_crc_lo),y ; Char on command line at current Y | ||||
| A4A4 | CMP #&20 ; Is it space? | |||
| A4A6 | BNE check_cmd_flags ; No: check the entry's no-arg flag | |||
| A4A8 | .skip_dot_and_spaces←1← A47E BEQ | |||
INY ; Advance past the space (or .) |
||||
| A4A9 | JMP loop_skip_trail_spaces ; Loop: keep skipping | |||
| A4AC | .check_cmd_flags←1← A4A6 BNE | |||
| LDA cmd_table_fs,x ; Load entry's flag byte (post-name) | ||||
| A4AF | ASL ; Shift bit 7 into C: the no-arg bit | |||
| A4B0 | BPL clear_v_flag ; C=0: entry allows arguments | |||
| A4B2 | LDA (fs_crc_lo),y ; Char on command line | |||
| A4B4 | CMP #&0d ; Is it CR (no argument)? | |||
| A4B6 | BNE clear_v_flag ; Argument present, V clear | |||
| A4B8 | BIT always_set_v_byte ; Force V=1: entry validated as match | |||
| A4BB | BVS clear_c_flag ; V set: skip the CLV | |||
| A4BD | .clear_v_flag←2← A4B0 BPL← A4B6 BNE | |||
| CLV ; Clear V (no-arg flag not asserted) | ||||
| A4BE | .clear_c_flag←1← A4BB BVS | |||
| CLC ; Clear C (no error / no-arg path) | ||||
| A4BF | .return_with_result←1← A4DA BCS | |||
| PLA ; Discard saved Y on stack | ||||
| A4C0 | LDA (fs_crc_lo),y ; A = current command-line char | |||
| A4C2 | RTS ; Return (Z=1 on match, C and V set per result) | |||
| A4C3 | .loop_scan_past_word←1← A4D0 BNE | |||
| INY ; Advance command-line offset | ||||
| A4C4 | .check_char_type←1← A463 BMI | |||
| LDA (fs_crc_lo),y ; Char on command line | ||||
| A4C6 | CMP #&0d ; Is it CR (end of input)? | |||
| A4C8 | BEQ set_c_and_return ; Yes: set C and return (no match) | |||
| A4CA | CMP #&2e ; Is it .? |
|||
| A4CC | BEQ skip_sep_spaces ; Yes: skip separator spaces | |||
| A4CE | CMP #&20 ; Is it space? | |||
| A4D0 | BNE loop_scan_past_word ; No: keep scanning past word | |||
| A4D2 | .skip_sep_spaces←2← A4CC BEQ← A4D7 BEQ | |||
| INY ; Advance past space | ||||
| A4D3 | LDA (fs_crc_lo),y ; Load next char | |||
| A4D5 | CMP #&20 ; Still space? | |||
| A4D7 | BEQ skip_sep_spaces ; Yes: keep skipping | |||
| A4D9 | .set_c_and_return←1← A4C8 BEQ | |||
| SEC ; Set C: signal no-match return path | ||||
| A4DA | BCS return_with_result ; ALWAYS branch to common return | |||
| A4DC | .check_urd_present | |||
| BIT fs_flags ; Test fs_flags bit 6 | ||||
| A4DF | BVS fscv_2_star_run ; Bit 6 set: take fscv_2_star_run | |||
| A4E1 | JMP error_bad_command ; Bit 6 clear: raise 'Bad command' | |||
FSCV reason 2: handle *RUNSaves the OS text pointer via
|
|
| A4E4 | .fscv_2_star_run←1← A4DF BVS |
| JSR save_ptr_to_os_text ; Save text pointer (for GSREAD-driven parsing) | |
| A4E7 | JSR mask_owner_access ; Reset fs_lib_flags low bits to 5-bit access mask |
| A4EA | ORA #2 ; Set bit 1 of A (mark *RUN-style invocation) |
| A4EC | STA hazel_fs_lib_flags ; Update hazel_fs_lib_flags with the result |
| A4EF | BNE cmd_run_load_mask ; ALWAYS branch |
*RUN entry for URD-prefixed argumentReached from cmd_fs_operation at &8E35 when the first character of the *RUN argument is '&' (the URD = User Root Directory prefix). Saves the OS text pointer via save_ptr_to_os_text, masks the access bits via mask_owner_access, clears bit 1 of the result, and stores into hazel_fs_lib_flags. Falls through to cmd_run_load_mask which calls parse_cmd_arg_y0 to begin parsing the rest of the *RUN argument. Single caller; never returns directly (continues into the run flow). |
|
| A4F1 | .cmd_run_via_urd←1← 8E35 JMP |
| JSR save_ptr_to_os_text ; Save current OS text pointer | |
| A4F4 | JSR mask_owner_access ; Mask access bits |
| A4F7 | AND #&fd ; Clear bit 1 of mask |
| A4F9 | STA hazel_fs_lib_flags ; Save into fs_lib_flags |
| A4FC | .cmd_run_load_mask←1← A4EF BNE |
| JSR parse_cmd_arg_y0 ; Begin parsing the *RUN argument | |
| A4FF | .open_file_for_run←1← A576 BNE |
| LDX #1 ; X=1: TX-buffer write index for argument | |
| A501 | JSR copy_arg_to_buf ; Copy argument to TX buffer |
| A504 | LDA #2 ; A=2: open-input mode for OSFIND |
| A506 | STA hazel_txcb_data ; Next byte down |
| A509 | LDY #&12 ; Y=&12: cmd code for *RUN |
| A50B | JSR save_net_tx_cb ; Send the request and wait for reply |
| A50E | LDA hazel_txcb_data ; Read reply status from TX[5] |
| A511 | CMP #1 ; Compare with 1 (not-found) |
| A513 | BNE try_library_path ; Loop until all 6 restored |
| A515 | LDX #3 ; Return from svc_8_osword |
| A517 | .loop_check_handles←1← A520 BPL |
| INC hazel_txcb_size_hi,x ; Increment handle byte | |
| A51A | BEQ alloc_run_fcb ; Load handler address low byte |
| A51C | JMP check_exec_addr ; Non-zero: handle valid, execute |
| A51F | .alloc_run_fcb←1← A51A BEQ |
| DEX ; Decrement X (post-find adjustment) | |
| A520 | BPL loop_check_handles ; Loop while X >= 0 (scan all 4 handle slots) |
| A522 | JSR alloc_fcb_or_error ; RTS dispatches to pushed handler |
| A525 | LDX #1 ; X=1: target offset for the *RUN-channel command |
| A527 | STX hazel_txcb_data ; Store X to hazel_txcb_data (cmd byte) |
| A52A | STX hazel_txcb_flag ; Store X to hazel_txcb_flag (cmd flag) |
| A52D | INX ; X=2 |
| A52E | JSR copy_arg_to_buf ; Copy filename arg into TX buffer |
| A531 | LDY #6 ; Test station active flag |
| A533 | JSR save_net_tx_cb ; Send re-open request |
| A536 | BCS done_run_dispatch ; C set: error from save_net_tx_cb -- abort *RUN |
| A538 | JMP alloc_run_channel ; Yes: handle clock read |
| A53B | .done_run_dispatch←1← A536 BCS |
| JMP finalise_and_return ; Jump to finalise and return | |
| A53E | .try_library_path←1← A513 BNE |
| LDA hazel_parse_buf ; Return | |
| A541 | CMP #&24 ; Y=&10: length of TXCB to save |
| A543 | BEQ error_bad_command ; Save current TX control block |
| A545 | LDA hazel_fs_lib_flags ; Load library flag byte |
| A548 | BMI library_tried ; Bit 7 set: library already tried |
| A54A | ROL ; Shift bit 7 into carry |
| A54B | ROL |
| A54C | BMI restore_filename ; Store BCD seconds |
| A54E | BCS error_bad_command ; Carry set: bad command |
| A550 | LDX #&ff ; X=&FF -- start scan from end |
| A552 | .loop_find_name_end←1← A558 BNE |
| INX ; Convert binary to BCD | |
| A553 | LDA hazel_parse_buf,x ; Load filename byte |
| A556 | CMP #&0d ; Compare with CR (terminator) |
| A558 | BNE loop_find_name_end ; Load hours from clock workspace |
| A55A | .loop_shift_name_right←1← A561 BPL |
| LDA hazel_parse_buf,x ; Shift filename right by 8 bytes | |
| A55D | STA hazel_rtc_buffer,x ; Store shifted byte |
| A560 | DEX ; Decrement scan index |
| A561 | BPL loop_shift_name_right ; Clear hours high position |
| A563 | LDX #7 ; Store zero |
| A565 | .loop_copy_lib_prefix←1← A56C BPL |
| LDA library_dir_prefix,x ; Copy 'Library.' prefix | |
| A568 | STA hazel_parse_buf,x ; Store prefix byte |
| A56B | DEX ; Decrement scan index |
| A56C | BPL loop_copy_lib_prefix ; Loop until prefix copied |
| A56E | LDA hazel_fs_lib_flags ; Load library flag |
| A571 | ORA #&60 ; Mark byte as 'argument' |
| A573 | STA hazel_fs_lib_flags ; Restore day+month byte |
| A576 | .retry_with_library←1← A58D BNE |
| BNE open_file_for_run ; Retry file open with library path | |
| A578 | .restore_filename←1← A54C BMI |
| LDX #&ff ; X=&FF -- restart scan from end | |
| A57A | .loop_restore_name←1← A583 BNE |
| INX ; Store BCD month | |
| A57B | LDA hazel_rtc_buffer,x ; Load backup byte |
| A57E | STA hazel_parse_buf,x ; Shift high nibble down |
| A581 | EOR #&0d ; 4th shift: isolate high nibble |
| A583 | BNE loop_restore_name ; No: continue restoring |
| A585 | JSR mask_owner_access ; Set owner-only access mask |
| A588 | ORA #&80 ; Mark caller's flags |
| A58A | STA hazel_fs_lib_flags ; Copy 7 bytes (Y=6 down to 0) |
| A58D | BNE retry_with_library ; ALWAYS branch |
| A58F | .library_tried←1← A548 BMI |
| JSR mask_owner_access ; Store to parameter block | |
| A592 | LDA #2 ; Loop for all 7 bytes |
| A594 | BIT hazel_fs_lib_flags ; Test hazel_fs_lib_flags bits 6 / 7 |
| A597 | BNE error_bad_command ; Either bit set: this is an invalid command path |
| A599 | JSR finalise_and_return ; Otherwise finalise and return |
| A59C | LDA #&0b ; A=&0B: FSCV reason 11 (filing-system change) |
| A59E | JMP call_fscv ; Tail-call FSCV |
Raise 'Bad command' BRK errorLoads error code &FE and tail-calls error_bad_inline with the inline string 'command' -- error_bad_inline prepends 'Bad ' to produce the final 'Bad command' message. Used by the FS command parser when no table entry matches the user's input. Never returns. |
|
| A5A1 | .error_bad_command←4← A4E1 JMP← A543 BEQ← A54E BCS← A597 BNE |
| LDA #&fe ; Error code &FE | |
| A5A3 | JSR error_bad_inline ; Raise 'Bad command' error |
| A5A6 | EQUS "command." |
| fall through ↓ | |
Validate exec address is non-zeroIterates X = 3..0 over the 4-byte exec-address copy at hazel_txcb_flag..hazel_exec_addr, incrementing each byte. If any byte becomes non-zero (BNE), branches forward to library_path_string (the OSCLI dispatch path). When all four INC operations leave a zero result the address was &FFFFFFFF + 1 = 0 -- not a valid exec address -- and the routine falls through to the no-exec-address handler. Single caller (&A51C in the *RUN handler).
|
|||||||
| A5AE | .check_exec_addr←1← A51C JMP | ||||||
| LDX #3 ; X=3: check 4 execution bytes | |||||||
| A5B0 | .loop_check_exec_bytes←1← A5B6 BNE | ||||||
| INC hazel_txcb_flag,x ; Increment execution address byte | |||||||
| A5B3 | BNE library_path_string ; Low byte = &6F | ||||||
| A5B5 | DEX ; Set osword_flag | ||||||
| A5B6 | BNE loop_check_exec_bytes ; Loop until all checked | ||||||
| A5B8 | LDA #&93 ; A=&93: error code 'Bad command' | ||||||
| A5BA | JSR error_inline_log ; Generate 'No!' error | ||||||
| A5BD | EQUS "Won't." | ||||||
| fall through ↓ | |||||||
Allocate FCB slot for *RUN target fileLoads the saved OSWORD parameter byte at hazel_txcb_data, calls alloc_fcb_slot to obtain a free channel index in A, transfers it into Y, then clears the per-channel attribute byte at hazel_fcb_status,X. Used by the *RUN argument-handling path at &A538 once the file is opened, to reserve a channel for the running program.
|
||||||||
| A5C3 | .alloc_run_channel←1← A538 JMP | |||||||
| LDA hazel_txcb_data ; Set workspace pointer high | ||||||||
| A5C6 | JSR alloc_fcb_slot ; Allocate FCB slot | |||||||
| A5C9 | TAY ; A = parsed character | |||||||
| A5CA | LDA #0 ; Y=OSWORD flag (slot specifier) | |||||||
| A5CC | STA hazel_fcb_status,x ; Clear status in channel table | |||||||
| A5CF | STY hazel_cur_dir_handle ; A=3: start searching from slot 3 | |||||||
| A5D2 | LDY #3 ; Y=3: skip past 3-byte FS header | |||||||
| A5D4 | JMP boot_cmd_oscli ; C set: slot invalid, store result | |||||||
| A5D7 | .library_dir_prefix←1← A565 LDA | |||||||
| EQUS "Library." ; Continue shift | ||||||||
| A5DF | .library_path_string←1← A5B3 BNE | |||||||
| JSR copy_arg_to_buf_x0 ; Copy parsed arg to TX buffer with X=0 | ||||||||
| A5E2 | LDY #0 ; Y=0 | |||||||
| A5E4 | CLC ; For the loop entry | |||||||
| A5E5 | JSR gsinit ; Transfer found slot to A | |||||||
| A5E8 | .loop_read_gs_string←1← A5EB BCC | |||||||
| JSR gsread ; Store slot number to PB byte 0 | ||||||||
| A5EB | BCC loop_read_gs_string ; Always (BCC after CLC) loop back | |||||||
| A5ED | DEY ; C set: slot invalid, store result | |||||||
| A5EE | .loop_skip_trailing←1← A5F3 BEQ | |||||||
| INY ; Advance Y past trailing space | ||||||||
| A5EF | LDA (os_text_ptr),y ; Y=Y-1: adjust workspace offset | |||||||
| A5F1 | CMP #&20 ; Is it space? | |||||||
| A5F3 | BEQ loop_skip_trailing ; Yes: skip it | |||||||
| A5F5 | EOR #&0d ; Test for CR (terminator) | |||||||
| A5F7 | CLC ; Clear C for arithmetic | |||||||
| A5F8 | TYA ; Compare Y with OSWORD flag | |||||||
| A5F9 | ADC os_text_ptr ; Add to text pointer low | |||||||
| A5FB | STA fs_crc_lo ; Store low byte of (os_text_ptr + Y) -> fs_crc_lo (repurposed as a generic pointer) | |||||||
| A5FD | LDA os_text_ptr_hi ; Load os_text_ptr_hi for the high-byte add | |||||||
| A5FF | ADC #0 ; Add carry from low add (no extra increment) | |||||||
| A601 | STA fs_crc_hi ; Store result high byte -> fs_crc_hi | |||||||
| A603 | JSR save_text_ptr ; Save text pointer for later | |||||||
| A606 | LDX #&c0 ; X=&C0: pointer-to-options high byte | |||||||
| A608 | STX fs_block_offset ; Y=1: workspace flag offset | |||||||
| A60A | LDA #&0e ; Store pending marker to workspace | |||||||
| A60C | STA fs_options ; Store as fs_options | |||||||
| A60E | STA hazel_retry_counter ; Increment retry counter | |||||||
| A611 | LDX #&4a ; X=&4A: FS command table offset | |||||||
| A613 | LDY #5 ; Store result A to PB via Y | |||||||
| A615 | JSR do_fs_cmd_iteration ; Rotate Econet flags back (restore state) | |||||||
| A618 | LDA tube_present ; Return from OSWORD 11 handler | |||||||
| A61B | BEQ dispatch_via_vector ; Store to ws_ptr_lo | |||||||
| A61D | AND hazel_txcb_tx_status ; Y=&7F: last byte of RX buffer | |||||||
| A620 | AND hazel_txcb_osword_flag | |||||||
| A623 | CMP #&ff ; All &FF? | |||||||
| A625 | BEQ dispatch_via_vector ; X-1: adjust count | |||||||
| A627 | JSR tube_claim_c3 ; Claim tube for data transfer | |||||||
| A62A | LDX #9 ; X=9: parameter count | |||||||
| A62C | LDY #&c1 ; Y=&C1: high byte of TX buffer pointer | |||||||
| A62E | LDA #4 ; A=4: option byte for *RUN | |||||||
| A630 | JMP tube_addr_data_dispatch ; Relocated execute path | |||||||
| A633 | .dispatch_via_vector←2← A61B BEQ← A625 BEQ | |||||||
| LDA #1 ; A=1: dispatch flag | ||||||||
| A635 | JMP (hazel_exec_addr) ; Indirect jump via workspace vector | |||||||
FS reply handler: select CSD stationSingle-instruction wrapper: JSR find_station_bit3 to record the new current-selected-directory (CSD) station in the table, then JMP return_with_last_flag to clean up and return. Single caller (the FS reply dispatch at &9594).
|
||||
| A638 | .fsreply_3_set_csd←1← 9594 JMP | |||
| JSR find_station_bit3 ; Find station-bit-3 entry | ||||
| A63B | JMP return_with_last_flag | |||
FS reply handler: set library stationTwo-instruction wrapper: |
|
| A63E | .fsreply_5_set_lib |
| JSR flip_set_station_boot ; Record library station in station table | |
| A641 | JMP return_with_last_flag |
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.
|
||||||
| A644 | .find_station_bit2←1← A6E9 JSR | |||||
| LDX #&10 ; X=&10: scan 16 entries | ||||||
| A646 | CLV ; Clear V (no-match marker) | |||||
| A647 | .loop_search_stn_bit2←2← A64D BNE← A654 BEQ | |||||
| DEX ; Step to previous entry | ||||||
| A648 | BMI done_search_bit2 ; Below 0: scan complete | |||||
| A64A | JSR match_station_net ; Compare entry X's stn/net with caller's | |||||
| A64D | BNE loop_search_stn_bit2 ; No match: continue | |||||
| A64F | LDA hazel_fcb_status,x ; Match: read entry's flag byte at hazel_fcb_status+X | |||||
| A652 | AND #4 ; Mask bit 2 | |||||
| A654 | BEQ loop_search_stn_bit2 ; Bit 2 clear: keep scanning | |||||
| A656 | TYA ; Bit 2 set: A = matched entry index (Y) | |||||
| A657 | STA hazel_fcb_slot_attr,x ; Store Y at hazel_fcb_slot_attr+X (link entry to slot) | |||||
| A65A | BIT always_set_v_byte ; BIT always_set_v_byte: V <- 1 (match found) | |||||
| A65D | .done_search_bit2←1← A648 BMI | |||||
| STY hazel_fs_saved_station ; Save Y at hazel_fs_saved_station (matched entry index) | ||||||
| A660 | BVS set_flags_bit2 ; V set: skip new-slot alloc | |||||
| A662 | TYA ; A = caller's index | |||||
| A663 | JSR alloc_fcb_slot ; Allocate a fresh FCB slot | |||||
| A666 | STA hazel_fcb_slot_1 ; Save FCB slot index at hazel_fcb_slot_1 | |||||
| A669 | BEQ jmp_restore_fs_ctx ; Z set: alloc failed -> restore FS context | |||||
| A66B | .set_flags_bit2←1← A660 BVS | |||||
| LDA #&26 ; A=&26: workspace flag for bit 2 search | ||||||
| A66D | 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.
|
||||||
| A66F | .find_station_bit3←3← A638 JSR← A6A1 JSR← A6EF JSR | |||||
| LDX #&10 ; X=&10: scan 16 entries | ||||||
| A671 | CLV ; Clear V (no-match marker) | |||||
| A672 | .loop_search_stn_bit3←2← A678 BNE← A67F BEQ | |||||
| DEX ; Step to previous entry | ||||||
| A673 | BMI done_search_bit3 ; Below 0: scan complete | |||||
| A675 | JSR match_station_net ; Compare entry's stn/net with caller's | |||||
| A678 | BNE loop_search_stn_bit3 ; No match: continue | |||||
| A67A | LDA hazel_fcb_status,x ; Match: read entry's flag byte at hazel_fcb_status+X | |||||
| A67D | AND #8 ; Mask bit 3 | |||||
| A67F | BEQ loop_search_stn_bit3 ; Bit 3 clear: keep scanning | |||||
| A681 | TYA ; Bit 3 set: A = matched entry index (Y) | |||||
| A682 | STA hazel_fcb_slot_attr,x ; Store Y at hazel_fcb_slot_attr+X (link entry to slot) | |||||
| A685 | BIT always_set_v_byte ; BIT always_set_v_byte: V <- 1 (match found) | |||||
| A688 | .done_search_bit3←1← A673 BMI | |||||
| STY hazel_fs_context_copy ; Save Y at hazel_fs_context_copy (matched entry index) | ||||||
| A68B | BVS set_flags_bit3 ; V set: skip new-slot alloc | |||||
| A68D | TYA ; A = caller's index | |||||
| A68E | JSR alloc_fcb_slot ; Allocate a fresh FCB slot | |||||
| A691 | STA hazel_fcb_slot_2 ; Save FCB slot index at hazel_fcb_slot_2 | |||||
| A694 | BEQ jmp_restore_fs_ctx ; Z set: alloc failed -> restore FS context | |||||
| A696 | .set_flags_bit3←1← A68B BVS | |||||
| LDA #&2a ; A=&2A: workspace flag for bit 3 search | ||||||
| A698 | BNE store_stn_flags_restore ; ALWAYS branch | |||||
*Flip command handlerExchanges the CSD and CSL (library) handles. Saves the current
CSD handle from
|
||||
| A69A | .cmd_flip | |||
| LDA hazel_fs_context_copy ; Load current CSD handle | ||||
| A69D | PHA ; Save CSD handle | |||
| A69E | LDY hazel_fs_prefix_stn ; Load library handle into Y | |||
| A6A1 | JSR find_station_bit3 ; Install library as new CSD | |||
| A6A4 | PLA ; Restore original CSD handle | |||
| A6A5 | 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.
|
|||||||
| A6A6 | .flip_set_station_boot←2← A63E JSR← A6F5 JSR | ||||||
| LDX #&10 ; X=&10: max 16 station entries | |||||||
| A6A8 | CLV ; Clear V (no match found yet) | ||||||
| A6A9 | .loop_search_stn_boot←2← A6AF BNE← A6B6 BEQ | ||||||
| DEX ; Decrement station index | |||||||
| A6AA | BMI done_search_boot ; All searched: exit loop | ||||||
| A6AC | JSR match_station_net ; Check if station[X] matches | ||||||
| A6AF | BNE loop_search_stn_boot ; No match: try next station | ||||||
| A6B1 | LDA hazel_fcb_status,x ; Load station flags byte | ||||||
| A6B4 | AND #&10 ; Test bit 4 (active flag) | ||||||
| A6B6 | BEQ loop_search_stn_boot ; Not active: try next station | ||||||
| A6B8 | TYA ; Transfer boot type to A | ||||||
| A6B9 | STA hazel_fcb_slot_attr,x ; Store boot setting for station | ||||||
| A6BC | BIT always_set_v_byte ; Set V flag (station match found) | ||||||
| A6BF | .done_search_boot←1← A6AA BMI | ||||||
| STY hazel_fs_prefix_stn ; Store boot type | |||||||
| A6C2 | BVS set_flags_boot ; V set (matched): skip allocation | ||||||
| A6C4 | TYA ; Boot type to A | ||||||
| A6C5 | JSR alloc_fcb_slot ; Allocate FCB slot for new entry | ||||||
| A6C8 | STA hazel_fcb_slot_3 ; Store allocation result | ||||||
| A6CB | BEQ jmp_restore_fs_ctx ; Zero: allocation failed, exit | ||||||
| A6CD | .set_flags_boot←1← A6C2 BVS | ||||||
| LDA #&32 ; A=&32: station flags (active+boot) | |||||||
| A6CF | .store_stn_flags_restore←2← A66D BNE← A698 BNE | ||||||
| STA hazel_fcb_status,x ; Store station flags | |||||||
| A6D2 | .jmp_restore_fs_ctx←3← A669 BEQ← A694 BEQ← A6CB BEQ | ||||||
| JMP restore_fs_context ; Restore FS context and return | |||||||
FS reply 1: copy boot handles + flag boot pendingCloses all network channels via
|
|
| A6D5 | .fsreply_1_boot |
| JSR close_all_net_chans ; Close all network channels | |
| A6D8 | LDA #&40 ; A=&40: protection-level marker |
| A6DA | TSB fs_flags |
| A6DD | SEC ; Mark this path as 'success' for the caller |
| A6DE | LDA hazel_txcb_result ; Load TX result code from hazel_txcb_result |
| A6E1 | STA hazel_fs_flags ; Store as hazel_fs_flags |
| A6E4 | PHA ; Save state |
| fall through ↓ | |
FS reply 2: install handles and (optionally) bootRecords the file-server / printer-server / library handles from
the I-AM reply into the station table by calling
|
||||||
| A6E5 | .fsreply_2_copy_handles | |||||
| PHP ; Save processor status | ||||||
| A6E6 | LDY hazel_txcb_data ; Load station number from reply | |||||
| A6E9 | JSR find_station_bit2 ; Find station entry with bit 2 | |||||
| A6EC | LDY hazel_txcb_flag ; Load network number from reply | |||||
| A6EF | JSR find_station_bit3 ; Find station entry with bit 3 | |||||
| A6F2 | LDY hazel_txcb_count ; Load boot type from reply | |||||
| A6F5 | JSR flip_set_station_boot ; Set boot config for station | |||||
| A6F8 | PLP ; Restore processor status | |||||
| A6F9 | BCS fsreply_2_handle_loop ; Carry set: proceed with boot | |||||
| A6FB | JMP return_with_last_flag ; Return with last flag | |||||
| A6FE | .fsreply_2_skip_handles | |||||
| EQUS "-NET-FindLib" | ||||||
| A70A | EQUB &0D | |||||
| A70B | .fsreply_2_handle_loop←1← A6F9 BCS | |||||
| LDX #&11 ; X=&11: CMOS RAM byte index | ||||||
| A70D | JSR osbyte_a1 ; Read CMOS &11 via osbyte_a1 | |||||
| A710 | TYA ; Result to A | |||||
| A711 | AND #2 ; Mask bit 1 (auto-CLI flag) | |||||
| A713 | BEQ fsreply_2_store_handle ; Bit clear: skip auto-CLI | |||||
| A715 | LDX #<(fsreply_2_skip_handles) ; X = lo of fsreply_2_skip_handles (boot-cmd string ptr) | |||||
| A717 | LDY #>(fsreply_2_skip_handles) ; Y = hi of fsreply_2_skip_handles | |||||
| A719 | JSR oscli ; OSCLI to execute boot command | |||||
| A71C | .fsreply_2_store_handle←1← A713 BEQ | |||||
| PLA ; Pop saved A | ||||||
| A71D | CMP #2 ; Compare with 2 | |||||
| A71F | BCC check_auto_boot_flag ; Below: skip making FS permanent | |||||
| A721 | LDA #osbyte_make_temporary_filing_system_permanent ; OSBYTE &6D: make filing system perman ent | |||||
| A723 | JSR osbyte ; Master: Make temporary filing system permanent | |||||
| A726 | .check_auto_boot_flag←1← A71F BCC | |||||
| LDA hazel_fs_lib_flags ; Load config flags | ||||||
| A729 | TAX ; Save copy in X | |||||
| A72A | AND #4 ; Test bit 2 (auto-boot flag) | |||||
| A72C | PHP ; Save bit 2 test result | |||||
| A72D | TXA ; Restore full flags | |||||
| A72E | AND #&fb ; Clear bit 2 (consume flag) | |||||
| A730 | STA hazel_fs_lib_flags ; Store cleared flags | |||||
| A733 | PLP ; Restore bit 2 test result | |||||
| A734 | BNE boot_suffix_string ; Bit 2 was set: skip to boot cmd | |||||
| A736 | LDA #osbyte_scan_keyboard ; OSBYTE &79: scan keyboard | |||||
| A738 | LDX #(255 - inkey_key_ctrl) EOR 128 ; X=internal key number EOR 128 | |||||
| A73A | JSR osbyte ; Test for 'CTRL' key pressed (X=129) | |||||
| A73D | TXA ; X has top bit set if 'CTRL' pressed | |||||
| A73E | BPL boot_suffix_string ; CTRL not pressed: proceed to boot | |||||
| A740 | .boot_load_cmd←1← A762 BEQ | |||||
| RTS ; CTRL pressed: cancel boot, return | ||||||
| A741 | EQUS "L.-NET-!Boot" ; Boot command 'L.-NET-!Boot' (Load !Boot) | |||||
| A74D | EQUB &0D ; CR terminator | |||||
| A74E | EQUS "E.-NET-!Boot" ; Boot command 'E.-NET-!Boot' (Exec !Boot) | |||||
| A75A | EQUB &0D ; CR terminator | |||||
| A75B | .boot_prefix_string←1← A764 LDX | |||||
| EQUS "ZAHN" ; Boot command low-byte index table -- 4 bytes spelling 'ZAHN', each the low byte of a boot-command address in the &A7xx page (Z=&5A, A=&41, H=&48, N=&4E) | ||||||
| A75F | .boot_suffix_string←2← A734 BNE← A73E BPL | |||||
| LDY hazel_fs_flags ; Read hazel_fs_flags (boot-state flag) | ||||||
| A762 | BEQ boot_load_cmd ; Z: take boot_load_cmd path | |||||
| fall through ↓ | ||||||
Look up boot command in boot_prefix_string table and OSCLI itLoads X = boot_prefix_string,Y (the low byte of the boot-command address), sets Y=&A7 (high byte = &A7xx area where the boot strings live), then JMPs to oscli with (X,Y) pointing at a CR-terminated command string. Single caller (&A5D4 in the RUN-then- boot dispatch).
|
||||
| A764 | .boot_cmd_oscli←1← A5D4 JMP | |||
| LDX boot_prefix_string,y ; Load boot-command low byte from boot_prefix_string[Y] | ||||
| A767 | LDY #&a7 ; Y=&A7: high byte (boot strings live in &A7xx) | |||
| A769 | JMP oscli ; Tail-jump to OSCLI to execute the boot command | |||
ANFS *command dispatch tables (5 concatenated sub-tables)See the comment block immediately above the
|
|
| A76C | .cmd_table_fs←11← 8BD8 LDA← 8BE7 LDA← 8BEF LDA← 8BFC LDA← 9465 LDA← 946D LDA← 95EE LDA← A460 LDA← A465 LDA← A475 LDA← A4AC LDA |
| EQUS "Net" ; Econet HW check + select NFS | |
| A76F | EQUB &80 ; no syn |
| A770 | EQUW cmd_net_check_hw-1 |
| A772 | EQUS "Pollps" ; syn 8: (<stn. id.>| |
| A778 | EQUB &88 ; syn &8 |
| A779 | EQUW cmd_pollps-1 |
| A77B | EQUS "Prot" ; toggle CMOS protection bit |
| A77F | EQUB &80 ; no syn |
| A780 | EQUW cmd_prot-1 |
| A782 | EQUS "PS" ; syn 8: (<stn. id.>| |
| A784 | EQUB &88 ; syn &8 |
| A785 | EQUW cmd_ps-1 |
| A787 | EQUS "Roff" ; printer offline |
| A78B | EQUB &80 ; no syn |
| A78C | EQUW cmd_roff-1 |
| A78E | EQUS "Unprot" ; toggle CMOS protection bit |
| A794 | EQUB &80 ; no syn |
| A795 | EQUW cmd_unprot-1 |
| A797 | EQUS "Wdump" ; syn 4 -- *DUMP alias |
| A79C | EQUB &C4 ; syn &4, V if no arg |
| A79D | EQUW cmd_dump-1 |
| A79F | EQUB &80 ; Sub-table 1 end (walker reads &80 -> stop) |
| A7A0 | EQUB &80 ; Padding (alignment before sub-table 2) |
| A7A1 | .cmd_table_nfs |
| EQUS "Access" ; syn 9: |
|
| A7A7 | EQUB &C9 ; syn &9, V if no arg V no arg; syn 9: obj> (L)(W)(R)... |
| A7A8 | EQUW cmd_fs_operation-1 |
| A7AA | EQUS "Bye" ; log off FS |
| A7AD | EQUB &80 ; no syn |
| A7AE | EQUW cmd_bye-1 |
| A7B0 | EQUS "Cdir" ; syn 6 -- create directory |
| A7B4 | EQUB &C6 ; syn &6, V if no arg |
| A7B5 | EQUW cmd_cdir-1 |
| A7B7 | EQUS "Dir" ; syn 1: ( |
| A7BA | EQUB &81 ; syn &1 |
| A7BB | EQUW cmd_dir-1 |
| A7BD | EQUS "Flip" ; swap fs/private workspace |
| A7C1 | EQUB &80 ; no syn |
| A7C2 | EQUW cmd_flip-1 |
| A7C4 | EQUS "FS" ; syn &B -- file-server selection |
| A7C6 | EQUB &8B ; syn &B |
| A7C7 | EQUW cmd_fs-1 |
| A7C9 | .cmd_table_nfs_iam←1← 8DC8 LDA |
| EQUS "I am" ; syn 2: ( |
|
| A7CD | EQUB &C2 ; syn &2, V if no arg |
| A7CE | EQUW cmd_iam_save_ctx-1 |
| A7D0 | EQUS "Lcat" ; syn 1: ( |
| A7D4 | EQUB &81 ; syn &1 |
| A7D5 | EQUW cmd_lcat-1 |
| A7D7 | EQUS "Lex" ; syn 1: ( |
| A7DA | EQUB &81 ; syn &1 |
| A7DB | EQUW cmd_lex-1 |
| A7DD | EQUS "Lib" ; syn 5: |
| A7E0 | EQUB &C5 ; syn &5, V if no arg |
| A7E1 | EQUW cmd_fs_operation-1 |
| A7E3 | EQUS "Pass" ; syn 7: |
| A7E7 | EQUB &C7 ; syn &7, V if no arg |
| A7E8 | EQUW cmd_pass-1 |
| A7EA | EQUS "Rename" ; syn &A: |
| A7F0 | EQUB &CA ; syn &A, V if no arg |
| A7F1 | EQUW cmd_rename-1 |
| A7F3 | EQUS "Wipe" ; syn 1: ( |
| A7F7 | EQUB &81 ; syn &1 |
| A7F8 | EQUW cmd_wipe-1 |
| A7FA | EQUB &80 ; Sub-table 2 end (walker reads &80 -> stop) |
| A7FB | EQUB &2C ; Padding -- &2C 8E happens to spell &8E2D = check_urd_prefix but is never read |
| A7FC | EQUB &8E ; Padding (continued) |
| A7FD | .cmd_table_help_topics |
| EQUS "Net" ; *HELP NET | |
| A800 | EQUB &80 ; no syn |
| A801 | EQUW help_net-1 |
| A803 | EQUS "Utils" ; *HELP UTILS |
| A808 | EQUB &80 ; no syn |
| A809 | EQUW help_utils-1 |
| A80B | EQUB &80 ; Sub-table 3 end (walker reads &80 -> stop) |
| A80C | .cmd_table_syntax_help |
| EQUS "FS" ; FS not selected | |
| A80E | EQUB &C1 ; syn &1, V if no arg |
| A80F | EQUW set_fs_or_ps_cmos_station-1 |
| A811 | EQUS "PS" ; PS not selected |
| A813 | EQUB &C3 ; syn &3, V if no arg |
| A814 | EQUW set_fs_or_ps_cmos_station-1 |
| A816 | EQUS "NoSpace" ; caller &9623 |
| A81D | EQUB &80 ; no syn |
| A81E | EQUW &9622 |
| A820 | EQUS "Space" ; caller &9619 |
| A825 | EQUB &80 ; no syn |
| A826 | EQUW &9618 |
| A828 | EQUB &80 ; Sub-tables 4/5 separator |
| A829 | EQUS "FS" ; caller &9670 |
| A82B | EQUB &81 ; syn &1 |
| A82C | EQUW print_fs_address-1 |
| A82E | EQUS "PS" ; caller &965F |
| A830 | EQUB &83 ; syn &3 |
| A831 | EQUW print_ps_address-1 |
| A833 | EQUS "Space" ; caller &9641 |
| A838 | EQUB &80 ; no syn |
| A839 | EQUW &9641 |
Service 8: unrecognised OSWORDHandles 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.
|
||||||
| A83B | .svc_8_osword | |||||
| BRA osword_store_svc_state ; BRA osword_store_svc_state -- skip past 22-byte caller-cleanup frame CLC so SBC subtracts value+1 | ||||||
| A83D | EQUB &A5, &EF, ; OSWORD setup state (13 bytes -- constants &E9, &0D, ; and offsets used by svc_8_osword) A = &30, &2D, ; OSWORD - &0E (CLC+SBC = -&0E) Below &0E: &C9, &07, ; not ours, return Index >= 7? (OSWORD > &B0, &29, ; &14) Above &14: not ours, return X=OSWORD &AA, &A0, ; handler index (0-6) Y=6: save 6 workspace &06 ; bytes | |||||
| A84A | .loop_save_osword_workspace←1← A855 BNE | |||||
| LDA svc_state,y ; Read svc_state[Y] (frame slot) | ||||||
| A84D | PHA ; Save on stack | |||||
| A84E | LDA tx_imm_idx_base,y ; Load OSWORD parameter byte | |||||
| A851 | STA svc_state,y ; Copy parameter to workspace | |||||
| A854 | DEY ; Next slot | |||||
| A855 | .osword_store_svc_state←1← A83B BRA | |||||
| BNE loop_save_osword_workspace ; Loop until Y wraps | ||||||
| A857 | JSR osword_setup_handler ; Set up dispatch and save state | |||||
| A85A | LDY #&fa ; Y=&FA: restore 6 workspace bytes | |||||
| A85C | .loop_restore_osword_workspace←1← A861 BNE | |||||
| PLA ; Restore saved workspace byte | ||||||
| A85D | STA nmi_buf_idx_base,y ; Store to osword_flag workspace | |||||
| A860 | INY ; Next byte | |||||
| A861 | BNE loop_restore_osword_workspace ; Loop until all 6 restored | |||||
| A863 | 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.
|
||||
| A864 | .osword_setup_handler←1← A857 JSR | |||
| LDA osword_subcode_dispatch,x ; X = OSWORD index (0-6) | ||||
| A867 | PHA ; Push for stack frame manipulation | |||
| A868 | LDA osword_pb_ready,x ; Load handler address low byte | |||
| A86B | PHA ; Push again | |||
| A86C | LDA (ws_ptr_hi),y ; Copy 3 bytes (Y=2,1,0) | |||
| A86E | STY svc_state ; Load from osword_flag workspace | |||
| A870 | .return_from_osword_setup | |||
| RTS ; RTS dispatches to pushed handler | ||||
| A871 | .osword_pb_ready←1← A868 LDA | |||
| EQUB &7E, ; PB-ready / parameter table (3 bytes) read by &6F, ; osword_setup_handler at &A868 via LDA &0F ; osword_pb_ready,X | ||||
| A874 | .osword_0e_handler | |||
| BIT suffix_copy_loop ; BIT $abs -- 3-byte skip-trick that jumps over the extract_osword_subcode prologue when called via &A874 | ||||
| fall through ↓ | ||||
Decode OSWORD &0E parameter byte and branch to handlerRight-shifts
The two |
|||||||||
| A877 | .extract_osword_subcode | ||||||||
| LSR ws_page ; Shift ws_page right -- splits parameter byte into upper / lower nibbles | |||||||||
| A879 | TAY ; A = sub-code | ||||||||
| A87A | LDA #&a9 ; LDA #&A9 -- 2-byte BIT-trick filler (skipped when entered at &A87E) | ||||||||
| A87C | LDA #&a9 ; LDA #&A9 -- 2-byte BIT-trick filler | ||||||||
| A87E | LDY ws_template_source ; Load template source pointer | ||||||||
| A881 | EOR ws_precomputed_value | ||||||||
| A884 | CMP #4 ; Compare with &04 | ||||||||
| A886 | BEQ save_txcb_and_convert ; Equal: take save_txcb_and_convert path | ||||||||
| A888 | CMP #3 ; Restore A (OSWORD sub-code) | ||||||||
| A88A | BEQ save_txcb_done ; Equal: take save_txcb_done path | ||||||||
| A88C | LDA #8 ; Other sub-codes: set state = 8 | ||||||||
| A88E | STA svc_state ; Store service state | ||||||||
| A890 | .return_from_osword_0e | ||||||||
| RTS ; Return | |||||||||
| A891 | .save_txcb_and_convert←2← A886 BEQ← A8E7 JSR | ||||||||
| LDX #0 ; X=0: start of TX control block | |||||||||
| A893 | LDY #&10 ; Y=&10: length of TXCB to save | ||||||||
| A895 | JSR save_net_tx_cb ; Save current TX control block | ||||||||
| A898 | LDA hazel_exec_addr ; Load seconds from clock workspace | ||||||||
| A89B | JSR bin_to_bcd ; Convert binary to BCD | ||||||||
| A89E | STA hazel_txcb_tx_status ; Store BCD seconds | ||||||||
| A8A1 | LDA hazel_txcb_result ; Load minutes from clock workspace | ||||||||
| A8A4 | JSR bin_to_bcd ; Convert binary to BCD | ||||||||
| A8A7 | STA hazel_txcb_size_hi ; Store BCD minutes | ||||||||
| A8AA | LDA hazel_txcb_count ; Load hours from clock workspace | ||||||||
| A8AD | JSR bin_to_bcd ; Convert binary to BCD | ||||||||
| A8B0 | STA hazel_exec_addr ; Store BCD hours | ||||||||
| A8B3 | LDA #0 ; Clear hours high position | ||||||||
| A8B5 | STA hazel_txcb_result ; Store zero | ||||||||
| A8B8 | LDA hazel_txcb_flag ; Load day+month byte | ||||||||
| A8BB | PHA ; Save for later high nibble extract | ||||||||
| A8BC | LDA hazel_txcb_data ; Load day value | ||||||||
| A8BF | JSR bin_to_bcd ; Convert day to BCD | ||||||||
| A8C2 | STA hazel_txcb_count ; Store BCD day | ||||||||
| A8C5 | PLA ; Restore day+month byte | ||||||||
| A8C6 | PHA ; Push current A | ||||||||
| A8C7 | AND #&0f ; Mask low nibble (month low bits) | ||||||||
| A8C9 | JSR bin_to_bcd ; Convert to BCD | ||||||||
| A8CC | STA hazel_txcb_flag ; Store BCD month | ||||||||
| A8CF | PLA ; Pop saved value | ||||||||
| A8D0 | LSR ; Shift high nibble down | ||||||||
| A8D1 | LSR ; Divide by 4 | ||||||||
| A8D2 | LSR ; (continued) | ||||||||
| A8D3 | LSR ; 4th shift: isolate high nibble | ||||||||
| A8D4 | ADC #&51 ; Add &51 (offset base) | ||||||||
| A8D6 | JSR bin_to_bcd ; Convert year to BCD | ||||||||
| A8D9 | STA hazel_txcb_data ; Store BCD year | ||||||||
| A8DC | LDY #6 ; Copy 7 bytes (Y=6 down to 0) | ||||||||
| A8DE | .loop_copy_bcd_to_pb←1← A8E4 BPL | ||||||||
| LDA hazel_txcb_data,y ; Load BCD byte from workspace | |||||||||
| A8E1 | STA (ws_ptr_hi),y ; Store to parameter block | ||||||||
| A8E3 | DEY ; Next byte down | ||||||||
| A8E4 | BPL loop_copy_bcd_to_pb ; Loop for all 7 bytes | ||||||||
| A8E6 | RTS ; Return | ||||||||
| A8E7 | .save_txcb_done←1← A88A BEQ | ||||||||
| JSR save_txcb_and_convert ; Convert TXCB date/time bytes to BCD | |||||||||
| A8EA | LDY #7 ; Y=7: copy 8 bytes (Y=7 down to 0) | ||||||||
| A8EC | .loop_copy_pbytes_to_workspace←1← A8F2 BNE | ||||||||
| LDA hazel_txcb_lib,y ; Load BCD byte from TXCB area (hazel_txcb_lib + Y) | |||||||||
| A8EF | STA (ws_ptr_hi),y ; Store to PB[Y] | ||||||||
| A8F1 | DEY ; Decrement Y (advance backwards) | ||||||||
| A8F2 | BNE loop_copy_pbytes_to_workspace ; Loop until Y wraps | ||||||||
| A8F4 | LDA #2 ; A=2: PB[0] parameter for OSWORD &0E (seconds-since-midnight format) | ||||||||
| A8F6 | STA (ws_ptr_hi),y ; Store parameter at PB[0] | ||||||||
| A8F8 | LDA #osword_read_cmos_clock ; A=&0E: OSWORD &0E (read CMOS RTC) | ||||||||
| A8FA | LDX ws_ptr_hi ; X = PB pointer low | ||||||||
| A8FC | LDY table_idx ; Y = PB pointer high (via table_idx scratch) | ||||||||
| A8FE | JMP osword ; Read CMOS clock | ||||||||
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.
|
|||||||
| A901 | .bin_to_bcd←6← A89B JSR← A8A4 JSR← A8AD JSR← A8BF JSR← A8C9 JSR← A8D6 JSR | ||||||
| PHP ; Save caller flags (D may be in any state) | |||||||
| A902 | TAX ; Save A across decimal-mode arithmetic | ||||||
| A903 | BEQ done_bcd_convert ; Zero: result is 0, skip loop | ||||||
| A905 | SED ; Enter decimal mode | ||||||
| A906 | LDA #0 ; Start BCD result at 0 | ||||||
| A908 | .loop_bcd_add←1← A90C BNE | ||||||
| CLC ; Clear carry for BCD add | |||||||
| A909 | ADC #1 ; Add 1 in decimal mode | ||||||
| A90B | DEX ; Count down binary value | ||||||
| A90C | BNE loop_bcd_add ; Loop until zero | ||||||
| A90E | .done_bcd_convert←1← A903 BEQ | ||||||
| PLP ; Restore caller flags (incl. D) | |||||||
| A90F | RTS ; Return with BCD result in A | ||||||
OSWORD &10 handler: send network packetASL on
|
||||
| A910 | .osword_10_handler | |||
| ASL tx_complete_flag ; ASL tx_complete_flag: old bit 7 -> C | ||||
| A913 | TYA ; A = Y (saved index) | |||
| A914 | BCS setup_ws_rx_ptrs ; C=1 (TX idle): start new transmission | |||
| A916 | STA (ws_ptr_hi),y ; C=0 (TX busy): write status byte back to PB | |||
| A918 | RTS ; Return (TX still in progress) | |||
| A919 | .setup_ws_rx_ptrs←1← A914 BCS | |||
| LDA net_rx_ptr_hi ; Read net_rx_ptr_hi | ||||
| A91B | STA ws_ptr_lo ; Copy to ws_ptr_lo | |||
| A91D | STA nmi_tx_block_hi ; Also set as NMI TX block high | |||
| A91F | LDA #&6f ; Low byte = &6F | |||
| A921 | STA osword_flag ; Set osword_flag | |||
| A923 | STA nmi_tx_block ; Set NMI TX block low | |||
| A925 | LDX #&0f ; X=&0F: byte count for copy | |||
| A927 | JSR copy_pb_byte_to_ws ; Copy data and begin transmission | |||
| A92A | JMP tx_begin ; Jump to begin Econet transmission | |||
OSWORD &11 handler: receive network packetReached via the OSWORD dispatch as well as via fall-through from
|
||||
| A92D | .osword_11_handler | |||
| LDX nfs_workspace_hi ; Load NFS workspace page high byte | ||||
| A92F | STX ws_ptr_lo ; Set workspace pointer high | |||
| A931 | STY osword_flag ; Set workspace pointer low from Y | |||
| A933 | ROR econet_flags ; Rotate Econet flags (save interrupt state) | |||
| A936 | TAY ; Y=OSWORD flag (slot specifier) | |||
| A937 | STA work_ae ; Store OSWORD flag | |||
| A939 | BNE use_specified_slot ; Non-zero: use specified slot | |||
| A93B | LDA #3 ; A=3: start searching from slot 3 | |||
| A93D | .loop_find_rx_slot←1← A94F BNE | |||
| JSR byte_to_2bit_index ; Convert slot to 2-bit workspace index | ||||
| A940 | BCS store_rx_result ; C set: slot invalid, store result | |||
| A942 | LSR ; Divide by 2 | |||
| A943 | LSR ; Continue shift | |||
| A944 | TAX ; Index to X | |||
| A945 | LDA (osword_flag),y ; Load workspace byte at offset | |||
| A947 | BEQ store_rx_result ; Zero: slot empty, store result | |||
| A949 | CMP #&3f ; Compare with &3F ('?' marker) | |||
| A94B | BEQ store_rx_slot_found ; Match: slot found for receive | |||
| A94D | INX ; Step to next slot | |||
| A94E | TXA ; Transfer back to A | |||
| A94F | BNE loop_find_rx_slot ; Loop back (A != 0) | |||
| A951 | .store_rx_slot_found←1← A94B BEQ | |||
| TXA ; Found slot index | ||||
| A952 | LDX #0 ; X=0: index for indirect store | |||
| A954 | STA (ws_ptr_hi,x) ; Store slot number to PB byte 0 | |||
| A956 | .use_specified_slot←1← A939 BNE | |||
| JSR byte_to_2bit_index ; Convert specified slot to workspace index | ||||
| A959 | BCS store_rx_result ; C set: slot invalid, store result | |||
| A95B | DEY ; Back up scan | |||
| A95C | STY osword_flag ; Update workspace pointer low | |||
| A95E | LDA #&c0 ; A=&C0: slot active marker | |||
| A960 | LDY #1 ; Y=1: result-byte offset | |||
| A962 | LDX #&0b ; X=&0B: byte count for PB copy | |||
| A964 | CPY work_ae ; Compare Y with OSWORD flag | |||
| A966 | ADC (osword_flag),y ; Add workspace byte (check slot state) | |||
| A968 | BEQ copy_pb_and_mark ; Zero: slot ready, copy PB and mark | |||
| A96A | BMI increment_and_retry ; Negative: slot busy, increment and retry | |||
| A96C | .loop_copy_slot_data←1← A97C BNE | |||
| CLC ; For the ADC chain | ||||
| A96D | .copy_pb_and_mark←1← A968 BEQ | |||
| JSR copy_pb_byte_to_ws ; Copy PB byte to workspace slot | ||||
| A970 | BCS osword_11_done ; C set: copy done, finish | |||
| A972 | LDA #&3f ; A=&3F: mark slot as pending ('?') | |||
| A974 | LDY #1 ; Y=1: workspace flag offset | |||
| A976 | STA (osword_flag),y ; Store pending marker to workspace | |||
| A978 | BNE osword_11_done ; ALWAYS branch | |||
| A97A | .increment_and_retry←1← A96A BMI | |||
| ADC #1 ; Increment retry counter | ||||
| A97C | BNE loop_copy_slot_data ; Non-zero: retry copy loop | |||
| A97E | DEY ; Decrement Y (adjust offset) | |||
| A97F | .store_rx_result←3← A940 BCS← A947 BEQ← A959 BCS | |||
| STA (ws_ptr_hi),y ; Store result A to PB via Y | ||||
| A981 | .osword_11_done←2← A970 BCS← A978 BNE | |||
| ROL econet_flags ; Rotate Econet flags back (restore state) | ||||
| A984 | RTS ; Return from OSWORD 11 handler | |||
OSWORD &12 handler: receive packet from workspaceReads
|
||||
| A985 | .osword_12_handler | |||
| LDA net_rx_ptr_hi ; Set workspace from RX ptr high | ||||
| A987 | STA ws_ptr_lo ; Store to ws_ptr_lo | |||
| A989 | LDY #&7f ; Y=&7F: last byte of RX buffer | |||
| A98B | LDA (net_rx_ptr),y ; Load port/count from RX buffer | |||
| A98D | INY ; Y=&80: set workspace pointer | |||
| A98E | STY osword_flag ; Store as osword_flag | |||
| A990 | TAX ; X = port/count value | |||
| A991 | DEX ; X-1: adjust count | |||
| A992 | LDY #0 ; Y=0 for copy | |||
| A994 | JSR copy_pb_byte_to_ws ; Copy workspace data | |||
| A997 | JMP commit_state_byte ; Update state and return | |||
| A99A | .osword_13_dispatch | |||
| TAX ; X = sub-code | ||||
| A99B | CMP #&13 ; Sub-code < &13? | |||
| A99D | BCS return_from_osword_13 ; Out of range: return | |||
| A99F | LDA osword_13_dispatch_hi,x ; Read dispatch hi from osword_13_dispatch_hi+X | |||
| A9A2 | PHA ; Push hi for RTS dispatch | |||
| A9A3 | LDA osword_13_dispatch_lo,x ; Read dispatch lo from osword_13_dispatch_lo+X | |||
| A9A6 | PHA ; Push lo for RTS dispatch | |||
| A9A7 | .return_from_osword_13←1← A99D BCS | |||
| RTS ; RTS -> dispatched OSWORD &13 sub-handler | ||||
OSWORD &13 dispatch low-byte table (18 entries)Read by |
|
| A9A8 | .osword_13_dispatch_lo←1← A9A3 LDA |
| EQUB <(osword_13_read_station-1) ; sub &00: osword_13_read_station (read FS station) | |
| A9A9 | EQUB <(osword_13_set_station-1) ; sub &01: osword_13_set_station (set FS station) |
| A9AA | EQUB <(osword_13_read_ws_pair-1) ; sub &02: osword_13_read_ws_pair (read workspace pair) |
| A9AB | EQUB <(osword_13_write_ws_pair-1) ; sub &03: osword_13_write_ws_ pair (write workspace pair) |
| A9AC | EQUB <(osword_13_read_prot-1) ; sub &04: osword_13_read_prot (read protection mask) |
| A9AD | EQUB <(osword_13_write_prot-1) ; sub &05: osword_13_write_prot (write protection mask) |
| A9AE | EQUB <(osword_13_read_handles-1) ; sub &06: osword_13_read_handles (read transfer handles) |
| A9AF | EQUB <(osword_13_set_handles-1) ; sub &07: osword_13_set_handles (set transfer handles) |
| A9B0 | EQUB <(osword_13_read_rx_flag-1) ; sub &08: osword_13_read_rx_flag (read RX flag) |
| A9B1 | EQUB <(osword_13_read_rx_port-1) ; sub &09: osword_13_read_rx_port (read RX port) |
| A9B2 | EQUB <(osword_13_read_error-1) ; sub &0A: osword_13_read_error (read last error) |
| A9B3 | EQUB <(osword_13_read_context-1) ; sub &0B: osword_13_read_context (read context) |
| A9B4 | EQUB <(osword_13_read_csd-1) ; sub &0C: osword_13_read_csd (read CSD) |
| A9B5 | EQUB <(osword_13_write_csd-1) ; sub &0D: osword_13_write_csd (write CSD) |
| A9B6 | EQUB <(osword_13_read_free_bufs-1) ; sub &0E: osword_13_read_free_ bufs (read free buffers) |
| A9B7 | EQUB <(osword_13_read_ctx_3-1) ; sub &0F: osword_13_read_ctx_3 (read context byte 3) |
| A9B8 | EQUB <(osword_13_write_ctx_3-1) ; sub &10: osword_13_write_ctx_3 (write context byte 3) |
| A9B9 | EQUB <(osword_13_bridge_query-1) ; sub &11: osword_13_bridge_query (bridge query) |
OSWORD &13 dispatch high-byte table (18 entries)Read by |
|
| A9BA | .osword_13_dispatch_hi←1← A99F LDA |
| EQUB >(osword_13_read_station-1) ; sub &00: osword_13_read_station | |
| A9BB | EQUB >(osword_13_set_station-1) ; sub &01: osword_13_set_station |
| A9BC | EQUB >(osword_13_read_ws_pair-1) ; sub &02: osword_13_read_ws_pair |
| A9BD | EQUB >(osword_13_write_ws_pair-1) ; sub &03: osword_13_write_ws_ pair |
| A9BE | EQUB >(osword_13_read_prot-1) ; sub &04: osword_13_read_prot |
| A9BF | EQUB >(osword_13_write_prot-1) ; sub &05: osword_13_write_prot |
| A9C0 | EQUB >(osword_13_read_handles-1) ; sub &06: osword_13_read_handles |
| A9C1 | EQUB >(osword_13_set_handles-1) ; sub &07: osword_13_set_handles |
| A9C2 | EQUB >(osword_13_read_rx_flag-1) ; sub &08: osword_13_read_rx_flag |
| A9C3 | EQUB >(osword_13_read_rx_port-1) ; sub &09: osword_13_read_rx_port |
| A9C4 | EQUB >(osword_13_read_error-1) ; sub &0A: osword_13_read_error |
| A9C5 | EQUB >(osword_13_read_context-1) ; sub &0B: osword_13_read_context |
| A9C6 | EQUB >(osword_13_read_csd-1) ; sub &0C: osword_13_read_csd |
| A9C7 | EQUB >(osword_13_write_csd-1) ; sub &0D: osword_13_write_csd |
| A9C8 | EQUB >(osword_13_read_free_bufs-1) ; sub &0E: osword_13_read_free_ bufs |
| A9C9 | EQUB >(osword_13_read_ctx_3-1) ; sub &0F: osword_13_read_ctx_3 |
| A9CA | EQUB >(osword_13_write_ctx_3-1) ; sub &10: osword_13_write_ctx_3 |
| A9CB | EQUB >(osword_13_bridge_query-1) ; sub &11: osword_13_bridge_query |
OSWORD &13 sub 0: read file server stationReturns the current file server station and network numbers in
|
|
| A9CC | .osword_13_read_station |
| JSR ensure_fs_selected ; Ensure NFS is currently the selected FS | |
| A9CF | .read_station_bytes |
| LDY #2 ; Y=2: copy 2 bytes | |
| A9D1 | .loop_copy_station←1← A9D7 BNE |
| LDA hazel_minus_1,y ; Load station byte | |
| A9D4 | STA (osword_pb_ptr),y ; Store to PB[Y] |
| A9D6 | DEY ; Step back |
| A9D7 | BNE loop_copy_station ; Loop for bytes 2..1 |
| A9D9 | RTS ; Return |
OSWORD &13 sub 1: set file server stationSets the file server station and network numbers from |
|
| A9DA | .osword_13_set_station |
| JSR ensure_fs_selected ; Ensure NFS is currently the selected FS | |
| A9DD | .osword_13_set_station_body |
| LDY #0 ; Y=0 for process_all_fcbs | |
| A9DF | JSR process_all_fcbs ; Close all open FCBs |
| A9E2 | LDY #2 ; Y=2: copy 2 bytes |
| A9E4 | .loop_store_station←1← A9EA BNE |
| LDA (osword_pb_ptr),y ; Load new station byte from PB | |
| A9E6 | STA hazel_minus_1,y ; Store to fs_server_base |
| A9E9 | DEY ; Step back to previous byte |
| A9EA | BNE loop_store_station ; Loop for bytes 2..1 |
| A9EC | JSR clear_if_station_match ; Clear handles if station matches |
| A9EF | LDA #&0e ; A=&0E: bits 1..3 (FS-state mask) |
| A9F1 | TSB fs_flags ; Set fs_flags bits 1..3 |
| A9F4 | LDA #&40 ; A=&40: FS-active flag bit |
| A9F6 | TRB fs_flags ; Clear FS-active flag (bit 6) |
| A9F9 | LDX #&0f ; X=&0F: scan all 16 FCB slots (X = 15 down to 0) |
| A9FB | .scan_fcb_entry←1← AA63 BPL |
| LDA hazel_fcb_status,x ; Load FCB flags | |
| A9FE | TAY ; Save flags in Y |
| A9FF | AND #2 ; Test bit 1 (FCB allocated?) |
| AA01 | BEQ next_fcb_entry ; No: skip to next entry |
| AA03 | TYA ; Entry index to A |
| AA04 | AND #&df ; Mask bit 5 |
| AA06 | STA hazel_fcb_status,x ; Store updated flags |
| AA09 | TAY ; Save in Y |
| AA0A | JSR match_station_net ; Does FCB match new station? |
| AA0D | BNE next_fcb_entry ; No match: skip to next |
| AA0F | CLC ; Clear carry |
| AA10 | TYA ; Restore flags |
| AA11 | AND #4 ; Test bit 2 (handle 1 active?) |
| AA13 | BEQ check_handle_2 ; No: check handle 2 |
| AA15 | TYA ; Restore flags |
| AA16 | ORA #&20 ; Set bit 5 (handle reassigned) |
| AA18 | TAY |
| AA19 | LDA hazel_fcb_slot_attr,x ; Get FCB high byte |
| AA1C | STA hazel_fs_saved_station ; Store as handle 1 station |
| AA1F | TXA ; FCB index |
| AA20 | ADC #&20 ; Add &20 for FCB table offset |
| AA22 | STA hazel_fcb_slot_1 ; Store as handle 1 FCB index |
| AA25 | LDA #2 ; A=2: fs_flags bit 1 mask |
| AA27 | TRB fs_flags ; Clear fs_flags bit 1 |
| AA2A | .check_handle_2←1← AA13 BEQ |
| TYA ; Y still holds the saved FCB status -- TYA so we can re-test bit 3 (handle-2 active flag) | |
| AA2B | AND #8 ; Test bit 3 (handle 2 active?) |
| AA2D | BEQ check_handle_3 ; No: check handle 3 |
| AA2F | TYA |
| AA30 | ORA #&20 ; Set bit 5 |
| AA32 | TAY |
| AA33 | LDA hazel_fcb_slot_attr,x ; Get FCB high byte |
| AA36 | STA hazel_fs_context_copy ; Store as handle 2 station |
| AA39 | TXA ; FCB index |
| AA3A | ADC #&20 ; Add &20 for FCB table offset |
| AA3C | STA hazel_fcb_slot_2 ; Store as handle 2 FCB index |
| AA3F | LDA #4 ; A=4: fs_flags bit 2 mask |
| AA41 | TRB fs_flags ; Clear fs_flags bit 2 |
| AA44 | .check_handle_3←1← AA2D BEQ |
| TYA ; Y still holds the saved FCB status -- TYA so we can re-test bit 4 (handle-3 active flag) | |
| AA45 | AND #&10 ; Test bit 4 (handle 3 active?) |
| AA47 | BEQ store_updated_status ; No: store final flags |
| AA49 | TYA ; Restore flags |
| AA4A | ORA #&20 ; Set bit 5 |
| AA4C | TAY ; Save updated flags |
| AA4D | LDA hazel_fcb_slot_attr,x ; Get FCB high byte |
| AA50 | STA hazel_fs_prefix_stn ; Store as handle 3 station |
| AA53 | TXA ; FCB index |
| AA54 | ADC #&20 ; Add &20 for FCB table offset |
| AA56 | STA hazel_fcb_slot_3 ; Store as handle 3 FCB index |
| AA59 | LDA #8 ; A=8: fs_flags bit 3 (FS-error pending) |
| AA5B | TRB fs_flags ; Clear FS-error-pending flag |
| AA5E | .store_updated_status←1← AA47 BEQ |
| TYA ; A = Y for store | |
| AA5F | STA hazel_fcb_status,x ; Store updated status into hazel_fcb_status[X] |
| AA62 | .next_fcb_entry←2← AA01 BEQ← AA0D BNE |
| DEX ; Decrement entry counter | |
| AA63 | BPL scan_fcb_entry ; Loop while X >= 0 (scan all FCBs) |
| AA65 | LDA #&0e ; A=&0E: status flag value |
| AA67 | BIT fs_flags ; Test fs_flags bits 1..3 |
| AA6A | BNE return_4 ; Non-zero: skip the FS-active set |
| AA6C | LDA #&40 ; A=&40: FS-active flag bit |
| AA6E | TSB fs_flags ; Set FS-active flag (bit 6 of fs_flags) |
| AA71 | .return_4←1← AA6A BNE |
| RTS ; Return -- FCB-status update complete | |
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. |
|
| AA72 | .osword_13_read_csd |
| CLC ; WS-to-PB direction (read) | |
| AA73 | BCC setup_csd_copy ; Skip SEC |
| fall through ↓ | |
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. |
|
| AA75 | .osword_13_write_csd |
| SEC ; C=1: PB-to-workspace direction | |
| AA76 | .setup_csd_copy←1← AA73 BCC |
| LDA #&17 ; Workspace offset &17 | |
| AA78 | STA osword_flag ; Save A as osword_flag (counter) |
| AA7A | LDA net_rx_ptr_hi ; Page from RX pointer high byte |
| AA7C | STA ws_ptr_lo ; Set ws_ptr_hi |
| AA7E | LDY #1 ; Y=1: first PB data byte |
| AA80 | 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.
|
||||||||
| AA82 | .copy_pb_byte_to_ws←5← A927 JSR← A96D JSR← A994 JSR← AA8E BPL← AA9B BCC | |||||||
| BCC copy_ws_byte_to_pb ; C=0: skip PB-to-WS copy | ||||||||
| AA84 | LDA (ws_ptr_hi),y ; C=1: load from parameter block | |||||||
| AA86 | STA (osword_flag),y ; Store to workspace | |||||||
| AA88 | .copy_ws_byte_to_pb←1← AA82 BCC | |||||||
| LDA (osword_flag),y ; Load from workspace | ||||||||
| AA8A | STA (ws_ptr_hi),y ; Store to parameter block | |||||||
| AA8C | INY ; Next byte | |||||||
| AA8D | DEX ; Count down | |||||||
| AA8E | BPL copy_pb_byte_to_ws ; Loop for all bytes | |||||||
| AA90 | 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. |
|
| AA91 | .osword_13_read_ws_pair |
| LDA nfs_workspace_hi ; Load workspace page high byte | |
| AA93 | STA ws_ptr_lo ; Set ws_ptr_hi |
| AA95 | INY ; Y=1 |
| AA96 | TYA ; A = current byte index |
| AA97 | STA osword_flag ; Set ws_ptr_lo = 1 |
| AA99 | TAX ; X=1: copy 2 bytes |
| AA9A | CLC ; WS-to-PB direction |
| AA9B | BCC copy_pb_byte_to_ws ; Copy via copy_pb_byte_to_ws |
| fall through ↓ | |
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. |
|
| AA9D | .osword_13_write_ws_pair |
| INY ; Y=1: first PB data byte | |
| AA9E | LDA (ws_ptr_hi),y ; Load PB[1] |
| AAA0 | INY ; Next byte |
| AAA1 | STA (nfs_workspace),y ; Store to (nfs_workspace)+2 |
| AAA3 | LDA (ws_ptr_hi),y ; Load PB[2] |
| AAA5 | INY ; Y=3 |
| AAA6 | STA (nfs_workspace),y ; Store to (nfs_workspace)+3 |
| AAA8 | JSR init_bridge_poll ; Reinitialise bridge routing |
| AAAB | EOR (nfs_workspace),y ; Compare result with workspace |
| AAAD | BNE return_from_write_ws_pair ; Different: leave unchanged |
| AAAF | STA (nfs_workspace),y ; Same: clear workspace byte |
| AAB1 | .return_from_write_ws_pair←1← AAAD BNE |
| RTS ; Return | |
OSWORD &13 sub 4: read protection maskReturns the current protection mask (ws_0d68) in PB[1]. |
|
| AAB2 | .osword_13_read_prot |
| LDA ws_0d68 ; Load protection mask | |
| AAB5 | JMP store_a_to_pb_1 ; Store to PB[1] and return |
OSWORD &13 sub 5: write protection maskLoads the new protection mask from |
|
| AAB8 | .osword_13_write_prot |
| INY ; Y=1: PB data offset | |
| AAB9 | LDA (ws_ptr_hi),y ; Load new mask from PB[1] |
| fall through ↓ | |
Store A in both shadow ACR/IER bytesCopies
|
||||
| AABB | .set_via_shadow_pair←2← 8FA6 JSR← B6D9 JSR | |||
| STA ws_0d68 ; Mirror A into ws_0d68 (shadow ACR) | ||||
| AABE | STA ws_0d69 ; Mirror A into ws_0d69 (shadow IER) | |||
| AAC1 | RTS ; Return | |||
OSWORD &13 sub 6: read FCB handle infoReturns the 3-byte FCB handle/port data from the workspace at
|
|
| AAC2 | .osword_13_read_handles |
| JSR ensure_fs_selected ; Ensure NFS is currently the selected FS | |
| AAC5 | LDY #3 ; Y=3: copy 3 bytes |
| AAC7 | .loop_copy_handles←1← AACD BNE |
| LDA hazel_fs_lib_flags,y ; Load handle byte | |
| AACA | STA (ws_ptr_hi),y ; Store to PB[Y] |
| AACC | DEY ; Previous byte |
| AACD | BNE loop_copy_handles ; Loop for bytes 3..1 |
| AACF | 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 channel tables. For valid handles with the appropriate flag bit, stores the station and FCB index, then updates flag bits across all FCB entries via update_fcb_flag_bits. |
|
| AAD0 | .osword_13_set_handles |
| JSR ensure_fs_selected ; Ensure NFS is currently the selected FS | |
| AAD3 | .start_set_handles |
| LDY #1 ; Y=1: first handle in PB | |
| AAD5 | .validate_handle←1← AB15 BNE |
| LDA (ws_ptr_hi),y ; Load handle value from PB[Y] | |
| AAD7 | CMP #&20 ; Must be >= &20 |
| AAD9 | BCC handle_invalid ; Below range: invalid |
| AADB | CMP #&30 ; Must be < &30 |
| AADD | BCS handle_invalid ; Above range: invalid |
| AADF | TAX ; X = handle value |
| AAE0 | LDA hazel_fcb_addr_mid,x ; Load fcb_attr_or_count_mid[handle] |
| AAE3 | BNE check_handle_alloc ; Non-zero: FCB exists |
| AAE5 | .handle_invalid←3← AAD9 BCC← AADD BCS← AAF1 BEQ |
| LDA #0 ; A=0: invalid-handle marker | |
| AAE7 | TAX ; X=&00 |
| AAE8 | STA (ws_ptr_hi,x) ; Clear PB[0] status |
| AAEA | BEQ next_handle_slot ; Skip to next handle |
| AAEC | .check_handle_alloc←1← AAE3 BNE |
| LDA hazel_fcb_state_byte,x ; Load fcb_flags[handle] flags | |
| AAEF | AND #2 ; Test bit 1 (allocated?) |
| AAF1 | BEQ handle_invalid ; Not allocated: invalid |
| AAF3 | TXA ; X = handle value |
| AAF4 | STA hazel_fs_lib_flags,y ; Store handle to fs_lib_flags+Y |
| AAF7 | LDA hazel_fcb_addr_mid,x ; Load station from fcb_attr_or_count_mid |
| AAFA | STA hazel_fs_network,y ; Store station to fs_server_net+Y |
| AAFD | CPY #1 ; Is this handle 1 (Y=1)? |
| AAFF | BNE assign_handle_2 ; No: check handle 2 |
| AB01 | TYA ; Save Y for processing |
| AB02 | PHA ; Push Y |
| AB03 | LDY #4 ; Bit mask &04 for handle 1 |
| AB05 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| AB08 | PLA ; Pop saved Y |
| AB09 | TAY ; Back to Y |
| AB0A | LDA hazel_fcb_state_byte,x ; Reload fcb_flags flags |
| AB0D | ORA #&24 ; Set bits 2+5 (active+updated) |
| AB0F | STA hazel_fcb_state_byte,x ; Store updated flags |
| AB12 | .next_handle_slot←3← AAEA BEQ← AB2E BNE← AB41 BNE |
| INY ; Next handle slot | |
| AB13 | CPY #4 ; Compare with 4 |
| AB15 | BNE validate_handle ; No: process next handle |
| AB17 | DEY ; Y=3 for return |
| AB18 | RTS ; Return |
| AB19 | .assign_handle_2←1← AAFF BNE |
| CPY #2 ; Is this handle 2 (Y=2)? | |
| AB1B | BNE assign_handle_3 ; No: must be handle 3 |
| AB1D | TYA ; Save current Y |
| AB1E | PHA ; Push Y |
| AB1F | LDY #8 ; Y=8 (handle-bit shift index) |
| AB21 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| AB24 | PLA ; Restore Y |
| AB25 | TAY ; Back to Y |
| AB26 | LDA hazel_fcb_state_byte,x ; Reload fcb_flags flags |
| AB29 | ORA #&28 ; Set bits 3 and 5 |
| AB2B | STA hazel_fcb_state_byte,x ; Store updated flags |
| AB2E | BNE next_handle_slot ; Next handle slot |
| AB30 | .assign_handle_3←1← AB1B BNE |
| TYA ; Handle 3: save Y | |
| AB31 | PHA ; Push for save/restore |
| AB32 | LDY #&10 ; Bit mask &10 for handle 3 |
| AB34 | JSR update_fcb_flag_bits ; Update flags across all FCBs |
| AB37 | PLA ; Pop saved value |
| AB38 | TAY ; Back to Y |
| AB39 | LDA hazel_fcb_state_byte,x ; Reload fcb_flags flags |
| AB3C | ORA #&30 ; Set bits 4+5 (active+updated) |
| AB3E | STA hazel_fcb_state_byte,x ; Store updated flags |
| AB41 | BNE next_handle_slot ; Next handle slot |
| fall through ↓ | |
Update FCB flag bits across all entriesScans all 16 FCB entries in hazel_fcb_status. 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.
|
||||||
| AB43 | .update_fcb_flag_bits←3← AB05 JSR← AB21 JSR← AB34 JSR | |||||
| TXA ; A = caller X | ||||||
| AB44 | PHA ; Push X | |||||
| AB45 | LDX #&0f ; X=&0F: scan all 16 FCB slots | |||||
| AB47 | .loop_scan_fcb_flags←1← AB63 BPL | |||||
| LDA hazel_fcb_status,x ; Load FCB flags | ||||||
| AB4A | ROL ; Shift bits 6-7 into bits 7-0 | |||||
| AB4B | ROL ; Shift bit into carry for test | |||||
| AB4C | BPL next_flag_entry ; Bit 6 clear: skip entry | |||||
| AB4E | TYA ; Restore Y (bit mask) | |||||
| AB4F | AND hazel_fcb_status,x ; Test mask bits against flags | |||||
| AB52 | BEQ no_flag_match ; Zero: no matching bits | |||||
| AB54 | TYA ; Matching: restore Y | |||||
| AB55 | ORA #&20 ; Set bit 5 (updated) | |||||
| AB57 | BNE clear_flag_bits ; Skip clear path | |||||
| AB59 | .no_flag_match←1← AB52 BEQ | |||||
| TYA ; No match: restore Y | ||||||
| AB5A | .clear_flag_bits←1← AB57 BNE | |||||
| EOR #&ff ; Invert all bits | ||||||
| AB5C | AND hazel_fcb_status,x ; Clear tested bits in flags | |||||
| AB5F | STA hazel_fcb_status,x ; Store updated flags | |||||
| AB62 | .next_flag_entry←1← AB4C BPL | |||||
| DEX ; Decrement FCB index | ||||||
| AB63 | BPL loop_scan_fcb_flags ; Loop for all 16 entries | |||||
| AB65 | PLA ; Restore original X | |||||
| AB66 | TAX ; Back to X | |||||
| AB67 | RTS ; Return | |||||
OSWORD &13 sub 8: read RX control block flagReturns byte 1 of the current RX control block in PB[1]. |
|
| AB68 | .osword_13_read_rx_flag |
| LDY #1 ; Y=1: PB[1] = RX flag location | |
| AB6A | LDA (net_rx_ptr),y ; Load (net_rx_ptr)+1 |
| AB6C | LDY #0 ; Y=0 |
| AB6E | 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]. |
|
| AB71 | .osword_13_read_rx_port |
| LDY #&7f ; Y=&7F: port byte offset | |
| AB73 | LDA (net_rx_ptr),y ; Load (net_rx_ptr)+&7F |
| AB75 | LDY #1 ; Y=1 |
| AB77 | STA (ws_ptr_hi),y ; Store to PB[1] |
| AB79 | INY ; Y=&02 |
| AB7A | LDA #&80 ; A=&80 |
| AB7C | STA (ws_ptr_hi),y ; Store &80 to PB[2] |
| AB7E | RTS ; Return |
OSWORD &13 sub 10: read error flagReturns the latched FS last-error byte
( |
|
| AB7F | .osword_13_read_error |
| LDA hazel_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.
|
|||||||
| AB82 | .store_a_to_pb_1←4← AAB5 JMP← AB6E JMP← AB89 BPL← AB91 BCS | ||||||
| INY ; Y=1: parameter block offset 1 | |||||||
| AB83 | STA (ws_ptr_hi),y ; Store result to PB[1] | ||||||
| AB85 | RTS ; Return | ||||||
OSWORD &13 sub 11: read context byteReturns the FS context/error code
( |
|
| AB86 | .osword_13_read_context |
| LDA hazel_fs_error_code ; Load context byte | |
| AB89 | 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. |
|
| AB8B | .osword_13_read_free_bufs |
| LDA #&6f ; Total buffers = &6F | |
| AB8D | SEC ; PB-to-WS direction (write) |
| AB8E | SBC spool_buf_idx ; Free = &6F - spool_buf_idx |
| AB91 | 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. |
|
| AB93 | .osword_13_read_ctx_3←1← AB9B BNE |
| INY ; Next ctx byte | |
| AB94 | LDA fs_flags,y ; Return |
| AB97 | STA (ws_ptr_hi),y ; Store to PB[Y] |
| AB99 | CPY #3 ; Done 3 bytes? |
| AB9B | BNE osword_13_read_ctx_3 ; No: loop |
| AB9D | 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. |
|
| AB9E | .osword_13_write_ctx_3←1← ABA6 BNE |
| INY ; Next byte offset | |
| AB9F | LDA (ws_ptr_hi),y ; Load PB[Y] |
| ABA1 | STA fs_flags,y ; Store to tx_retry_count[Y] |
| ABA4 | CPY #3 ; Done 3 bytes? |
| ABA6 | BNE osword_13_write_ctx_3 ; No: loop |
| ABA8 | RTS ; Return |
OSWORD &13 sub 17: query bridge statusCalls init_bridge_poll, then returns the bridge status. If bridge_status is &FF (no bridge), stores 0 in PB[0]. Otherwise stores bridge_status in PB[1] and conditionally updates PB[3] based on station comparison. |
|
| ABA9 | .osword_13_bridge_query |
| JSR init_bridge_poll ; Poll for bridge | |
| ABAC | LDY #0 ; Y=0 |
| ABAE | LDA spool_control_flag ; Load bridge status |
| ABB1 | CMP #&ff ; Is it &FF (no bridge)? |
| ABB3 | BNE bridge_found ; No: bridge found |
| ABB5 | TYA ; A=&00 |
| ABB6 | STA (ws_ptr_hi),y ; PB[0] = 0 (no bridge) |
| ABB8 | INY ; Y=&01 |
| ABB9 | BNE store_bridge_station ; ALWAYS branch |
| ABBB | .bridge_found←1← ABB3 BNE |
| INY ; Y=1 | |
| ABBC | STA (ws_ptr_hi),y ; PB[1] = bridge status |
| ABBE | INY ; Advance Y |
| ABBF | INY ; Y=3 |
| ABC0 | LDA (ws_ptr_hi),y ; Load PB[3] (caller value) |
| ABC2 | BEQ use_default_station ; Zero: use default station |
| ABC4 | .compare_bridge_status |
| EOR spool_control_flag ; Compare with bridge status | |
| ABC7 | BNE return_from_bridge_query ; Non-zero: take return path |
| ABC9 | BEQ store_bridge_station ; Same: confirm station |
| ABCB | .use_default_station←1← ABC2 BEQ |
| LDA hazel_fs_network ; Load default from fs_server_net | |
| ABCE | .store_bridge_station←2← ABB9 BNE← ABC9 BEQ |
| STA (ws_ptr_hi),y ; Store to PB[3] | |
| ABD0 | .return_from_bridge_query←1← ABC7 BNE |
| RTS ; Return | |
| ABD1 | .bridge_txcb_init_table←1← ABFE LDA |
| EQUB &82 ; TX 0: ctrl = &82 (immediate mode) | |
| ABD2 | EQUB &9C ; TX 1: port = &9C (bridge discovery) |
| ABD3 | EQUB &FF ; TX 2: dest station = &FF (broadcast) |
| ABD4 | EQUB &FF ; TX 3: dest network = &FF (all nets) |
| ABD5 | EQUS "BRIDGE" ; TX 4-9: immediate data payload |
| ABDB | EQUB &9C, &00 ; TX 11: &00 (terminator) |
| ABDD | .bridge_rxcb_init_data |
| EQUB &7F, &9C, ; RX 0: ctrl = &7F (receive) RX 1: port = &00, &00, ; &9C (bridge discovery) RX 2: station = &00 &71, &0D, ; (any) RX 3: network = &00 (any) RX 6: &FF, &FF, ; extended addr fill (&FF) RX 7: extended &73, &0D, ; addr fill (&FF) RX 9: buf end hi (&0D) -> &FF, &FF ; &0D74 | |
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.
|
||||
| ABE9 | .init_bridge_poll←5← 8E21 JSR← 9022 JSR← A3D1 JSR← AAA8 JSR← ABA9 JSR | |||
| LDA spool_control_flag ; Check bridge status | ||||
| ABEC | CMP #&ff ; Is it &FF (uninitialised)? | |||
| ABEE | BNE return_from_bridge_poll ; No: bridge already active, return | |||
| ABF0 | TYA ; Save Y | |||
| ABF1 | PHA ; Preserve Y on stack | |||
| ABF2 | LDY #&18 ; Y=&18: workspace offset for init | |||
| ABF4 | LDX #&0b ; X=&0B: 12 bytes to copy | |||
| ABF6 | ROR econet_flags ; Rotate econet_flags right (save flag) | |||
| ABF9 | .loop_copy_bridge_init←1← AC05 BPL | |||
| LDA bridge_err_table,y ; Load init data byte | ||||
| ABFC | STA (nfs_workspace),y ; Store to workspace | |||
| ABFE | LDA bridge_txcb_init_table,x ; Load TXCB template byte | |||
| AC01 | STA txcb_ctrl,x ; Store to TX control block | |||
| AC03 | INY ; Next workspace byte | |||
| AC04 | DEX ; Next template byte | |||
| AC05 | BPL loop_copy_bridge_init ; Loop for all 12 bytes | |||
| AC07 | STX spool_control_flag ; Store X (-1) as bridge counter | |||
| AC0A | ROL econet_flags ; Restore econet_flags flag | |||
| AC0D | .loop_wait_ws_status←2← AC10 BCC← AC35 BPL | |||
| ASL tx_complete_flag ; Shift ws_0d60 left (check status) | ||||
| AC10 | BCC loop_wait_ws_status ; C=0: status clear, retry | |||
| AC12 | LDA #&82 ; Control byte &82 for TX | |||
| AC14 | STA txcb_ctrl ; Set in TX control block | |||
| AC16 | LDA #&c0 ; Data block at &00C0 | |||
| AC18 | STA nmi_tx_block ; Set NMI TX block low | |||
| AC1A | LDA #0 ; High byte = 0 (page 0) | |||
| AC1C | STA nmi_tx_block_hi ; Set NMI TX block high | |||
| AC1E | JSR tx_begin ; Begin Econet transmission | |||
| AC21 | .loop_wait_tx_done←1← AC23 BMI | |||
| BIT txcb_ctrl ; Test TX control block bit 7 | ||||
| AC23 | BMI loop_wait_tx_done ; Negative: TX still in progress | |||
| AC25 | PHX ; Push X (saved across delay) | |||
| AC26 | LDA #osbyte_vsync ; A=&13: OSBYTE 'wait for VSYNC' | |||
| AC28 | JSR osbyte ; Wait for vertical sync | |||
| AC2B | PLX ; Restore caller's X | |||
| AC2C | LDY #&18 ; Y=&18: status-byte offset | |||
| AC2E | LDA (nfs_workspace),y ; Load bridge response | |||
| AC30 | BMI bridge_responded ; Negative: bridge responded | |||
| AC32 | JSR advance_x_by_8 ; Advance retry counter by 8 | |||
| AC35 | BPL loop_wait_ws_status ; Positive: retry poll loop | |||
| AC37 | .bridge_responded←1← AC30 BMI | |||
| LDA #&3f ; Set response to &3F (OK) | ||||
| AC39 | STA (nfs_workspace),y ; Store to workspace | |||
| AC3B | PLA ; Restore saved Y | |||
| AC3C | TAY ; Result byte to Y | |||
| AC3D | LDA spool_control_flag ; Load bridge status | |||
| AC40 | TAX ; X = bridge status | |||
| AC41 | EOR #&ff ; Invert (presence -> absence) | |||
| AC43 | BEQ return_from_bridge_poll ; Status was &FF: return (no bridge) | |||
| AC45 | TXA ; Return bridge station in A | |||
| AC46 | .return_from_bridge_poll←2← ABEE BNE← AC43 BEQ | |||
| RTS ; Return | ||||
OSWORD &14 handler: bridge poll / station statusTriages by
|
||||||
| AC47 | .osword_14_handler | |||||
| CMP #1 ; Compare sub-code with 1 | ||||||
| AC49 | BCS handle_tx_request ; Sub-code >= 1: handle TX request | |||||
| AC4B | PHA ; Save state | |||||
| AC4C | JSR ensure_fs_selected ; Ensure NFS is currently the selected FS | |||||
| AC4F | PLA ; Pop saved A from the stack frame | |||||
| AC50 | LDY #&23 ; Y=&23: workspace offset for params | |||||
| AC52 | JSR mask_owner_access ; Set owner access mask | |||||
| AC55 | .loop_copy_txcb_init←1← AC62 BNE | |||||
| LDA init_txcb,y ; Load TXCB init byte | ||||||
| AC58 | BNE store_txcb_init_byte ; Non-zero: use template value | |||||
| AC5A | LDA hazel_minus_1a,y ; Zero: use workspace default value | |||||
| AC5D | .store_txcb_init_byte←1← AC58 BNE | |||||
| STA (nfs_workspace),y ; Store to workspace | ||||||
| AC5F | DEY ; Next byte down | |||||
| AC60 | CPY #&17 ; Until Y reaches &17 | |||||
| AC62 | BNE loop_copy_txcb_init ; Loop for all bytes | |||||
| AC64 | INY ; Next byte | |||||
| AC65 | 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. |
|
| AC67 | .store_osword_pb_ptr |
| LDY #&1c ; Y=&1C: workspace offset for PB pointer | |
| AC69 | LDA ws_ptr_hi ; Load PB page number |
| AC6B | ADC #1 ; PB starts at next page boundary (+1) |
| AC6D | JSR store_ptr_at_ws_y ; Store PB start pointer at ws[&1C] |
| AC70 | LDY #1 ; Y=1: PB byte 1 (transfer length) |
| AC72 | LDA (ws_ptr_hi),y ; Load transfer length from PB |
| AC74 | LDY #&20 ; Y=&20: TXCB offset |
| AC76 | ADC ws_ptr_hi ; Add PB base for buffer end address |
| AC78 | JSR store_ptr_at_ws_y ; Store PB pointer to workspace |
| AC7B | LDY #2 ; Y=2: parameter offset |
| AC7D | LDA #&90 ; Control byte &90 |
| AC7F | STA need_release_tube ; Set escapable flag |
| AC81 | STA (ws_ptr_hi),y ; Store control byte to PB |
| AC83 | INY ; Y=&03 |
| AC84 | INY ; Y=&04 |
| AC85 | .loop_copy_ws_to_pb←1← AC8D BNE |
| LDA hazel_minus_2,y ; Load workspace data | |
| AC88 | STA (ws_ptr_hi),y ; Store to parameter block |
| AC8A | INY ; Next byte |
| AC8B | CPY #7 ; Until Y reaches 7 |
| AC8D | BNE loop_copy_ws_to_pb ; Loop for 3 bytes (Y=4,5,6) |
| AC8F | LDA nfs_workspace_hi ; Read nfs_workspace_hi |
| AC91 | STA net_tx_ptr_hi ; Store to net_tx_ptr_hi |
| AC93 | JSR enable_irq_and_poll ; Enable interrupts |
| AC96 | LDY #&20 ; Y=&20: workspace offset |
| AC98 | LDA #&ff ; Set to &FF (pending) |
| AC9A | STA (nfs_workspace),y ; Mark send pending in workspace |
| AC9C | INY ; Y=&21 |
| AC9D | STA (nfs_workspace),y ; Also mark offset &21 |
| AC9F | LDY #&19 ; Y=&19: control offset |
| ACA1 | LDA #&90 ; Control byte &90 |
| ACA3 | STA (nfs_workspace),y ; Store to workspace |
| ACA5 | DEY ; Y=&18: RX control offset |
| ACA6 | LDA #&7f ; Control byte &7F |
| ACA8 | STA (nfs_workspace),y ; Store RX control |
| ACAA | 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.
|
||||||||
| ACAD | .store_ptr_at_ws_y←2← AC6D JSR← AC78 JSR | |||||||
| STA (nfs_workspace),y ; Store address low byte at ws[Y] | ||||||||
| ACAF | INY ; Advance to high byte offset | |||||||
| ACB0 | LDA table_idx ; Load high byte base (table_idx) | |||||||
| ACB2 | ADC #0 ; Add carry for page crossing | |||||||
| ACB4 | STA (nfs_workspace),y ; Store address high byte at ws[Y+1] | |||||||
| ACB6 | RTS ; Return | |||||||
Sub-code 0: copy PB station/network into RX block, dispatch burstSub-code-0 path of
Wraps the body in
|
|||||||||||||||||||||
| ACB7 | .handle_tx_request←1← AC49 BCS | ||||||||||||||||||||
| PHP ; Save processor flags | |||||||||||||||||||||
| ACB8 | LDY #1 ; Y=1: workspace offset | ||||||||||||||||||||
| ACBA | LDA (ws_ptr_hi),y ; Load station number from PB | ||||||||||||||||||||
| ACBC | TAX ; X = station number | ||||||||||||||||||||
| ACBD | INY ; Y=&02 | ||||||||||||||||||||
| ACBE | LDA (ws_ptr_hi),y ; Load network number from PB | ||||||||||||||||||||
| ACC0 | INY ; Y=3: workspace start offset | ||||||||||||||||||||
| ACC1 | STY osword_flag ; Store Y as ws_ptr_lo | ||||||||||||||||||||
| ACC3 | LDY #&72 ; Y=&72: workspace offset for dest | ||||||||||||||||||||
| ACC5 | STA (net_rx_ptr),y ; Store network to workspace | ||||||||||||||||||||
| ACC7 | DEY ; Y=&71 | ||||||||||||||||||||
| ACC8 | TXA ; A = station (from X) | ||||||||||||||||||||
| ACC9 | STA (net_rx_ptr),y ; Store station to workspace | ||||||||||||||||||||
| ACCB | PLP ; Restore flags from PHP | ||||||||||||||||||||
| ACCC | BNE handle_burst_xfer ; Non-zero sub-code: handle burst | ||||||||||||||||||||
| ACCE | .loop_send_pb_chars←1← ACEA BNE | ||||||||||||||||||||
| LDY osword_flag ; Load current offset | |||||||||||||||||||||
| ACD0 | INC osword_flag ; Advance offset for next byte | ||||||||||||||||||||
| ACD2 | LDA (ws_ptr_hi),y ; Load next char from PB | ||||||||||||||||||||
| ACD4 | BEQ return_5 ; Zero: end of data, return | ||||||||||||||||||||
| ACD6 | LDY #&7d ; Y=&7D: workspace pointer offset | ||||||||||||||||||||
| ACD8 | STA (net_rx_ptr),y ; Store char to RX buffer | ||||||||||||||||||||
| ACDA | PHA ; Save char for later test | ||||||||||||||||||||
| ACDB | JSR init_ws_copy_wide ; Init workspace copy for wide xfer | ||||||||||||||||||||
| ACDE | SEC ; Set carry | ||||||||||||||||||||
| ACDF | ROR need_release_tube ; Set bit 7: Tube needs release | ||||||||||||||||||||
| ACE1 | JSR enable_irq_and_poll ; Enable IRQ and send packet | ||||||||||||||||||||
| ACE4 | .loop_bridge_tx_delay←1← ACE5 BNE | ||||||||||||||||||||
| DEX ; Delay countdown | |||||||||||||||||||||
| ACE5 | BNE loop_bridge_tx_delay ; Loop while X != 0 | ||||||||||||||||||||
| ACE7 | PLA ; Restore char | ||||||||||||||||||||
| ACE8 | EOR #&0d ; Test if char was CR (&0D) | ||||||||||||||||||||
| ACEA | BNE loop_send_pb_chars ; Loop while not CR | ||||||||||||||||||||
| ACEC | .return_5←1← ACD4 BEQ | ||||||||||||||||||||
| RTS ; CR sent: return | |||||||||||||||||||||
OSWORD &14 burst-transfer path: extend buffer end and TXReached from
|
||||
| ACED | .handle_burst_xfer←1← ACCC BNE | |||
| JSR init_ws_copy_wide ; Init workspace for wide copy | ||||
| ACF0 | LDY #&7b ; Y=&7B: end-byte offset | |||
| ACF2 | LDA (net_rx_ptr),y ; Load buffer size | |||
| ACF4 | ADC #3 ; Add 3 (end-of-buffer adjust) | |||
| ACF6 | 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.
|
|||||||
| ACF8 | .enable_irq_and_poll←2← AC93 JSR← ACE1 JSR | ||||||
| CLI ; Re-enable IRQs | |||||||
| ACF9 | JMP send_net_packet ; Send packet and return | ||||||
NETV handler: OSWORD dispatchInstalled as the NETV handler via
|
|||||||||
| ACFC | .netv_handler | ||||||||
| PHP ; Save processor flags | |||||||||
| ACFD | PHA ; Save A | ||||||||
| ACFE | TXA ; Save X | ||||||||
| ACFF | PHA ; Push X | ||||||||
| AD00 | TYA ; Save Y | ||||||||
| AD01 | PHA ; Push Y | ||||||||
| AD02 | TSX ; Get stack pointer | ||||||||
| AD03 | LDA stack_page_3,x ; Read OSWORD number from stack | ||||||||
| AD06 | CMP #9 ; OSWORD >= 9? | ||||||||
| AD08 | BCS restore_regs_return ; Yes: out of range, restore + return | ||||||||
| AD0A | TAX ; X = OSWORD number | ||||||||
| AD0B | JSR push_osword_handler_addr ; Push handler address for dispatch | ||||||||
| AD0E | .restore_regs_return←1← AD08 BCS | ||||||||
| PLA ; Restore Y | |||||||||
| AD0F | TAY ; Back to Y | ||||||||
| AD10 | PLA ; Restore X | ||||||||
| AD11 | TAX ; Back to X | ||||||||
| AD12 | PLA ; Restore A | ||||||||
| AD13 | PLP ; Restore flags | ||||||||
| AD14 | RTS ; Return | ||||||||
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.
|
|||||||
| AD15 | .push_osword_handler_addr←1← AD0B JSR | ||||||
| LDA netv_dispatch_hi,x ; Load handler high byte from hi-table column X | |||||||
| AD18 | PHA ; Push for the eventual RTS dispatch | ||||||
| AD19 | LDA netv_dispatch_lo,x ; Load handler low byte from lo-table column X | ||||||
| AD1C | PHA ; Push lo so RTS pulls (lo, hi)+1 -> handler entry | ||||||
| AD1D | LDA osbyte_a_copy ; Reload original OSWORD number into A for the handler Reload OSWORD number for handler | ||||||
| AD1F | RTS ; RTS jumps to handler with A=OSWORD number | ||||||
NETV reason-code dispatch low-byte table (9 entries)Read by |
|
| AD20 | .netv_dispatch_lo←1← AD19 LDA |
| EQUB <(dispatch_rts-1) ; reason &00: dispatch_rts (no-op (RTS only)) | |
| AD21 | EQUB <(netv_print_data-1) ; reason &01: netv_print_data (NETV reason 1: print data) |
| AD22 | EQUB <(netv_print_data-1) ; reason &02: netv_print_data (NETV reason 2: print data (alias)) |
| AD23 | EQUB <(netv_print_data-1) ; reason &03: netv_print_data (NETV reason 3: print data (alias)) |
| AD24 | EQUB <(osword_4_handler-1) ; reason &04: osword_4_handler (NETV reason 4: OSWORD &04) |
| AD25 | EQUB <(netv_spool_check-1) ; reason &05: netv_spool_check (NETV reason 5: spool check) |
| AD26 | EQUB <(dispatch_rts-1) ; reason &06: dispatch_rts (no-op (RTS only)) |
| AD27 | EQUB <(netv_claim_release-1) ; reason &07: netv_claim_release (NETV reason 7: claim/release) |
| AD28 | EQUB <(osword_8_handler-1) ; reason &08: osword_8_handler (NETV reason 8: OSWORD &08) |
NETV reason-code dispatch high-byte table (9 entries)Read by |
|
| AD29 | .netv_dispatch_hi←1← AD15 LDA |
| EQUB >(dispatch_rts-1) ; reason &00: dispatch_rts | |
| AD2A | EQUB >(netv_print_data-1) ; reason &01: netv_print_data |
| AD2B | EQUB >(netv_print_data-1) ; reason &02: netv_print_data |
| AD2C | EQUB >(netv_print_data-1) ; reason &03: netv_print_data |
| AD2D | EQUB >(osword_4_handler-1) ; reason &04: osword_4_handler |
| AD2E | EQUB >(netv_spool_check-1) ; reason &05: netv_spool_check |
| AD2F | EQUB >(dispatch_rts-1) ; reason &06: dispatch_rts hi OSWORD 6: no-op (RTS) |
| AD30 | EQUB >(netv_claim_release-1) ; reason &07: netv_claim_release |
| AD31 | EQUB >(osword_8_handler-1) ; reason &08: osword_8_handler |
OSWORD &04 handler: clear C, send abortReaches the stack via
|
||||
| AD32 | .osword_4_handler | |||
| TSX ; Read the MOS stack frame holding caller flags | ||||
| AD33 | ROR stack_page_6,x ; Shift carry out of caller P (stack[&106+X]) | |||
| AD36 | ASL stack_page_6,x ; Carry is now cleared in caller P | |||
| AD39 | TYA ; A = original Y | |||
| AD3A | LDY #&da ; Y=&DA: workspace osword-4 result offset | |||
| AD3C | STA (nfs_workspace),y ; Store Y at (nfs_workspace)+&DA | |||
| AD3E | LDA #0 ; A=0: clear A for the abort path | |||
| 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.
|
||||
| AD40 | .tx_econet_abort←3← 8AFD JSR← AD93 JSR← ADF9 JSR | |||
| LDY #&d9 ; Y=&D9: workspace offset for the abort code byte Y=&D9: workspace abort offset | ||||
| AD42 | STA (nfs_workspace),y ; Store the abort code (passed in A) at workspace[&D9] Store abort code to workspace | |||
| AD44 | LDA #&80 ; A=&80: control = immediate-operation flag | |||
| AD46 | LDY #&0c ; Y=&0C: TXCB control-byte offset | |||
| AD48 | STA (nfs_workspace),y ; Set TXCB[&0C] = &80 (immediate / abort) | |||
| AD4A | LDA net_tx_ptr ; Save current net_tx_ptr low (we'll repoint TX at the abort packet) Save current TX ptr low | |||
| AD4C | PHA ; Push it for restore on exit | |||
| AD4D | LDA net_tx_ptr_hi ; Save net_tx_ptr high too | |||
| AD4F | PHA ; Push it | |||
| AD50 | STY net_tx_ptr ; TX low = &0C (abort packet starts at workspace[&0C]) Set TX ptr to workspace offset | |||
| AD52 | LDX nfs_workspace_hi ; Get nfs_workspace high byte | |||
| AD54 | STX net_tx_ptr_hi ; TX high = workspace page (so net_tx_ptr now points at the abort packet in workspace) Set TX ptr high | |||
| AD56 | JSR send_net_packet ; Send the abort packet via the standard TX path Send the abort packet | |||
| AD59 | LDA #&3f ; A=&3F: TXCB status = abort-complete sentinel Set status to &3F (complete) | |||
| AD5B | STA (net_tx_ptr,x) ; Write status via (net_tx_ptr,X) -- mark TX done Store at TX ptr offset 0 | |||
| AD5D | PLA ; Pull saved net_tx_ptr high | |||
| AD5E | STA net_tx_ptr_hi ; Restore | |||
| AD60 | PLA ; Pull saved net_tx_ptr low | |||
| AD61 | STA net_tx_ptr ; Restore -- caller's TX state intact | |||
| AD63 | 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.
|
||||
| AD64 | .netv_claim_release | |||
| LDY osword_pb_ptr_hi ; Y = OSWORD parameter-block pointer high byte (used as an 'unrecognised' sentinel below) Load PB pointer high | ||||
| AD66 | CMP #&81 ; Code &81? (compatibility shortcut for one specific claim type) Compare with &81 (special case) | |||
| AD68 | BEQ process_match_result ; Yes: skip table scan, use match-result with Y already set non-zero Match: skip to processing | |||
| AD6A | LDY #1 ; Y=1: state 2 marker | |||
| AD6C | LDX #&0a ; X=&0A: scan first 11 entries (table indices 0..&0A) X=&0A: 11 codes to check | |||
| AD6E | JSR match_rx_code ; Look up A in the claim code table | |||
| AD71 | BEQ process_match_result ; Match: handle as state 2 | |||
| AD73 | DEY ; DEY: Y=0 (state 3 marker, two DEYs from 1) | |||
| AD74 | DEY ; Y=-1: flag second range | |||
| AD75 | LDX #&11 ; X=&11: scan all 18 entries (state 3 also accepts the extended range) X=&11: 18 codes to check | |||
| AD77 | JSR match_rx_code ; Look up A again with extended range | |||
| AD7A | BEQ process_match_result ; Match: handle as state 3 | |||
| AD7C | INY ; Y=1 again (no match found, will return below) Not found: increment Y | |||
| AD7D | .process_match_result←3← AD68 BEQ← AD71 BEQ← AD7A BEQ | |||
| LDX #2 ; X=2: default state code passed to tx_econet_abort X=2: default state | ||||
| AD7F | TYA ; Move match marker (Y) into A for the BEQ test A = Y (search result) | |||
| AD80 | BEQ return_from_claim_release ; Y=0 (no match): return without action | |||
| AD82 | PHP ; Save flags so we can branch later on Y's sign Save result flags | |||
| AD83 | BPL save_tube_state ; Y > 0 (state 2): skip the X bump | |||
| AD85 | INX ; State 3: X=3 (different abort code) | |||
| AD86 | .save_tube_state←1← AD83 BPL | |||
| LDY #&dc ; Y=&DC: workspace offset for tube state bytes Y=&DC: workspace offset for save | ||||
| AD88 | .loop_save_tube_bytes←1← AD90 BPL | |||
| LDA tube_claimed_id,y ; Read tube_claimed_id,Y | ||||
| AD8B | STA (nfs_workspace),y ; Save in workspace[&DC..] | |||
| AD8D | DEY ; Step backwards | |||
| AD8E | CPY #&da ; Done at &DA? | |||
| AD90 | BPL loop_save_tube_bytes ; Loop while Y > &DA (saves &DA, &DB, &DC -- 3 bytes) Loop for 3 bytes | |||
| AD92 | TXA ; Move state code (2 or 3) into A for the abort A = state (2 or 3) | |||
| AD93 | JSR tx_econet_abort ; Send abort with the state code | |||
| AD96 | PLP ; Restore the saved flags (Y's sign) | |||
| AD97 | BPL return_from_claim_release ; Y was positive (state 2): just return | |||
| AD99 | LDA #&7f ; A=&7F: 'pending response' control value | |||
| AD9B | LDY #&0c ; Y=&0C: TXCB control offset | |||
| AD9D | STA (nfs_workspace),y ; Mark TXCB as pending | |||
| AD9F | .loop_poll_ws_status←1← ADA1 BPL | |||
| LDA (nfs_workspace),y ; Read TXCB status byte | ||||
| ADA1 | BPL loop_poll_ws_status ; Bit 7 still clear: keep polling for response Positive: keep waiting | |||
| ADA3 | TSX ; Capture S so we can patch the caller's stack frame Get stack pointer | |||
| ADA4 | LDY #&dd ; Y=&DD: highest workspace offset for the response copy Y=&DD: workspace result offset | |||
| ADA6 | LDA (nfs_workspace),y ; Read first response byte (workspace[&DD]) | |||
| ADA8 | ORA #&44 ; Set bit 6 and bit 2 | |||
| ADAA | BNE store_stack_byte ; Always taken (after ORA result is non-zero); store into stack[&106+X] then walk down Always branch (NZ from ORA) | |||
| ADAC | .loop_restore_stack←1← ADB5 BNE | |||
| DEY ; Step Y down | ||||
| ADAD | DEX ; Step X down (stack offset) | |||
| ADAE | LDA (nfs_workspace),y ; Read next workspace byte | |||
| ADB0 | .store_stack_byte←1← ADAA BNE | |||
| STA stack_page_6,x ; Patch caller's stack frame at &106+X | ||||
| ADB3 | CPY #&da ; Reached &DA (lower workspace bound)? | |||
| ADB5 | BNE loop_restore_stack ; No: keep restoring | |||
| ADB7 | .return_from_claim_release←2← AD80 BEQ← AD97 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.
|
|||||||||
| ADB8 | .match_rx_code←3← AD6E JSR← AD77 JSR← ADBE BPL | ||||||||
| CMP osword_claim_codes,x ; Compare A with table entry at index X | |||||||||
| ADBB | BEQ return_from_match_rx_code ; Match: return with Z set | ||||||||
| ADBD | DEX ; Step to next earlier table entry | ||||||||
| ADBE | BPL match_rx_code ; Loop while X >= 0 (table walked top-down) | ||||||||
| ADC0 | .return_from_match_rx_code←2← ADBB BEQ← ADDB BNE | ||||||||
| RTS ; Return; Z reflects last CMP | |||||||||
OSWORD per-claim-code lookup table (18 bytes)Looked up by |
|
| ADC1 | .osword_claim_codes←1← ADB8 CMP |
| EQUB &04 ; Range 1+2: OSWORD &04 | |
| ADC2 | EQUB &09 ; Range 1+2: OSWORD &09 |
| ADC3 | EQUB &0A |
| ADC4 | EQUB &14 ; Range 1+2: OSWORD &14 |
| ADC5 | EQUB &15 ; Range 1+2: OSWORD &15 |
| ADC6 | EQUB &9A |
| ADC7 | EQUB &9B ; Range 1+2: OSWORD &9B |
| ADC8 | EQUB &E1 ; Range 1+2: OSWORD &E1 |
| ADC9 | EQUB &E2 |
| ADCA | EQUB &E3 ; Range 1+2: OSWORD &E3 |
| ADCB | EQUB &E4 ; Range 1+2: OSWORD &E4 |
| ADCC | EQUB &0B |
| ADCD | EQUB &0C ; Range 2 only: OSWORD &0C |
| ADCE | EQUB &0F ; Range 2 only: OSWORD &0F |
| ADCF | EQUB &79 ; Range 2 only: OSWORD &79 |
| ADD0 | EQUB &7A |
| ADD1 | EQUB &86 |
| ADD2 | 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.
|
||||
| ADD3 | .osword_8_handler | |||
| LDY #&0e ; Y=&0E: scan 15 bytes (offsets 14..0) of the PB Y=&0E: copy 15 bytes (0-14) | ||||
| ADD5 | CMP #7 ; Is the OSWORD number 7? | |||
| ADD7 | BEQ copy_pb_to_ws ; Yes: handle as either 7 or 8 -- both copy PB to ws Yes: handle | |||
| ADD9 | CMP #8 ; Is the OSWORD number 8? | |||
| ADDB | BNE return_from_match_rx_code ; Neither 7 nor 8: return early (other OSWORDs handled elsewhere) No: return | |||
| ADDD | .copy_pb_to_ws←1← ADD7 BEQ | |||
| LDX #&db ; X=&DB: workspace offset for the PB copy Workspace low = &DB | ||||
| ADDF | STX nfs_workspace ; Temporarily reuse nfs_workspace as the destination low byte (high byte already points at the workspace page) Set nfs_workspace low byte | |||
| ADE1 | .loop_copy_pb_to_ws←1← ADE6 BPL | |||
| LDA (osword_pb_ptr),y ; Read PB[Y] | ||||
| ADE3 | STA (nfs_workspace),y ; Write to (nfs_workspace),Y -- effectively writes to workspace[&DB+Y] Store to workspace[Y] | |||
| ADE5 | DEY ; Step backwards through the 15 bytes | |||
| ADE6 | BPL loop_copy_pb_to_ws ; Loop while Y >= 0 | |||
| ADE8 | INY ; Bring Y back to 0 for the next single-byte write Y=0 | |||
| ADE9 | DEC nfs_workspace ; Decrement nfs_workspace low byte: now points at workspace[&DA] (one before the copied region) Workspace low = &DA | |||
| ADEB | LDA osbyte_a_copy ; Read original OSWORD number from osbyte_a_copy Load OSWORD number | |||
| ADED | STA (nfs_workspace),y ; Store at workspace[&DA] -- so the abort packet header carries the OSWORD number Store at workspace+0 (= &DA) | |||
| ADEF | STY nfs_workspace ; Restore nfs_workspace to its proper low byte (Y=0) Workspace low = 0 (restore) | |||
| ADF1 | LDY #&14 ; Y=&14: TXCB control offset | |||
| ADF3 | LDA #&e9 ; A=&E9: status code for OSWORD-passthrough abort Control value &E9 | |||
| ADF5 | STA (nfs_workspace),y ; Store status at TXCB[&14] | |||
| ADF7 | LDA #1 ; A=1: abort code for tx_econet_abort | |||
| ADF9 | JSR tx_econet_abort ; Send the abort packet | |||
| ADFC | STX nfs_workspace ; Restore nfs_workspace from X (X is unchanged across tx_econet_abort) 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.
|
||||
| ADFE | .init_ws_copy_wide←2← ACDB JSR← ACED JSR | |||
| LDX #&0d ; X=&0D: 14 template bytes to process | ||||
| AE00 | LDY #&7c ; Y=&7C: workspace destination offset for wide variant Y=&7C: workspace destination offset | |||
| AE02 | BIT always_set_v_byte ; BIT &FF unconditionally sets V (the always_set_v_byte trick) Test bit 6 (V flag check) | |||
| AE05 | BVS loop_copy_ws_template ; V=1 always: skip the narrow-mode prologue and CLV 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.
|
||||
| AE07 | .init_ws_copy_narrow←1← 9872 JSR | |||
| LDY #&17 ; Y=&17: workspace destination offset for narrow variant Y=&17: narrow mode dest offset | ||||
| AE09 | LDX #&1a ; X=&1A: 27 template bytes to process; fall into ws_copy_vclr_entry which CLVs 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.
|
|||||||||||
| AE0B | .ws_copy_vclr_entry←1← AECC JSR | ||||||||||
| CLV ; Clear V: narrow mode (writes via nfs_workspace pointer) Clear V flag for narrow mode | |||||||||||
| AE0C | .loop_copy_ws_template←2← AE05 BVS← AE2D BPL | ||||||||||
| LDA ws_txcb_template_data,x ; Read next template byte | |||||||||||
| AE0F | CMP #&fe ; &FE: end-of-template marker? | ||||||||||
| AE11 | BEQ done_ws_template_copy ; Yes: finalise and return | ||||||||||
| AE13 | CMP #&fd ; &FD: skip-this-offset marker? | ||||||||||
| AE15 | BEQ advance_template_idx ; Yes: advance index without storing | ||||||||||
| AE17 | CMP #&fc ; &FC: substitute-workspace-page- pointer marker? Is it &FC? (page ptr marker) | ||||||||||
| AE19 | BNE select_store_target ; No special marker: store this byte verbatim | ||||||||||
| AE1B | LDA net_rx_ptr_hi ; Wide path: page pointer is net_rx_ptr's high byte &FC: load RX buffer page | ||||||||||
| AE1D | BVS store_tx_ptr_hi ; V=1 (wide): keep the rx_ptr high byte | ||||||||||
| AE1F | LDA nfs_workspace_hi ; V=0 (narrow): use nfs_workspace high byte instead V=0: use nfs_workspace_hi | ||||||||||
| AE21 | .store_tx_ptr_hi←1← AE1D BVS | ||||||||||
| STA net_tx_ptr_hi ; Stash whichever page byte we picked into net_tx_ptr_hi Store as TX ptr high | |||||||||||
| AE23 | .select_store_target←1← AE19 BNE | ||||||||||
| BVS store_via_rx_ptr ; V=1 (wide): store via net_rx_ptr,Y | |||||||||||
| AE25 | STA (nfs_workspace),y ; V=0 (narrow): store via nfs_workspace,Y | ||||||||||
| AE27 | BVC advance_template_idx ; Always branch: V is still clear here | ||||||||||
| AE29 | .store_via_rx_ptr←1← AE23 BVS | ||||||||||
| STA (net_rx_ptr),y ; Wide-mode store via net_rx_ptr | |||||||||||
| AE2B | .advance_template_idx←2← AE15 BEQ← AE27 BVC | ||||||||||
| DEY ; Step Y down (workspace offset) | |||||||||||
| AE2C | DEX ; Step X down (template index) | ||||||||||
| AE2D | BPL loop_copy_ws_template ; Loop while X >= 0 | ||||||||||
| AE2F | .done_ws_template_copy←1← AE11 BEQ | ||||||||||
| INY ; Bump Y back to first written offset | |||||||||||
| AE30 | STY net_tx_ptr ; Save it as net_tx_ptr low for the caller | ||||||||||
| AE32 | RTS ; Return | ||||||||||
Workspace TXCB template (39 bytes, three overlapping regions)Three overlapping copy regions indexed by different callers:
Per-byte inline comments below describe each entry's role in the TXCB it ends up in. |
|||||||||||||||||
| AE33 | .ws_txcb_template_data←1← AE0C LDA | ||||||||||||||||
| EQUB &85 ; Wide &6F: ctrl=&85 | |||||||||||||||||
| AE34 | EQUB &00 | ||||||||||||||||
| AE35 | EQUB &FD ; Wide &71: skip (dest station) | ||||||||||||||||
| AE36 | EQUB &FD | ||||||||||||||||
| AE37 | EQUB &7D | ||||||||||||||||
| AE38 | EQUB &FC ; Wide &74: buf start hi=page ptr | ||||||||||||||||
| AE39 | EQUB &FF ; Wide &75: buf start ext lo | ||||||||||||||||
| AE3A | EQUB &FF ; Wide &76: buf start ext hi | ||||||||||||||||
| AE3B | EQUB &7E ; Wide &77: buf end lo=&7E | ||||||||||||||||
| AE3C | EQUB &FC | ||||||||||||||||
| AE3D | EQUB &FF | ||||||||||||||||
| AE3E | EQUB &FF ; Wide &7A: buf end ext hi | ||||||||||||||||
| AE3F | EQUB &00 ; Wide &7B: zero | ||||||||||||||||
| AE40 | EQUB &00 ; Wide &7C: zero | ||||||||||||||||
| AE41 | EQUB &FE ; Narrow stop (&FE terminator) | ||||||||||||||||
| AE42 | EQUB &80 | ||||||||||||||||
| AE43 | EQUB &93 | ||||||||||||||||
| AE44 | EQUB &FD ; Narrow &0E: skip (dest station) | ||||||||||||||||
| AE45 | EQUB &FD | ||||||||||||||||
| AE46 | EQUB &D9 | ||||||||||||||||
| AE47 | EQUB &FC ; Narrow &11: buf start hi=page ptr | ||||||||||||||||
| AE48 | EQUB &FF ; Narrow &12: buf start ext lo | ||||||||||||||||
| AE49 | EQUB &FF ; Narrow &13: buf start ext hi | ||||||||||||||||
| AE4A | EQUB &DE ; Narrow &14: buf end lo=&DE | ||||||||||||||||
| AE4B | EQUB &FC | ||||||||||||||||
| AE4C | EQUB &FF | ||||||||||||||||
| AE4D | EQUB &FF ; Narrow &17: buf end ext hi | ||||||||||||||||
| AE4E | EQUB &FE ; Spool stop (&FE terminator) | ||||||||||||||||
| AE4F | EQUB &D1 | ||||||||||||||||
| AE50 | EQUB &FD | ||||||||||||||||
| AE51 | EQUB &FD ; Spool &03: skip (dest network) | ||||||||||||||||
| AE52 | EQUB &21 | ||||||||||||||||
| AE53 | EQUB &FD | ||||||||||||||||
| AE54 | EQUB &FF ; Spool &06: buf start ext lo | ||||||||||||||||
| AE55 | EQUB &FF ; Spool &07: buf start ext hi | ||||||||||||||||
| AE56 | EQUB &FD ; Spool &08: skip (buf end lo) | ||||||||||||||||
| AE57 | EQUB &FD | ||||||||||||||||
| AE58 | EQUB &FF | ||||||||||||||||
| AE59 | 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.
|
||||
| AE5A | .netv_spool_check | |||
| DEX ; Step counter | ||||
| AE5B | CPX osword_pb_ptr ; Match osword_pb_ptr? | |||
| AE5D | BNE return_from_spool_reset ; No: return (not our PB) | |||
| AE5F | LDA vdu_status ; Load spool state byte | |||
| AE61 | ROR ; Shift bit 0 into C | |||
| AE62 | BCS return_from_spool_reset ; C=1: already active, return | |||
| fall through ↓ | ||||
Reset spool buffer to initial stateSets the spool buffer pointer (
|
||||
| AE64 | .reset_spool_buf_state←2← 8F93 JSR← AEB5 JMP | |||
| LDA #&21 ; Buffer start offset = &21 | ||||
| AE66 | STA spool_buf_idx ; Store as buffer pointer | |||
| AE69 | LDA #&41 ; Control state &41 | |||
| AE6B | STA ws_0d6a ; Store as spool control state | |||
| AE6E | .return_from_spool_reset←4← AE5D BNE← AE62 BCS← AE71 BNE← AE85 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.
|
||||
| AE6F | .netv_print_data | |||
| CPY #4 ; Check Y == 4 | ||||
| AE71 | BNE return_from_spool_reset ; Non-zero: nothing to print, return | |||
| AE73 | TXA ; A = X (control byte) | |||
| AE74 | DEX ; Step counter back | |||
| AE75 | BNE handle_spool_ctrl_byte ; Non-zero: handle spool ctrl byte | |||
| AE77 | TSX ; Read MOS stack frame | |||
| AE78 | ORA stack_page_6,x ; OR with stack value | |||
| AE7B | STA stack_page_6,x ; Store back to stack | |||
| AE7E | .loop_drain_printer_buf←2← AE8D BCC← AE92 BCC | |||
| LDA #osbyte_read_buffer ; OSBYTE &91: read buffer | ||||
| AE80 | LDX #buffer_printer ; X=3: printer buffer | |||
| AE82 | JSR osbyte ; Read character from buffer | |||
| AE85 | BCS return_from_spool_reset ; C set: return path | |||
| AE87 | TYA ; A = extracted character | |||
| AE88 | JSR append_byte_to_rxbuf ; Add byte to RX buffer | |||
| AE8B | CPY #&6e ; Buffer past &6E limit? | |||
| AE8D | BCC loop_drain_printer_buf ; No: read more from buffer | |||
| AE8F | JSR process_spool_data ; Print accumulated spool data | |||
| AE92 | 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.
|
||||
| AE94 | .append_byte_to_rxbuf←3← AE88 JSR← AEAF JSR← AF66 JSR | |||
| LDY spool_buf_idx ; Y = spool_buf_idx | ||||
| AE97 | STA (net_rx_ptr),y ; Store A at (net_rx_ptr)+Y | |||
| AE99 | INC spool_buf_idx ; Advance spool_buf_idx | |||
| AE9C | 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.
|
|||||||
| AE9D | .handle_spool_ctrl_byte←2← 901F JSR← AE75 BNE | ||||||
| ROR ; Rotate bit 0 into carry | |||||||
| AE9E | BCC check_spool_state ; C clear: take check_spool_state path | ||||||
| AEA0 | LDA ws_0d6a ; Load spool control state Equal: take fill path | ||||||
| AEA3 | PHA ; Save state byte | ||||||
| AEA4 | ROR ; Rotate bit 0 into carry | ||||||
| AEA5 | PLA ; Restore state | ||||||
| AEA6 | BCS done_spool_ctrl ; C=1: already started, reset | ||||||
| AEA8 | ORA #3 ; Set bits 0-1 (active + pending) | ||||||
| AEAA | STA ws_0d6a ; Store updated state Stop: process_spool_data and return | ||||||
| AEAD | LDA #3 ; A=3: spool-data result code | ||||||
| AEAF | JSR append_byte_to_rxbuf ; Append result to RX buffer | ||||||
| AEB2 | JSR process_spool_data ; Process the accumulated spool data | ||||||
| AEB5 | .done_spool_ctrl←1← AEA6 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.
|
||||
| AEB8 | .process_spool_data←4← AE8F JSR← AEB2 JSR← AEFB BCC← AF69 JSR | |||
| LDY #8 ; Y=8: buf_start_lo TXCB offset | ||||
| AEBA | LDA spool_buf_idx ; Load current spool-buffer index | |||
| AEBD | STA (nfs_workspace),y ; Store at workspace+8 (buf_start_lo) | |||
| AEBF | LDA net_rx_ptr_hi ; Load RX page (= net_rx_ptr_hi) | |||
| AEC1 | INY ; Y=&09 | |||
| AEC2 | STA (nfs_workspace),y ; Store at workspace+9 (buf_start_hi) | |||
| AEC4 | LDY #5 ; Y=5: alt buf_start_hi offset | |||
| AEC6 | STA (nfs_workspace),y ; Store at workspace+5 (also buf-start hi) | |||
| AEC8 | LDY #&0b ; Y=&0B: TXCB offset for following copy | |||
| AECA | LDX #&26 ; X=&26: template offset for vclr region | |||
| AECC | JSR ws_copy_vclr_entry ; Copy 12-byte ws-template region (V-clear) | |||
| AECF | DEY ; Step back to offset &0A | |||
| AED0 | LDA ws_0d6a ; Read shadow ACR (ws_0d6a) | |||
| AED3 | PHA ; Save state | |||
| AED4 | ROL ; Shift bit 7 into C | |||
| AED5 | PLA ; Restore state | |||
| AED6 | EOR #&80 ; Toggle bit 7 | |||
| AED8 | STA ws_0d6a ; Store updated shadow back to ws_0d6a | |||
| AEDB | ROL ; Shift bit 0 into bit 1 | |||
| AEDC | STA (nfs_workspace),y ; Store at workspace+&0A | |||
| AEDE | LDA vdu_status ; Read vdu_status | |||
| AEE0 | PHA ; Push for later restore | |||
| AEE1 | AND #&fe ; Clear bit 0 of vdu_status | |||
| AEE3 | STA vdu_status ; Store updated | |||
| AEE5 | LDY #&21 ; Y=&21: spool_buf_idx reset value | |||
| AEE7 | STY spool_buf_idx ; Reset spool_buf_idx | |||
| AEEA | LDA #0 ; A=0 | |||
| AEEC | TAX ; X=0 | |||
| AEED | LDY nfs_workspace_hi ; Y = workspace high page | |||
| AEEF | CLI ; Re-enable IRQs (NMI window over) | |||
| AEF0 | JSR send_disconnect_reply ; Send disconnect reply | |||
| AEF3 | PLA ; Restore vdu_status | |||
| AEF4 | STA vdu_status ; Restore vdu_status | |||
| AEF6 | RTS ; Return | |||
| AEF7 | .check_spool_state←1← AE9E BCC | |||
| LDA ws_0d6a ; Read shadow ACR | ||||
| AEFA | ROR ; Shift bit 0 into C | |||
| AEFB | BCC process_spool_data ; C clear: re-process spool data | |||
| AEFD | LDA vdu_status ; Read vdu_status | |||
| AEFF | PHA ; Push for restore | |||
| AF00 | AND #&fe ; Clear bit 0 of vdu_status | |||
| AF02 | STA vdu_status ; Store updated | |||
| AF04 | LDA #&14 ; A=&14: TX command byte | |||
| AF06 | .start_spool_retry←1← AF7A BNE | |||
| PHA ; Save TX command | ||||
| AF07 | LDX #&0b ; X=&0B: tx_econet_txcb_template offset | |||
| AF09 | LDY #&2c ; Y=&2C: dest TXCB offset | |||
| AF0B | .loop_copy_spool_tx←1← AF12 BPL | |||
| LDA tx_econet_txcb_template,x ; Read template byte at tx_econet_txcb_template+X | ||||
| AF0E | STA (net_rx_ptr),y ; Store at (net_rx_ptr)+Y | |||
| AF10 | DEY ; Decrement Y | |||
| AF11 | DEX ; Decrement X | |||
| AF12 | BPL loop_copy_spool_tx ; Loop until X wraps below 0 | |||
| AF14 | STX need_release_tube ; Store X (= &FF) as need_release_tube | |||
| AF16 | LDY #2 ; Y=2: workspace offset for source | |||
| AF18 | LDA (nfs_workspace),y ; Read (nfs_workspace)+2 | |||
| AF1A | PHA ; Save station | |||
| AF1B | INY ; Y=3 | |||
| AF1C | LDA (nfs_workspace),y ; Read (nfs_workspace)+3 | |||
| AF1E | LDY #&24 ; Y=&24: dest offset in TXCB | |||
| AF20 | STA (net_rx_ptr),y ; Store at (net_rx_ptr)+Y | |||
| AF22 | DEY ; Y=&23 | |||
| AF23 | PLA ; Restore station | |||
| AF24 | STA (net_rx_ptr),y ; Store at (net_rx_ptr)+Y | |||
| AF26 | LDX #&0b ; X=&0B: rx_palette_txcb_template offset | |||
| AF28 | LDY #&0b ; Y=&0B: dest offset in workspace | |||
| AF2A | .loop_copy_spool_rx←1← AF3B BPL | |||
| LDA rx_palette_txcb_template,x ; Read template byte at rx_palette_txcb_template+X | ||||
| AF2D | CMP #&fd ; Compare with &FD (skip-byte marker) | |||
| AF2F | BEQ advance_spool_rx_idx ; Equal: skip this byte | |||
| AF31 | CMP #&fc ; Compare with &FC (page-ptr marker) | |||
| AF33 | BNE store_spool_rx_byte ; Not &FC: store as-is | |||
| AF35 | LDA net_rx_ptr_hi ; &FC: substitute net_rx_ptr_hi | |||
| AF37 | .store_spool_rx_byte←1← AF33 BNE | |||
| STA (nfs_workspace),y ; Store at (nfs_workspace)+Y | ||||
| AF39 | .advance_spool_rx_idx←1← AF2F BEQ | |||
| DEY ; Next dest | ||||
| AF3A | DEX ; Next source | |||
| AF3B | BPL loop_copy_spool_rx ; Loop until X wraps | |||
| AF3D | LDA #&21 ; A=&21: TXCB control byte | |||
| AF3F | STA net_tx_ptr ; Store at net_tx_ptr lo | |||
| AF41 | LDA net_rx_ptr_hi ; Read net_rx_ptr_hi | |||
| AF43 | STA net_tx_ptr_hi ; Store as net_tx_ptr hi | |||
| AF45 | JSR setup_pass_txbuf ; Set up the pass-through TX buffer | |||
| AF48 | JSR send_net_packet ; Send the TX packet | |||
| AF4B | LDA #0 ; A=0: clear net_tx_ptr lo | |||
| AF4D | STA net_tx_ptr ; Store -> net_tx_ptr lo | |||
| AF4F | LDA nfs_workspace_hi ; Read nfs_workspace_hi | |||
| AF51 | STA net_tx_ptr_hi ; Store -> net_tx_ptr hi | |||
| AF53 | JSR wait_net_tx_ack ; Wait for TX ack | |||
| AF56 | LDY #&2d ; Y=&2D: spool result-byte offset | |||
| AF58 | LDA (net_rx_ptr),y ; Read result via (net_rx_ptr)+Y | |||
| AF5A | BEQ spool_tx_succeeded ; Z: success path | |||
| AF5C | CMP #3 ; Compare with 3 (retry threshold) | |||
| AF5E | BNE spool_tx_retry ; Other: take retry path | |||
| AF60 | .spool_tx_succeeded←1← AF5A BEQ | |||
| PLA ; Discard saved TX cmd | ||||
| AF61 | PLA ; Restore vdu_status | |||
| AF62 | STA vdu_status ; Restore vdu_status | |||
| AF64 | LDA #0 ; A=0: success-return code | |||
| AF66 | JSR append_byte_to_rxbuf ; Append byte to RX buffer | |||
| AF69 | JSR process_spool_data ; Recurse: process_spool_data | |||
| AF6C | LDA ws_0d6a ; Read shadow ACR | |||
| AF6F | AND #&f0 ; Mask high nibble | |||
| AF71 | STA ws_0d6a ; Store updated shadow | |||
| AF74 | RTS ; Return | |||
| AF75 | .spool_tx_retry←1← AF5E BNE | |||
| TAX ; Save retry counter | ||||
| AF76 | PLA ; Pop saved TX cmd | |||
| AF77 | SEC ; Set carry | |||
| AF78 | SBC #1 ; Decrement retry | |||
| AF7A | BNE start_spool_retry ; Non-zero: retry from start_spool_retry | |||
| AF7C | CPX #1 ; Check the saved retry counter | |||
| AF7E | BNE printer_busy_msg ; Not 1: take printer_busy_msg path | |||
| fall through ↓ | ||||
Raise 'Printer busy' errorLoads error code &A6 and tail-calls error_inline_log with the inline string 'Printer busy'. Called when an attempt is made to enable a printer server while one is already active. Never returns. |
|
| AF80 | .err_printer_busy←1← B3B3 JMP |
| LDA #&a6 ; A=&A6: 'Printer busy' error code | |
| AF82 | JSR error_inline_log ; Raise via error_inline_log (never returns) |
| AF85 | EQUS "Printer busy." |
| AF92 | .printer_busy_msg←1← AF7E BNE |
| LDA #&a7 ; A=&A7: 'Printer jammed' error code | |
| AF94 | JSR error_inline_log ; Raise via error_inline_log (never returns) |
| AF97 | 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.
|
||||
| AFA6 | .send_disconnect_reply←3← 97E6 JSR← AEF0 JSR← BCE2 JSR | |||
| STX net_tx_ptr ; X = caller's TX-ptr low byte | ||||
| AFA8 | STY net_tx_ptr_hi ; Y = caller's TX-ptr high byte | |||
| AFAA | PHA ; Save A (the disconnect status to send) | |||
| AFAB | ORA #0 ; Test if A=0 (broadcast disconnect) | |||
| AFAD | BEQ send_disconnect_status ; Yes: skip the per-station scan | |||
| AFAF | LDX #&ff ; X=&FF: scan counter -- INX in loop bumps to 0 X=&FF: start search from -1 | |||
| AFB1 | TAY ; Y=A: status code (also used as station-table key) Y = disconnect code | |||
| AFB2 | .loop_scan_disconnect←2← AFBB BNE← AFC5 BNE | |||
| TYA ; Restore status into A for the compare | ||||
| AFB3 | INX ; Step station-table index | |||
| AFB4 | CMP hazel_fcb_slot_attr,x ; Compare with table[X] at &C230 (per-station status) Compare with station table entry | |||
| AFB7 | BEQ verify_stn_match ; Match: verify station address still matches | |||
| AFB9 | CPX #&0f ; Reached end of 16-slot table? | |||
| AFBB | BNE loop_scan_disconnect ; No: keep scanning | |||
| AFBD | LDA #0 ; All slots tested, no match: A=0 | |||
| AFBF | BEQ send_disconnect_status ; Always taken: jump to send-status | |||
| AFC1 | .verify_stn_match←1← AFB7 BEQ | |||
| TAY ; Y = matching index | ||||
| AFC2 | JSR match_station_net ; Verify station/network at this slot still matches caller Check station and network match | |||
| AFC5 | BNE loop_scan_disconnect ; Mismatch: station moved, keep scanning | |||
| AFC7 | LDA hazel_fcb_status,x ; Read connection-active flag at &C260+X | |||
| AFCA | AND #1 ; Mask to bit 0 (active flag) | |||
| AFCC | .send_disconnect_status←2← AFAD BEQ← AFBF BEQ | |||
| LDY #0 ; Y=0: TX[0] = control byte | ||||
| AFCE | ORA (net_tx_ptr),y ; OR active-flag bit into the status | |||
| AFD0 | PHA ; Save the combined status | |||
| AFD1 | STA (net_tx_ptr),y ; Write it to TX[0] | |||
| AFD3 | JSR send_net_packet ; Send the disconnect packet via four-way handshake Send the packet | |||
| AFD6 | LDA #&ff ; A=&FF: sentinel | |||
| AFD8 | LDY #8 ; Y=8: TX[8] / TX[9] = packet trailer markers | |||
| AFDA | STA (net_tx_ptr),y ; Write &FF at TX[8] | |||
| AFDC | INY ; Step Y | |||
| AFDD | STA (net_tx_ptr),y ; Write &FF at TX[9] | |||
| AFDF | PLA ; Pull the saved status | |||
| AFE0 | TAX ; Move into X for the test | |||
| AFE1 | LDY #&d1 ; Y=&D1: control byte for ack-mode TXCB[1] | |||
| AFE3 | PLA ; Pull caller's original A again (was double-saved) Check original disconnect code | |||
| AFE4 | PHA ; Push it back | |||
| AFE5 | BEQ store_tx_ctrl_byte ; A=0: skip the override | |||
| AFE7 | LDY #&90 ; Non-zero: use Y=&90 (FS reply port instead) Non-zero: use &90 control | |||
| AFE9 | .store_tx_ctrl_byte←1← AFE5 BEQ | |||
| TYA ; Move chosen control/port into A | ||||
| AFEA | LDY #1 ; Y=1: TX[1] is the port byte | |||
| AFEC | STA (net_tx_ptr),y ; Write to TX[1] | |||
| AFEE | TXA ; Move saved status into A | |||
| AFEF | DEY ; Y=0: TX[0] for ack poll | |||
| AFF0 | PHA ; Push the status (we'll EOR with reply below) Save status on stack | |||
| AFF1 | .loop_wait_disc_tx_ack←1← AFFD BCS | |||
| LDA #&7f ; A=&7F: marker pattern | ||||
| AFF3 | STA (net_tx_ptr),y ; Write to TX[0] | |||
| AFF5 | JSR wait_net_tx_ack ; Wait for the TX/RX flip | |||
| AFF8 | PLA ; Pull saved status (peek without consuming) Restore status | |||
| AFF9 | PHA ; Push it back | |||
| AFFA | EOR (net_tx_ptr),y ; EOR with TX[0]: zero iff reply matches saved Compare with current TX buffer | |||
| AFFC | ROR ; Rotate result; C set if bit 0 differs Rotate result bit 0 to carry | |||
| AFFD | BCS loop_wait_disc_tx_ack ; C set: keep waiting | |||
| AFFF | PLA ; Discard saved status | |||
| B000 | PLA ; Discard caller's saved A | |||
| B001 | RTS ; Return | |||
Spool / disconnect TX control-block template (12 bytes)12-byte Econet TXCB initialisation template used by the spool /
disconnect TX paths. Copied into the workspace TXCB at offsets
|
|
| B002 | .tx_econet_txcb_template←1← AF0B LDA |
| EQUB &80 ; ctrl=&80 (standard TX) | |
| B003 | EQUB &9F ; port=&9F |
| B004 | EQUB &00 ; dest station=&00 (filled later) |
| B005 | EQUB &00 ; dest network=&00 (filled later) |
| B006 | EQUB &9F ; buf start lo (&9F) |
| B007 | EQUB &8E ; buf start hi (&8E); start = &8E9F |
| B008 | EQUB &FF ; buf start ext lo=&FF |
| B009 | EQUB &FF ; buf start ext hi=&FF |
| B00A | EQUB &A7 ; buf end lo (&A7) |
| B00B | EQUB &8E ; buf end hi (&8E); end = &8EA7 |
| B00C | EQUB &FF ; buf end ext lo=&FF |
| B00D | EQUB &FF ; buf end ext hi=&FF |
Palette-RX control-block template (12 bytes)12-byte template used by the *PS / palette-RX paths. Copied with
marker processing: |
|
| B00E | .rx_palette_txcb_template←1← AF2A LDA |
| EQUB &7F ; ctrl=&7F (RX listen) | |
| B00F | EQUB &9E ; port=&9E |
| B010 | EQUB &FD ; skip: preserve dest station |
| B011 | EQUB &FD |
| B012 | EQUB &2D |
| B013 | EQUB &FC ; buf start hi=page ptr (&FC) |
| B014 | EQUB &FF ; buf start ext lo=&FF |
| B015 | EQUB &FF ; buf start ext hi=&FF |
| B016 | EQUB &30 |
| B017 | EQUB &FC |
| B018 | EQUB &FF ; buf end ext lo=&FF |
| B019 | EQUB &FF ; buf end ext hi=&FF |
Language reply 2: save palette / VDU stateReached via the language-reply dispatch table when a remote sends
reply code 2 ('save palette and VDU state'). Saves the current
template byte from |
|
| B01A | .lang_2_save_palette_vdu |
| LDA osword_flag ; Read osword_flag (preserved across the dispatch) | |
| B01C | PHA ; Save state byte |
| B01D | LDA #&e9 ; A=&E9: workspace start lo for palette save |
| B01F | STA nfs_workspace ; Store as nfs_workspace lo |
| B021 | LDY #0 ; Y=0 |
| B023 | STY osword_flag ; Reset osword_flag = 0 |
| B025 | LDA vdu_screen_mode ; Read vdu_screen_mode (MOS state byte) |
| B028 | STA (nfs_workspace),y ; Store at (nfs_workspace)+0 |
| B02A | INC nfs_workspace ; Advance nfs_workspace lo |
| B02C | LDA vdu_display_start_hi ; Read vdu_display_start_hi (next MOS byte) |
| B02F | PHA ; Save another byte |
| B030 | TYA ; A=0 for first palette entry |
| B031 | .loop_read_palette←1← B050 BNE |
| STA (nfs_workspace),y ; Store at (nfs_workspace) | |
| B033 | LDX nfs_workspace ; Read updated nfs_workspace lo |
| B035 | LDY nfs_workspace_hi ; Read nfs_workspace hi |
| B037 | LDA #osword_read_palette ; A=&0B: OSWORD &0B = read palette entry |
| B039 | JSR osword ; Read palette entry |
| B03C | PLA ; Restore inner saved |
| B03D | LDY #0 ; Y=0 |
| B03F | STA (nfs_workspace),y ; Store palette result at workspace |
| B041 | INY ; Y=1: physical colour offset |
| B042 | LDA (nfs_workspace),y ; Re-read palette result |
| B044 | PHA ; Save for next iteration |
| B045 | LDX nfs_workspace ; Read updated workspace lo |
| B047 | INC nfs_workspace ; Advance workspace |
| B049 | INC osword_flag ; Increment osword_flag (palette index) |
| B04B | DEY ; Y=0 |
| B04C | LDA osword_flag ; Read updated osword_flag |
| B04E | CPX #&f9 ; Compare with &F9 (last palette entry) |
| B050 | BNE loop_read_palette ; Not done: loop |
| B052 | PLA ; Restore outer saved |
| B053 | STY osword_flag ; Reset osword_flag = 0 after palette loop |
| B055 | INC nfs_workspace ; Advance workspace |
| B057 | JSR serialise_palette_entry ; Serialise the next palette entry |
| B05A | INC nfs_workspace ; Advance workspace |
| B05C | PLA ; Restore final saved |
| B05D | STA osword_flag ; Save osword_flag |
| 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.
|
||||
| B05F | .commit_state_byte←4← 9856 JMP← 987E JSR← 98B6 JSR← A997 JMP | |||
| LDA ws_0d69 ; Read shadow ACR copy from ws_0d69 | ||||
| B062 | STA ws_0d68 ; Store as live ACR shadow at ws_0d68 | |||
| B065 | 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.
|
|||||||||||
| B066 | .serialise_palette_entry←1← B057 JSR | ||||||||||
| LDA vdu_mode ; Read vdu_mode (current palette index) | |||||||||||
| B069 | ORA #&40 ; Mark as palette entry | ||||||||||
| B06B | STA (nfs_workspace),y ; Store at (nfs_workspace)+Y | ||||||||||
| B06D | LDX vdu_mode ; Read vdu_mode | ||||||||||
| B070 | INC nfs_workspace ; Advance workspace | ||||||||||
| B072 | TYA ; A = current Y (= 0) | ||||||||||
| B073 | STA (nfs_workspace),y ; Store 0 at (nfs_workspace)+Y | ||||||||||
| B075 | LDA read_osbyte_table,x ; Read lookup byte from read_osbyte_table+X | ||||||||||
| B078 | LDX #0 ; X=0: indexed-indirect mode | ||||||||||
| B07A | INC nfs_workspace ; Advance workspace | ||||||||||
| B07C | STA (nfs_workspace,x) ; Store at (nfs_workspace,X) | ||||||||||
| B07E | JSR read_osbyte_to_ws_x0 ; Read OSBYTE result via x=0 helper | ||||||||||
| 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.
|
|||||||||
| B081 | .read_osbyte_to_ws_x0←1← B07E JSR | ||||||||
| LDX #0 ; X=0: zero-arg helper entry | |||||||||
| 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.
|
|||||||||||
| B083 | .read_osbyte_to_ws | ||||||||||
| LDY osword_flag ; Y = osword_flag (OSBYTE-table index) | |||||||||||
| B085 | INC osword_flag ; Increment osword_flag for next call | ||||||||||
| B087 | INC nfs_workspace ; Advance nfs_workspace | ||||||||||
| B089 | LDA read_osbyte_return,y ; Load OSBYTE number from read_osbyte_return+Y | ||||||||||
| B08C | LDY #&ff ; Y=&FF -- OSBYTE arg (read mode) | ||||||||||
| B08E | JSR osbyte ; Issue OSBYTE | ||||||||||
| B091 | TXA ; Result to A | ||||||||||
| B092 | LDX #0 ; X=0: indexed-indirect mode | ||||||||||
| B094 | STA (nfs_workspace,x) ; Store at (nfs_workspace,X) | ||||||||||
| B096 | RTS ; Return | ||||||||||
| B097 | .read_osbyte_return←1← B089 LDA | ||||||||||
| EQUB &C2, &C3 | |||||||||||
| B099 | .read_osbyte_table←1← B075 LDA | ||||||||||
| EQUS "000@XX`" | |||||||||||
| ; *CDir command handler | |||||||||||
| ; Parses 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. |
|||||||||||
; Reached via PHA/PHA/RTS dispatch from cmd_table_fs entry |
|||||||||||
; *Cdir; the byte at the entry-1 address &B0A0 |
|||||||||||
; happens to decode as JMP (cdir_unused_dispatch_table,X) but is
; never executed. |
|||||||||||
| ; On Entry: | |||||||||||
;
|
|||||||||||
| B0A0 | .cmd_cdir_indirect_dispatch | ||||||||||
| JMP (cdir_unused_dispatch_table,x) ; JMP (cdir_unused_dispatch_ table,X) -- never executed; see cmd_cdir Save command line offset Push onto stack | |||||||||||
| B0A3 | JSR mask_owner_access ; Set owner-only access mask | ||||||||||
| B0A6 | JSR skip_to_next_arg ; Skip to optional size argument | ||||||||||
| B0A9 | CMP #&0d ; End of line? | ||||||||||
| B0AB | BNE parse_cdir_size ; No: parse size argument | ||||||||||
| B0AD | LDX #2 ; Default allocation size index = 2 | ||||||||||
| B0AF | BNE done_cdir_size ; ALWAYS branch | ||||||||||
| B0B1 | .parse_cdir_size←1← B0AB BNE | ||||||||||
| LDA #&ff ; A=&FF: mark as decimal parse | |||||||||||
| B0B3 | STA fs_work_4 ; Store decimal parse flag | ||||||||||
| B0B5 | JSR parse_addr_arg ; Parse numeric size argument | ||||||||||
| B0B8 | LDX #&1b ; X=&1B: top of 26-entry size table | ||||||||||
| B0BA | .loop_find_alloc_size←1← B0BE BCC | ||||||||||
| DEX ; Try next lower index | |||||||||||
| B0BB | CMP cdir_size_thresholds,x ; Compare size with threshold | ||||||||||
| B0BE | BCC loop_find_alloc_size ; A < threshold: keep searching | ||||||||||
| B0C0 | .done_cdir_size←1← B0AF BNE | ||||||||||
| STX hazel_txcb_data ; Store allocation size index | |||||||||||
| B0C3 | PLA ; Restore command line offset | ||||||||||
| B0C4 | TAY ; Transfer to Y | ||||||||||
| B0C5 | JSR save_ptr_to_os_text ; Save text pointer for filename parse | ||||||||||
| B0C8 | JSR parse_filename_arg ; Parse directory name argument | ||||||||||
| B0CB | LDX #1 ; X=1: one argument to copy | ||||||||||
| B0CD | JSR copy_arg_to_buf ; Copy directory name to TX buffer | ||||||||||
| B0D0 | LDY #&1b ; Y=&1B: *CDir FS command code | ||||||||||
| B0D2 | .cdir_dispatch_col | ||||||||||
| JMP save_net_tx_cb ; Send command to file server | |||||||||||
*CDir allocation size threshold table (26 entries)26 thresholds dividing 0-255 into size classes for the *CDir
directory-size argument. Table base is at |
|
| B0D5 | EQUB &00 ; Index 1: threshold 0 (catch-all) |
| B0D6 | EQUB &0A ; Index 2: threshold 10 (default) |
| B0D7 | EQUB &14 ; Index 3: threshold 20 |
| B0D8 | EQUB &1D ; Index 4: threshold 29 |
| B0D9 | EQUB &27 |
| B0DA | EQUB &31 |
| B0DB | EQUB &3B ; Index 7: threshold 59 |
| B0DC | EQUB &45 ; Index 8: threshold 69 |
| B0DD | EQUB &4F |
| B0DE | EQUB &58 ; Index 10: threshold 88 |
| B0DF | EQUB &62 ; Index 11: threshold 98 |
| B0E0 | EQUB &6C ; Index 12: threshold 108 |
| B0E1 | EQUB &76 |
| B0E2 | EQUB &80 |
| B0E3 | EQUB &8A ; Index 15: threshold 138 |
| B0E4 | EQUB &94 ; Index 16: threshold 148 |
| B0E5 | EQUB &9D |
| B0E6 | EQUB &A7 ; Index 18: threshold 167 |
| B0E7 | EQUB &B1 ; Index 19: threshold 177 |
| B0E8 | EQUB &BB |
| B0E9 | EQUB &C5 ; Index 21: threshold 197 |
| B0EA | EQUB &CF |
| B0EB | EQUB &D8 ; Index 23: threshold 216 |
| B0EC | EQUB &E2 ; Index 24: threshold 226 |
| B0ED | EQUB &EC ; Index 25: threshold 236 |
| B0EE | .cdir_size_done←1← 8F84 LDA |
| EQUB &F6 ; Index 26: threshold &F6 (246) -- last cdir-size threshold; doubles as cdir_size_done[0] (unread by init loop) | |
| B0EF | EQUB &FF ; cdir_size_done[1] = &FF -> tx_retry_count (retry counter init) |
| B0F0 | EQUB &28 ; cdir_size_done[2] = &28 -> rx_wait_timeout (40 retries) |
| B0F1 | EQUB &0A ; cdir_size_done[3] = &0A -> peek_retry_count (10 retries) |
*LCat command handlerRotates the caller's carry into bit 7 of
|
||||||
| B0F2 | .cmd_lcat | |||||
| ROR hazel_fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||||
| B0F5 | SEC ; Set carry (= library directory) | |||||
| B0F6 | BCS cat_set_lib_flag ; ALWAYS branch | |||||
*LEx command handlerRotates the caller's carry into bit 7 of
|
||||||
| B0F8 | .cmd_lex | |||||
| ROR hazel_fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||||
| B0FB | SEC ; Set carry (= library directory) | |||||
| B0FC | BCS ex_set_lib_flag ; ALWAYS branch | |||||
| B0FE | .ps_scan_resume | |||||
| JSR set_text_and_xfer_ptr ; Set OS text pointer and FS-options transfer ptr | ||||||
| B101 | LDY #0 ; Y=0: TX-buffer offset for the first byte | |||||
| fall through ↓ | ||||||
*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.
|
||||
| B103 | .cmd_ex | |||
| ROR hazel_fs_lib_flags ; Rotate carry into lib flag bit 7 | ||||
| B106 | CLC ; Clear carry (= current directory) | |||
| B107 | .ex_set_lib_flag←1← B0FC BCS | |||
| ROL hazel_fs_lib_flags ; Rotate carry back, clearing bit 7 | ||||
| B10A | LDA #&ff ; A=&FF: initial column counter | |||
| B10C | STA fs_spool_handle ; Store column counter | |||
| B10E | LDA #1 ; One entry per line (Ex format) | |||
| B110 | STA fs_work_7 ; Store entries per page | |||
| B112 | LDA #3 ; FS command code 3: Examine | |||
| B114 | STA fs_work_5 ; Store command code | |||
| B116 | BNE setup_ex_request ; ALWAYS branch | |||
FSCV reason 5: catalogue (*CAT)Sets up transfer parameters via |
|
| B118 | .fscv_5_cat |
| JSR set_xfer_params ; Set transfer parameters | |
| B11B | LDY #0 ; Y=0: start from entry 0 |
| B11D | ROR hazel_fs_lib_flags ; Rotate carry into lib flag |
| B120 | CLC ; Clear carry (= current directory) |
| B121 | .cat_set_lib_flag←1← B0F6 BCS |
| ROL hazel_fs_lib_flags ; Rotate carry back, clearing bit 7 | |
| B124 | LDA #3 ; Three entries per column (Cat) |
| B126 | STA fs_spool_handle ; Store column counter |
| B128 | STA fs_work_7 ; Store entries per page |
| B12A | LDA #&0b ; FS command code &0B: Catalogue |
| B12C | STA fs_work_5 ; Store command code |
| B12E | .setup_ex_request←1← B116 BNE |
| JSR save_ptr_to_os_text ; Save text pointer | |
| B131 | LDA #&ff ; A=&FF: enable escape checking |
| B133 | STA need_release_tube ; Set escapable flag |
| B135 | LDA #6 ; Command code 6 |
| B137 | STA hazel_txcb_data ; Store in TX buffer |
| B13A | JSR parse_filename_arg ; Parse directory argument |
| B13D | LDX #1 ; X=1: offset in buffer |
| B13F | JSR copy_arg_to_buf ; Copy argument to TX buffer |
| B142 | LDA hazel_fs_lib_flags ; Get library/FS flags |
| B145 | LSR ; Shift bit 0 to carry |
| B146 | BCC store_owner_flags ; Bit 0 clear: skip |
| B148 | ORA #&40 ; Set bit 6 (owner access flag) |
| B14A | .store_owner_flags←1← B146 BCC |
| ROL ; Rotate back | |
| B14B | STA hazel_fs_lib_flags ; Store modified flags |
| B14E | LDY #&12 ; Y=&12: FS command for examine |
| B150 | JSR save_net_tx_cb ; Send request to file server |
| B153 | LDX #3 ; X=3: offset to directory title |
| B155 | JSR print_10_chars ; Print directory title (10 chars) |
| B158 | JSR print_inline_no_spool ; Print '(' |
| B15B | EQUS "(" |
| B15C | LDA hazel_txcb_objtype ; Load FS object-type code from hazel_txcb_objtype (file/dir/etc) |
| B15F | JSR print_decimal_3dig_no_spool |
| B162 | JSR print_inline_no_spool ; Print ') ' to close the type-code field |
| B165 | EQUS ") " |
| B16B | LDY hazel_txcb_type ; Read hazel_txcb_type (FS reply opcode) |
| B16E | BNE print_public_label ; Non-zero (private library): take the public-label branch |
| B170 | JSR print_inline_no_spool ; Print 'Owner' + CR |
| B173 | EQUS "Owner." |
| B179 | BNE cat_after_label_print ; Non-zero: branch to cat_after_label_print |
| B17B | .print_public_label←1← B16E BNE |
| JSR print_inline_no_spool ; Print 'Public' + CR | |
| B17E | EQUS "Public." |
| B185 | .cat_after_label_print←1← B179 BNE |
| LDA hazel_fs_lib_flags ; Read hazel_fs_lib_flags | |
| B188 | PHA ; Push for stack-based saves |
| B189 | JSR mask_owner_access ; Mask owner access bits |
| B18C | LDY #&15 ; Y=&15: FS command for dir info |
| B18E | JSR save_net_tx_cb ; Send request to file server |
| B191 | INX ; Advance X past header |
| B192 | LDY #&10 ; Y=&10: print 16 chars |
| B194 | JSR print_chars_from_buf ; Print file entry |
| B197 | JSR print_inline_no_spool ; Print ' Option ' |
| B19A | EQUS " Option " |
| B1A5 | LDA hazel_fs_flags ; Read hazel_fs_flags |
| B1A8 | TAX ; Transfer to X for table lookup |
| B1A9 | JSR print_hex_byte_no_spool ; Print option as hex |
| B1AC | JSR print_inline_no_spool ; Print ' (' |
| B1AF | EQUS " (" |
| B1B1 | LDY option_str_offset_data,x ; Look up option-string offset for index X |
| B1B4 | .loop_print_dir_format←1← B1BD BNE |
| LDA option_offset_table,y ; Look up option byte at the resolved offset | |
| B1B7 | BMI print_dir_header ; Bit 7 of A set (negative): print directory header |
| B1B9 | JSR print_char_no_spool ; Print char (no spool) |
| B1BC | INY ; Advance Y |
| B1BD | BNE loop_print_dir_format ; Loop until Y wraps |
| B1BF | .print_dir_header←1← B1B7 BMI |
| JSR print_inline_no_spool ; Print ')\rDir. ' header for the directory listing | |
| B1C2 | EQUS ").Dir. " |
| B1C9 | LDX #&11 ; X=&11: filename offset in TX buffer |
| B1CB | JSR print_10_chars ; Print 10-char filename |
| B1CE | JSR print_inline_no_spool ; Print inline 'attr-bits' fragment |
| B1D1 | EQUS " Lib. " ; label for *Ex output |
| B1DB | LDX #&1b ; X=&1B: extension offset in TX buffer |
| B1DD | JSR print_10_chars ; Print 10-char extension |
| B1E0 | JSR print_newline_no_spool ; Print newline |
| B1E3 | PLA ; Pop saved counter |
| B1E4 | STA hazel_fs_lib_flags ; Store as fs_lib_flags |
| B1E7 | .setup_ex_pagination←1← B218 BNE |
| STY hazel_txcb_flag ; Save Y as hazel_txcb_flag (next-entry index) | |
| B1EA | STY fs_work_4 ; Save Y as fs_work_4 |
| B1EC | LDX fs_work_5 ; Load fs_work_5 (page count) |
| B1EE | STX hazel_txcb_count ; Store at hazel_txcb_count |
| B1F1 | LDX fs_work_7 ; Load fs_work_7 |
| B1F3 | STX hazel_txcb_data ; Store at hazel_txcb_data |
| B1F6 | LDX #3 ; X=3: TX[3] is start of arg buffer |
| B1F8 | JSR copy_arg_to_buf ; Copy filename arg |
| B1FB | LDY #3 ; Y=3: cmd code 3 (catalog) |
| B1FD | JSR save_net_tx_cb ; Send TX request |
| B200 | INX ; X advances entry counter |
| B201 | LDA hazel_txcb_data ; Read reply status |
| B204 | BEQ jmp_osnewl ; Z: empty reply -> exit cat |
| B206 | PHA ; Push reply status |
| B207 | .loop_scan_entry_data←1← B20B BPL |
| INY ; Advance Y | |
| B208 | LDA hazel_txcb_data,y ; Read entry byte from hazel_txcb_data+Y |
| B20B | BPL loop_scan_entry_data ; Bit 7 clear: keep scanning |
| B20D | STA hazel_txcb_lib,y ; Store with high-bit clear at hazel_txcb_lib+Y |
| B210 | JSR ex_print_col_sep ; Print column separator |
| B213 | PLA ; Pop saved status |
| B214 | CLC ; Clear carry for the ADC below |
| B215 | ADC fs_work_4 ; Add fs_work_4 (page accumulator) |
| B217 | TAY ; New index |
| B218 | BNE setup_ex_pagination ; Non-zero: continue paging |
| 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.
|
||||
| B21A | .print_10_chars←3← B155 JSR← B1CB JSR← B1DD JSR | |||
| LDY #&0a ; Y=10: ten characters to print (fixed-width field) | ||||
| fall through ↓ | ||||
Print Y characters from buffer via OSASCILoops Y times, loading each byte from fs_cmd_data+X and printing it via OSASCI. Advances X after each character, leaving X pointing past the last printed byte.
|
||||||
| B21C | .print_chars_from_buf←2← B194 JSR← B224 BNE | |||||
| LDA hazel_txcb_data,x ; Read next character from reply buffer at offset X | ||||||
| B21F | JSR print_char_no_spool ; Print via OSASCI, bypassing the *SPOOL file | |||||
| B222 | INX ; Step buffer offset | |||||
| B223 | DEY ; Step character counter | |||||
| B224 | BNE print_chars_from_buf ; Loop until Y=0 | |||||
| B226 | RTS ; Return; X points just past the last printed byte | |||||
| B227 | .jmp_osnewl←1← B204 BEQ | |||||
| JMP print_newline_no_spool | ||||||
Parse command argument from offset zeroSets Y=0 and falls through to parse_filename_arg for GSREAD-based filename parsing with prefix character handling.
|
||||
| B22A | .parse_cmd_arg_y0←3← 9FE6 JSR← A4FC JSR← B367 JSR | |||
| LDY #0 ; Y=0: scan from start of command line | ||||
| fall through ↓ | ||||
Parse filename via GSREAD with prefix handlingCalls
|
|||||||
| B22C | .parse_filename_arg←3← B0C8 JSR← B13A JSR← B6FD JSR | ||||||
| JSR gsread_to_buf ; Read the GSREAD-style filename argument into the &C030 buffer, then fall into parse_access_prefix | |||||||
| fall through ↓ | |||||||
Parse access and FS selection prefix charactersExamines the first character(s) of the parsed buffer at &C030 for prefix characters: '&' sets the FS selection flag (bit 6 of hazel_fs_lib_flags) 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. |
|
| B22F | .parse_access_prefix←4← 942C JSR← 94CF JSR← 9505 JSR← 9C2B JSR |
| LDA hazel_parse_buf ; Read first parsed-buffer character (the candidate prefix) | |
| B232 | EOR #&26 ; EOR with '&'; Z set iff the byte was '&' |
| B234 | BNE check_colon_prefix ; Not '&': try ':' (and '#') instead |
| B236 | LDA hazel_fs_lib_flags ; Read fs_lib_flags |
| B239 | ORA #&40 ; Set bit 6 (URD-relative resolution flag) |
| B23B | STA hazel_fs_lib_flags ; Write back updated flags |
| B23E | JSR strip_token_prefix ; Strip the '&' from the buffer (shift left + trim) |
| B241 | DEX ; Step caller's X back to account for the consumed character |
| B242 | LDA hazel_parse_buf ; Re-read the (now first) buffer byte after the strip |
| B245 | EOR #&2e ; EOR with '.'; Z set iff '&.' pair (URD root) |
| B247 | BNE check_hash_prefix ; Not '&.': just '&' alone -- check for trailing '#' |
| B249 | LDA hazel_parse_buf_1 ; It was '&.': peek the byte after the dot |
| B24C | EOR #&0d ; EOR with CR; Z set iff '&.CR>' (illegal: dot needs a name to follow) |
| B24E | BEQ error_bad_prefix ; '&.CR>' is invalid: raise 'Bad filename' |
| B250 | DEX ; Valid '&.name>': step X back for the dot too |
| fall through ↓ | |
Strip first character from parsed token bufferShifts all bytes in the &C030 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 ':'.
|
||||||
| B251 | .strip_token_prefix←5← 9459 JSR← 94EE JSR← 94F3 JSR← B23E JSR← B293 JSR | |||||
| TXA ; Save caller's X (TX buffer offset) | ||||||
| B252 | PHA ; Push it | |||||
| B253 | LDX #&ff ; X=&FF: INX in loop bumps to 0 for first byte | |||||
| B255 | .loop_shift_str_left←1← B25E BNE | |||||
| INX ; Step to next byte position | ||||||
| B256 | LDA hazel_parse_buf_1,x ; Read byte X+1 (the next character) | |||||
| B259 | STA hazel_parse_buf,x ; Store it back at byte X (shifting left by one) | |||||
| B25C | EOR #&0d ; EOR with CR; Z set if we just shifted the terminator | |||||
| B25E | BNE loop_shift_str_left ; More to shift: continue | |||||
| B260 | TXA ; X is now the buffer length (excluding CR) | |||||
| B261 | BEQ done_strip_prefix ; Empty after shift: skip trim, restore X, return | |||||
| B263 | .loop_trim_trailing←1← B270 BNE | |||||
| LDA hazel_parse_buf_m1,x ; Read last buffer byte (X-1 because we count from 0) | ||||||
| B266 | EOR #&20 ; EOR with space; Z set iff it's a trailing space | |||||
| B268 | BNE done_strip_prefix ; Not a space: trim done, restore X, return | |||||
| B26A | LDA #&0d ; It is a space: replace with CR (truncate the string) | |||||
| B26C | STA hazel_parse_buf_m1,x ; Store CR at the now-trimmed position | |||||
| B26F | DEX ; Step backwards | |||||
| B270 | BNE loop_trim_trailing ; Loop while X > 0 | |||||
| B272 | .done_strip_prefix←2← B261 BEQ← B268 BNE | |||||
| PLA ; Restore caller's TX buffer offset | ||||||
| B273 | TAX ; Transfer back to X | |||||
| B274 | .return_from_strip_prefix←3← B277 BEQ← B27E BNE← B289 BNE | |||||
| RTS ; Return | ||||||
| B275 | .check_hash_prefix←1← B247 BNE | |||||
| EOR #&23 ; Test for '#' prefix (3 ^ &23 = 0) | ||||||
| B277 | BEQ return_from_strip_prefix ; Equal: '#' was the prefix, return | |||||
| B279 | .error_bad_prefix←2← B24E BEQ← B2AF BEQ | |||||
| JMP error_bad_filename ; Other: not a recognised prefix -> error | ||||||
| B27C | .check_colon_prefix←1← B234 BNE | |||||
| EOR #&1c ; Test for ':' (&3F ^ &1C) | ||||||
| B27E | BNE return_from_strip_prefix ; Different: caller had no prefix, return | |||||
| B280 | LDA hazel_parse_buf_2 ; ':' confirmed -- read next char from parse buffer | |||||
| B283 | EOR #&2e ; Test for '.' (path separator) | |||||
| B285 | BEQ set_fs_select_flag ; Equal: ':.' qualified prefix | |||||
| B287 | EOR #&23 ; Test for '#' | |||||
| B289 | BNE return_from_strip_prefix ; Other: no recognised tail prefix, return | |||||
| B28B | .set_fs_select_flag←1← B285 BEQ | |||||
| LDA hazel_fs_lib_flags ; Recognised: load fs_lib_flags | ||||||
| B28E | ORA #&40 ; Set bit 6 (FS-select pending) | |||||
| B290 | STA hazel_fs_lib_flags ; Store updated fs_lib_flags | |||||
| B293 | JSR strip_token_prefix ; Recurse to strip the trailing component | |||||
| B296 | DEX ; Decrement X (consume processed char) | |||||
| B297 | RTS ; Return | |||||
| B298 | .option_str_offset_data←1← B1B1 LDY | |||||
| EQUB &00 ; Data: option string offset table | ||||||
| B299 | EQUS "/<c" | |||||
| B29C | .option_offset_table←1← B1B4 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.
|
||||||
| B29F | .copy_arg_to_buf_x0←5← 8DD5 JSR← 8E38 JSR← 9C39 JSR← 9E29 JSR← A5DF JSR | |||||
| LDX #0 ; X=0: place the argument at the start of the TX buffer; fall into copy_arg_to_buf 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.
|
|||||||||
| B2A1 | .copy_arg_to_buf←11← 9CEB JSR← 9E22 JSR← 9E45 JSR← 9FEB JSR← A501 JSR← A52E JSR← B0CD JSR← B13F JSR← B1F8 JSR← B36C JSR← B712 JSR | ||||||||
| LDY #0 ; Y=0: scan from start of command line (CLC entry skips '&' validation) Y=0: start of argument | |||||||||
| fall through ↓ | |||||||||
Copy command line characters to TX bufferCopies characters from (fs_crc_lo)+Y to fs_cmd_data+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.
|
||||||||
| B2A3 | .copy_arg_validated←3← 8DD0 JSR← 9553 JSR← 9589 JSR | |||||||
| SEC ; Set C: this entry validates against '&' | ||||||||
| B2A4 | .loop_copy_char←1← B2B7 BNE | |||||||
| LDA (fs_crc_lo),y ; Read next source byte through fs_crc_lo pointer Get character from command line | ||||||||
| B2A6 | STA hazel_txcb_data,x ; Store into TX buffer at offset X | |||||||
| B2A9 | BCC advance_positions ; Validation off (C clear): just advance positions Carry clear: skip validation | |||||||
| B2AB | CMP #&21 ; Test against '!' to bias the EOR comparison | |||||||
| B2AD | EOR #&26 ; EOR with '&'; Z set iff source byte was '&' | |||||||
| B2AF | BEQ error_bad_prefix ; '&' inside the argument is illegal: raise 'Bad filename' Yes: '&' not allowed in filenames | |||||||
| B2B1 | .restore_after_check | |||||||
| EOR #&26 ; Restore A by undoing the EOR (so the loop terminator test below sees the original byte) '&' in filename: bad filename | ||||||||
| B2B3 | .advance_positions←1← B2A9 BCC | |||||||
| INX ; Advance TX buffer offset | ||||||||
| B2B4 | INY ; Advance command-line offset | |||||||
| B2B5 | EOR #&0d ; EOR with CR; Z set iff we just stored the terminator Is it CR (end of line)? | |||||||
| B2B7 | BNE loop_copy_char ; More to copy: continue | |||||||
| B2B9 | .loop_trim_trailing_spaces←1← B2C6 BNE | |||||||
| LDA hazel_txcb_network,x ; Look at the byte just before the CR we stopped on Load character from end of buffer | ||||||||
| B2BC | EOR #&20 ; EOR with space; Z set iff that byte was a trailing space Test for space (&20) | |||||||
| B2BE | BNE done_trim_spaces ; Not a space: trim done | |||||||
| B2C0 | DEX ; Step back over the space | |||||||
| B2C1 | LDA #&0d ; A=&0D: replace the trailing space with CR | |||||||
| B2C3 | STA hazel_txcb_lib,x ; Store CR at the now-truncated end | |||||||
| B2C6 | BNE loop_trim_trailing_spaces ; Always taken (A=&0D from LDA #&0D so Z is clear); look at the next byte back ALWAYS: trim next character back | |||||||
| B2C8 | .done_trim_spaces←1← B2BE BNE | |||||||
| LDA #0 ; All trailing spaces consumed (or none present) A=0: success return code | ||||||||
| B2CA | .return_from_copy_arg←1← B2E0 BMI | |||||||
| RTS ; Return | ||||||||
| B2CB | EQUS "Load" | |||||||
Clear FS selection flags from options word
|
||||
| B2CF | .mask_owner_access←15← 8E53 JSR← 94C9 JSR← 9501 JSR← 9C28 JSR← A036 JSR← A153 JSR← A4E7 JSR← A4F4 JSR← A585 JSR← A58F JSR← AC52 JSR← B0A3 JSR← B189 JSR← B357 JSR← B6F3 JSR | |||
| LDA hazel_fs_lib_flags ; Read fs_lib_flags (now at &C271 in 4.21) | ||||
| B2D2 | AND #&1f ; Keep only the 5-bit owner access mask | |||
| B2D4 | STA hazel_fs_lib_flags ; Store back, clearing FS-selection and other high bits Store masked flags | |||
| B2D7 | RTS ; Return | |||
| B2D8 | EQUS "Run" | |||
| B2DB | .ex_init_scan_x0 | |||
| LDX #0 ; X=0: scan from start of TX entry | ||||
| B2DD | .loop_scan_entries←1← B2FD BNE | |||
| LDA hazel_txcb_data,x ; Read entry byte at hazel_txcb_data+X | ||||
| B2E0 | BMI return_from_copy_arg ; Bit 7 set: end-of-entries -> return | |||
| B2E2 | BNE col_sep_print_cr ; Non-printable: take CR-newline path at col_sep_print_cr | |||
| 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. |
|
| B2E4 | .ex_print_col_sep←1← B210 JSR |
| LDY fs_spool_handle ; Read fs_spool_handle (also column counter in *Cat mode) | |
| B2E6 | BMI col_sep_eol_check ; Negative: *Ex mode (one-per-line) -- skip column logic, just print newline |
| B2E8 | INY ; Bump column counter |
| B2E9 | TYA ; Get the new value into A |
| B2EA | AND #3 ; Wrap to 0..3 (4 columns per row) |
| B2EC | STA fs_spool_handle ; Save the new column index |
| B2EE | BEQ col_sep_eol_check ; Wrapped to 0: end of row, print newline |
| B2F0 | JSR print_inline_no_spool ; Mid-row: print 2-space column separator via inline |
| B2F3 | EQUS " " |
| B2F5 | BNE col_sep_print_char ; Non-zero: take col_sep_print_char tail |
| B2F7 | .col_sep_eol_check←2← B2E6 BMI← B2EE BEQ |
| LDA #&0d ; A=&0D: CR character | |
| B2F9 | .col_sep_print_cr←1← B2E2 BNE |
| JSR print_char_no_spool ; Print CR (no spool) | |
| B2FC | .col_sep_print_char←1← B2F5 BNE |
| INX ; Next entry | |
| B2FD | BNE loop_scan_entries ; Loop until X wraps |
| B2FF | EOR zp_0078 |
| B301 | ADC zp_0063 |
| fall through ↓ | |
Print 3-digit decimal via *SPOOL-bypassing printAs print_decimal_3dig (&B32A) but each digit is emitted via print_char_no_spool, which closes the *SPOOL handle around OSASCI so the digit doesn't appear in any active capture. Always prints all three digits (no leading-zero suppression).
|
||||
| B303 | .print_decimal_3dig_no_spool←1← B15F JSR | |||
| TAY ; Y = value to convert (digits read off via successive divisions) | ||||
| B304 | LDA #&64 ; Divisor for hundreds digit | |||
| B306 | JSR print_decimal_digit_no_spool ; Print hundreds digit | |||
| B309 | LDA #&0a ; Divisor for tens digit | |||
| B30B | JSR print_decimal_digit_no_spool ; Print tens digit | |||
| B30E | LDA #1 ; Divisor for units digit (always print at least the units to avoid the empty 0 case) | |||
| fall through ↓ | ||||
Print one decimal digit, *SPOOL-bypassingAs print_decimal_digit (&B338) but emits via print_char_no_spool. fs_error_ptr is used as scratch storage for the divisor and is preserved across the print.
|
|||||||||
| B310 | .print_decimal_digit_no_spool←2← B306 JSR← B30B JSR | ||||||||
| STA fs_error_ptr ; Stash divisor in fs_error_ptr (the SBC target below) | |||||||||
| B312 | TYA ; Convert remaining value to A | ||||||||
| B313 | LDX #&2f ; X = '0'-1: digit counter, INX in the loop steps to '0' first | ||||||||
| B315 | SEC ; Set carry | ||||||||
| B316 | .loop_divide_decimal_digit←1← B319 BCS | ||||||||
| INX ; Step quotient digit | |||||||||
| B317 | SBC fs_error_ptr ; Subtract divisor | ||||||||
| B319 | BCS loop_divide_decimal_digit ; No underflow: keep dividing | ||||||||
| B31B | ADC fs_error_ptr ; Underflow: add divisor back to recover the remainder | ||||||||
| B31D | TAY ; Remainder -> Y, ready for the next digit | ||||||||
| B31E | TXA ; Move digit ('0'-'9') from X into A for printing | ||||||||
| B31F | LDX fs_error_ptr ; Save divisor in X across the print (print_char_no_spool preserves X is not guaranteed) | ||||||||
| B321 | JSR print_char_no_spool ; Print the digit, bypassing *SPOOL | ||||||||
| B324 | STX fs_error_ptr ; Restore divisor from X | ||||||||
| B326 | RTS ; Return | ||||||||
Print decimal number with leading zero suppressionSets
|
||||
| B327 | .print_num_no_leading←5← 8A79 JSR← 90DD JSR← 9665 JSR← 9676 JSR← 9683 JSR | |||
| BIT always_set_v_byte ; 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.
|
||||||
| B32A | .print_decimal_3dig←2← B55B JSR← B572 JMP | |||||
| TAY ; Transfer value to Y (remainder) | ||||||
| B32B | LDA #&64 ; A=100: hundreds divisor | |||||
| B32D | JSR print_decimal_digit ; Print hundreds digit | |||||
| B330 | LDA #&0a ; A=10: tens divisor | |||||
| B332 | JSR print_decimal_digit ; Print tens digit | |||||
| B335 | CLV ; Clear V (always print units) | |||||
| B336 | 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'.
|
|||||||||
| B338 | .print_decimal_digit←2← B32D JSR← B332 JSR | ||||||||
| STA fs_error_ptr ; Store divisor | |||||||||
| B33A | TYA ; Get remaining value | ||||||||
| B33B | LDX #&2f ; X='0'-1: digit counter | ||||||||
| B33D | SEC ; Set carry for subtraction | ||||||||
| B33E | PHP ; Save V flag for leading zero check | ||||||||
| B33F | .loop_divide_digit←1← B342 BCS | ||||||||
| INX ; Count quotient digit | |||||||||
| B340 | SBC fs_error_ptr ; Subtract divisor | ||||||||
| B342 | BCS loop_divide_digit ; No underflow: continue dividing | ||||||||
| B344 | ADC fs_error_ptr ; Add back divisor (get remainder) | ||||||||
| B346 | TAY ; Remainder to Y for next digit | ||||||||
| B347 | TXA ; Digit character to A | ||||||||
| B348 | PLP ; Restore V flag | ||||||||
| B349 | BVC print_nonzero_digit ; V clear: always print digit | ||||||||
| B34B | CMP #&30 ; V set: is digit '0'? | ||||||||
| B34D | BEQ return_from_print_digit ; Yes: suppress leading zero | ||||||||
| B34F | .print_nonzero_digit←1← B349 BVC | ||||||||
| LDX fs_error_ptr ; Save divisor across OSASCI call | |||||||||
| B351 | JSR osasci ; Write character | ||||||||
| B354 | STX fs_error_ptr ; Restore divisor | ||||||||
| B356 | .return_from_print_digit←1← B34D BEQ | ||||||||
| RTS ; Return | |||||||||
*Info command handlerDispatched from the star-command table at index &28. Clears the
owner-only access bits via
|
||||
| B357 | .cmd_info_dispatch | |||
| JSR mask_owner_access ; Clear owner-only access bits before checking the URD | ||||
| B35A | LDA #&69 ; A=&69: 'i' character (info prefix) | |||
| B35C | STA hazel_txcb_data ; Store 'i' as start of FS command name in the TX buffer | |||
| B35F | LDA #&2e ; A='.': abbreviation terminator | |||
| B361 | STA hazel_txcb_flag ; Store '.' as command-name terminator | |||
| B364 | JSR save_ptr_to_os_text ; Save the command-line pointer for the dispatcher | |||
| B367 | JSR parse_cmd_arg_y0 ; Parse the *Info argument from the command line | |||
| B36A | LDX #2 ; X=2: TX-buffer offset to copy the arg into (after 'i.') | |||
| B36C | JSR copy_arg_to_buf ; Append parsed argument to the TX command buffer | |||
| B36F | TAY ; A = next index | |||
| B370 | JMP send_cmd_and_dispatch ; Send the FS command and dispatch the reply | |||
Copy text pointer to OS text pointer workspaceSaves fs_crc_lo/hi into the MOS text pointer locations at &00F2/&00F3. Preserves A on the stack. Called before GSINIT/GSREAD sequences that need to parse from the current command line position.
|
||||
| B373 | .save_ptr_to_os_text←9← A03C JSR← A4E4 JSR← A4F1 JSR← B0C5 JSR← B12E JSR← B364 JSR← B3F6 JSR← B5D4 JSR← B6FA JSR | |||
| PHA ; Save A | ||||
| B374 | LDA fs_crc_lo ; Copy text pointer low byte | |||
| B376 | STA os_text_ptr ; To OS text pointer low | |||
| B378 | LDA fs_crc_hi ; Copy text pointer high byte | |||
| B37A | STA os_text_ptr_hi ; To OS text pointer high | |||
| B37C | PLA ; Restore A | |||
| B37D | RTS ; Return | |||
| B37E | .loop_advance_char←1← B389 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.
|
|||||||||
| B37F | .skip_to_next_arg←1← B0A6 JSR | ||||||||
| LDA (fs_crc_lo),y ; Load char from command line | |||||||||
| B381 | CMP #&20 ; Space? | ||||||||
| B383 | BEQ loop_skip_space_chars ; Yes: skip trailing spaces | ||||||||
| B385 | CMP #&0d ; CR (end of line)? | ||||||||
| B387 | BEQ return_from_skip_arg ; Yes: return (at end) | ||||||||
| B389 | BNE loop_advance_char ; ALWAYS branch | ||||||||
| B38B | .loop_skip_space_chars←2← B383 BEQ← B390 BEQ | ||||||||
| INY ; Advance past space | |||||||||
| B38C | LDA (fs_crc_lo),y ; Load next character | ||||||||
| B38E | CMP #&20 ; Still a space? | ||||||||
| B390 | BEQ loop_skip_space_chars ; Yes: skip multiple spaces | ||||||||
| B392 | .return_from_skip_arg←1← B387 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.
|
||||
| B393 | .save_ptr_to_spool_buf←2← B3B9 JSR← B590 JSR | |||
| PHA ; Save A | ||||
| B394 | LDA fs_crc_lo ; Copy text pointer low byte | |||
| B396 | STA fs_options ; To spool buffer pointer low | |||
| B398 | LDA fs_crc_hi ; Copy text pointer high byte | |||
| B39A | STA fs_block_offset ; To spool buffer pointer high | |||
| B39C | PLA ; Restore A | |||
| B39D | 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 (addr_work), and clears the low byte (work_ae) to zero. Preserves Y on the stack.
|
||||||
| B39E | .init_spool_drive←2← B3B6 JSR← B583 JSR | |||||
| TYA ; Save Y | ||||||
| B39F | PHA ; Push it | |||||
| B3A0 | JSR get_ws_page ; Get workspace page number | |||||
| B3A3 | STA addr_work ; Store as spool drive page high | |||||
| B3A5 | PLA ; Restore Y | |||||
| B3A6 | TAY ; Transfer to Y | |||||
| B3A7 | LDA #0 ; A=0 | |||||
| B3A9 | STA work_ae ; Clear spool drive page low | |||||
| B3AB | 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.
|
||||
| B3AC | .cmd_ps | |||
| LDA #1 ; A=1: check printer ready | ||||
| B3AE | BIT ws_0d6a ; Test printer server workspace flag | |||
| B3B1 | BNE done_ps_available ; Non-zero: printer available | |||
| B3B3 | JMP err_printer_busy ; Printer not available: error | |||
| B3B6 | .done_ps_available←1← B3B1 BNE | |||
| JSR init_spool_drive ; Initialise spool drive | ||||
| B3B9 | JSR save_ptr_to_spool_buf ; Save pointer to spool buffer | |||
| B3BC | LDA (fs_options),y ; Read fs_options[Y] | |||
| B3BE | CMP #&0d ; End of command line? | |||
| B3C0 | BEQ no_ps_name_given ; Yes: no argument given | |||
| B3C2 | CLV ; Clear V (= explicit PS name given) | |||
| B3C3 | JSR is_decimal_digit ; Is first char a decimal digit? | |||
| B3C6 | BCC save_ps_cmd_ptr ; C clear: save ptr and continue | |||
| B3C8 | TYA ; A = current Y | |||
| B3C9 | PHA ; Save Y | |||
| B3CA | JSR load_ps_server_addr ; Load PS server address | |||
| B3CD | PLA ; Restore Y | |||
| B3CE | TAY ; Back to Y register | |||
| B3CF | JSR parse_fs_ps_args ; Parse FS/PS arguments | |||
| B3D2 | JMP store_ps_station ; 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.
|
||||
| B3D5 | .copy_ps_data_y1c←1← 8F5B JSR | |||
| LDY #&18 ; Y=&18: standard offset for the PS template; fall into copy_ps_data Start at offset &18 | ||||
| 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
|
|||||||||||
| B3D7 | .copy_ps_data←1← B5B6 JSR | ||||||||||
| LDX #&f8 ; X=&F8: walks 0..7 via wraparound (loads from ps_template_base+&F8 = ps_template_data &8E9F) X=&F8: offset into template | |||||||||||
| B3D9 | .loop_copy_ps_tmpl←1← B3E0 BNE | ||||||||||
| LDA ps_template_base,x ; Read template byte from ps_template_data + (X-&F8) Get template byte | |||||||||||
| B3DC | STA (net_rx_ptr),y ; Store into RX buffer at offset Y | ||||||||||
| B3DE | INY ; Step destination | ||||||||||
| B3DF | INX ; Step source -- wraps from &FF to &00 to terminate Next source offset | ||||||||||
| B3E0 | BNE loop_copy_ps_tmpl ; Loop while X != 0 (8 iterations: &F8..&FF) | ||||||||||
| B3E2 | RTS ; Return | ||||||||||
| B3E3 | .no_ps_name_given←1← B3C0 BEQ | ||||||||||
| BIT always_set_v_byte ; Set V (= no explicit PS name) | |||||||||||
| B3E6 | .save_ps_cmd_ptr←1← B3C6 BCC | ||||||||||
| STY ws_ptr_hi ; Save Y at ws_ptr_hi | |||||||||||
| B3E8 | BVS done_ps_name_parse ; V set: skip PS name parsing | ||||||||||
| B3EA | LDX #6 ; Max 6 characters for PS name | ||||||||||
| B3EC | LDY #&18 ; Buffer offset &1C for PS name | ||||||||||
| B3EE | LDA #&20 ; Space character | ||||||||||
| B3F0 | .loop_pad_ps_name←1← B3F4 BNE | ||||||||||
| STA (net_rx_ptr),y ; Fill buffer with space | |||||||||||
| B3F2 | INY ; Advance Y past padding | ||||||||||
| B3F3 | DEX ; Count down | ||||||||||
| B3F4 | BNE loop_pad_ps_name ; Loop while Y wraps | ||||||||||
| B3F6 | JSR save_ptr_to_os_text ; Save text pointer | ||||||||||
| B3F9 | LDY ws_ptr_hi ; Restore Y from ws_ptr_hi | ||||||||||
| B3FB | JSR gsinit ; Initialise string reading | ||||||||||
| B3FE | BEQ done_ps_name_parse ; Empty string: skip to send | ||||||||||
| B400 | LDX #6 ; X=6: scan up to 6 PS slots | ||||||||||
| B402 | STY ws_ptr_hi ; Save updated string pointer | ||||||||||
| B404 | LDY #&18 ; Buffer offset for PS name | ||||||||||
| B406 | STY table_idx ; Save buffer position | ||||||||||
| B408 | .loop_read_ps_char←1← B414 BNE | ||||||||||
| LDY ws_ptr_hi ; Restore string pointer | |||||||||||
| B40A | JSR gsread ; Read next character | ||||||||||
| B40D | STY ws_ptr_hi ; Save updated pointer | ||||||||||
| B40F | BCS done_ps_name_parse ; C set: end of slots | ||||||||||
| B411 | JSR store_char_uppercase ; Store char uppercased in buffer | ||||||||||
| B414 | BNE loop_read_ps_char ; Loop for more characters | ||||||||||
| B416 | .done_ps_name_parse←3← B3E8 BVS← B3FE BEQ← B40F BCS | ||||||||||
| JSR reverse_ps_name_to_tx ; Copy reversed PS name to TX | |||||||||||
| B419 | JSR send_net_packet ; Send PS status request | ||||||||||
| B41C | JSR pop_requeue_ps_scan ; Pop and requeue PS scan | ||||||||||
| B41F | JSR load_ps_server_addr ; Load PS server address | ||||||||||
| B422 | LDA #0 ; A=0 | ||||||||||
| B424 | TAX ; X=&00 | ||||||||||
| B425 | LDY #&20 ; Offset &24 in buffer | ||||||||||
| B427 | STA (net_rx_ptr),y ; Clear PS status byte | ||||||||||
| B429 | .loop_pop_ps_slot←1← B455 BNE | ||||||||||
| PLA ; Pop saved slot index | |||||||||||
| B42A | BEQ done_ps_scan ; Zero: all slots done | ||||||||||
| B42C | PHA ; Push it back (for retry) | ||||||||||
| B42D | TAY ; Transfer to Y | ||||||||||
| B42E | LDA (nfs_workspace),y ; Read slot status | ||||||||||
| B430 | BPL done_ps_slot_mark ; Bit 7 clear: slot inactive | ||||||||||
| B432 | JSR advance_y_by_4 ; Advance Y by 4 (next slot) | ||||||||||
| B435 | LDA (nfs_workspace),y ; Read ws byte at (nfs_workspace)+Y | ||||||||||
| B437 | STA work_ae ; Save as work_ae lo | ||||||||||
| B439 | LDA (work_ae,x) ; Read indirect via (work_ae,X) | ||||||||||
| B43B | BEQ read_ps_station_addr ; Z set: zero -> read station addr | ||||||||||
| B43D | CMP #3 ; Compare with 3 | ||||||||||
| B43F | BNE done_ps_slot_mark ; Other than 3: skip slot mark | ||||||||||
| B441 | .read_ps_station_addr←1← B43B BEQ | ||||||||||
| DEY ; Back up to network byte | |||||||||||
| B442 | LDA (nfs_workspace),y ; Read network byte | ||||||||||
| B444 | STA fs_work_6 ; Save as fs_work_6 | ||||||||||
| B446 | DEY ; Back up to station byte | ||||||||||
| B447 | LDA (nfs_workspace),y ; Read station byte | ||||||||||
| B449 | STA fs_work_5 ; Save as fs_work_5 | ||||||||||
| B44B | LDY #&20 ; Y=&20: PS marker offset | ||||||||||
| B44D | STA (net_rx_ptr),y ; Store station to (net_rx_ptr)+&20 | ||||||||||
| B44F | .done_ps_slot_mark←2← B430 BPL← B43F BNE | ||||||||||
| PLA ; Pop saved slot index | |||||||||||
| B450 | TAY ; Transfer to Y | ||||||||||
| B451 | LDA #&3f ; A=&3F: 'processed' marker | ||||||||||
| B453 | STA (nfs_workspace),y ; Mark slot as processed | ||||||||||
| B455 | BNE loop_pop_ps_slot ; ALWAYS branch | ||||||||||
| B457 | .done_ps_scan←1← B42A BEQ | ||||||||||
| JSR print_printer_server_is ; Print 'Printer server is ' fragment | |||||||||||
| B45A | LDY #&20 ; Y=&20: marker offset | ||||||||||
| B45C | LDA (net_rx_ptr),y ; Read marker byte | ||||||||||
| B45E | BNE print_ps_now ; Non-zero: print 'now |
||||||||||
| B460 | JSR print_inline ; Print 'still ' fragment | ||||||||||
| B463 | EQUS "still " | ||||||||||
| B469 | CLV ; Bit-7 terminator (next opcode) | ||||||||||
| B46A | BVC print_ps_padding ; ALWAYS branch | ||||||||||
| B46C | .print_ps_now←1← B45E BNE | ||||||||||
| JSR print_inline ; Print 'now ' fragment | |||||||||||
| B46F | EQUS "now " | ||||||||||
| B473 | NOP ; Bit-7 terminator | ||||||||||
| B474 | .print_ps_padding←1← B46A BVC | ||||||||||
| JSR print_fs_info_newline ; Print station number and newline | |||||||||||
| fall through ↓ | |||||||||||
Write printer-server station number into NFS workspaceStores fs_work_5/fs_work_6 (the parsed station/network bytes) into nfs_workspace offsets 2 and 3 (the printer-server slot's station/ net pair). Single caller (cmd_ps's parse-success path at &B3D2). |
|
| B477 | .store_ps_station←1← B3D2 JMP |
| LDY #2 ; Y=2: workspace offset for stored station | |
| B479 | LDA fs_work_5 ; Load station number |
| B47B | STA (nfs_workspace),y ; Store at (nfs_workspace)+2 |
| B47D | INY ; Y=&03 |
| B47E | LDA fs_work_6 ; Load network number |
| B480 | STA (nfs_workspace),y ; Store at (nfs_workspace)+3 |
| B482 | 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.
|
||||
| B483 | .print_file_server_is←1← A3B8 JSR | |||
| JSR print_inline ; Print 'File' via inline string | ||||
| B486 | EQUS "File" | |||
| B48A | CLV ; Clear V so the BVC below is taken | |||
| B48B | BVC print_server_is_suffix ; Always taken (V was just cleared); skip the 'Printer' prologue and reach the shared ' server is ' suffix | |||
| fall through ↓ | ||||
Print 'Printer server is ' prefixUses print_inline to output the full label 'Printer server is ' with trailing space.
|
||||
| B48D | .print_printer_server_is←2← B457 JSR← B5FE JSR | |||
| JSR print_inline ; Print 'Printer' via inline string | ||||
| B490 | EQUS "Printer" | |||
| B497 | NOP ; NOP -- bit-7 terminator + harmless resume opcode | |||
| B498 | .print_server_is_suffix←1← B48B BVC | |||
| JSR print_inline ; Print ' server is ' via inline string | ||||
| B49B | EQUS " server is " ; fragment for 'File/Printer server is ...' messages | |||
| B4A6 | NOP ; NOP -- bit-7 terminator + harmless resume opcode | |||
| B4A7 | RTS ; Return; caller now prints the actual server (file or printer) address | |||
Load printer server address from workspaceReads the station and network bytes from workspace offsets 2 and 3 into the station/network variables.
|
||||
| B4A8 | .load_ps_server_addr←4← B3CA JSR← B41F JSR← B5A1 JSR← B601 JSR | |||
| LDY #2 ; Y=2: workspace offset of PS station byte | ||||
| B4AA | LDA (nfs_workspace),y ; Read station byte | |||
| B4AC | STA fs_work_5 ; Stash in fs_work_5 (PS station) | |||
| B4AE | INY ; Y=3: workspace offset of PS network byte | |||
| B4AF | LDA (nfs_workspace),y ; Read network byte | |||
| B4B1 | STA fs_work_6 ; Stash in fs_work_6 (PS network) | |||
| B4B3 | 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.
|
||||
| B4B4 | .pop_requeue_ps_scan←2← B41C JSR← B5FB JSR | |||
| PLA ; Pull saved upper byte of ws_ptr_lo+osword_flag pair Pop return address low | ||||
| B4B5 | STA osword_flag ; Save into osword_flag | |||
| B4B7 | PLA ; Pull lower byte | |||
| B4B8 | STA ws_ptr_lo ; Save into ws_ptr_lo | |||
| B4BA | LDA #0 ; Push 0 -- placeholder, will be the stacked return marker Push 0 as end-of-list marker | |||
| B4BC | PHA ; Push it | |||
| B4BD | LDA #&84 ; ws_ptr_hi base = &84 (start of PS slot table area) Start scanning from offset &84 | |||
| B4BF | STA ws_ptr_hi ; Save base | |||
| B4C1 | LSR econet_flags ; Shift bit 0 of econet_flags into C (saved scan state) Shift PS slot flags right | |||
| B4C4 | LDA #3 ; A=3: PS slot index counter | |||
| B4C6 | .loop_scan_ps_slots←1← B4D8 BNE | |||
| JSR byte_to_2bit_index ; Convert slot index to 12-byte-aligned table offset Convert to 2-bit workspace index | ||||
| B4C9 | BCS done_ps_slot_scan ; Out of range (clamped to 0): all slots scanned Carry set: no more slots | |||
| B4CB | LSR ; A /= 2 (shift down) | |||
| B4CC | LSR ; A /= 2 again (now slot index * 4 / 4 = slot index) To get slot offset | |||
| B4CD | TAX ; X = slot index | |||
| B4CE | LDA (nfs_workspace),y ; Read slot's status byte at workspace[Y] | |||
| B4D0 | BEQ done_ps_slot_scan ; Slot empty (0): scan done | |||
| B4D2 | CMP #&3f ; Slot is '?' (uninitialised marker)? | |||
| B4D4 | BEQ reinit_ps_slot ; Yes: re-init this slot's data | |||
| fall through ↓ | ||||
Advance to next PS slot, wrap if all 256 doneINX / TXA / BNE loop_scan_ps_slots. Slot index in X advances; the BNE re-enters the scan unless X has wrapped to zero (all 256 slots scanned). Single caller (the no-match path at &B4FF in the PS slot scanner).
|
||||
| B4D6 | .skip_next_ps_slot←1← B4FF JMP | |||
| INX ; Step slot index | ||||
| B4D7 | TXA ; Move to A for next iteration | |||
| B4D8 | BNE loop_scan_ps_slots ; Loop while X != 0 (wraps when all slots done) Loop for more slots | |||
| B4DA | .reinit_ps_slot←1← B4D4 BEQ | |||
| TYA ; Save Y (slot table offset) | ||||
| B4DB | PHA ; Push it | |||
| B4DC | LDA #&7f ; A=&7F: slot status 'busy/active' | |||
| B4DE | STA (nfs_workspace),y ; Mark slot active | |||
| B4E0 | INY ; Step Y to control byte | |||
| B4E1 | LDA #&9e ; A=&9E: control byte (Master 128 PS-init pattern) Low byte: workspace page | |||
| B4E3 | STA (nfs_workspace),y ; Store control byte | |||
| B4E5 | LDA #0 ; A=0: zero-fill the next two bytes | |||
| B4E7 | JSR write_two_bytes_inc_y ; Write two zeros, advance Y | |||
| B4EA | LDA ws_ptr_hi ; Read current ws_ptr_hi | |||
| B4EC | STA (nfs_workspace),y ; Store as buffer-link low byte | |||
| B4EE | CLC ; Clear C ready for the +3 | |||
| B4EF | PHP ; Save flags so the ADC's C doesn't leak | |||
| B4F0 | ADC #3 ; Bump ws_ptr_hi by 3 (next slot's base) | |||
| B4F2 | PLP ; Restore flags | |||
| B4F3 | STA ws_ptr_hi ; Save updated ws_ptr_hi | |||
| B4F5 | JSR write_ps_slot_byte_ff ; Write buffer page + two &FF sentinels | |||
| B4F8 | LDA ws_ptr_hi ; Read ws_ptr_hi (now updated) | |||
| B4FA | STA (nfs_workspace),y ; Store as second-link byte | |||
| B4FC | .write_ps_slot_hi_link | |||
| JSR write_ps_slot_byte_ff ; Write another buffer page + two &FF sentinels Write another page + &FF bytes | ||||
| B4FF | JMP skip_next_ps_slot ; Continue scanning slots | |||
| B502 | .done_ps_slot_scan←2← B4C9 BCS← B4D0 BEQ | |||
| ASL econet_flags ; Restore bit 0 of econet_flags via ASL (recovers from the LSR at &B4C1) Shift PS slot flags back | ||||
| B505 | LDA ws_ptr_lo ; Pull saved ws_ptr_lo | |||
| B507 | PHA ; Push it back (the caller's return-resume sequence) Push onto stack | |||
| B508 | LDA osword_flag ; Pull saved osword_flag | |||
| B50A | PHA ; Push it back | |||
| B50B | LDA #&0a ; A=&0A: outer counter | |||
| B50D | TAY ; Y=&0A: inner counter | |||
| B50E | TAX ; X=&0A: middle counter | |||
| B50F | STA fs_work_4 ; Save outer in fs_work_4 | |||
| B511 | .loop_ps_delay←3← B512 BNE← B515 BNE← B519 BNE | |||
| DEY ; Decrement inner counter | ||||
| B512 | BNE loop_ps_delay ; Inner not zero: keep delaying | |||
| B514 | DEX ; Decrement middle | |||
| B515 | BNE loop_ps_delay ; Middle not zero: refresh inner and continue Middle loop: 10 iterations | |||
| B517 | DEC fs_work_4 ; Decrement outer in fs_work_4 | |||
| B519 | BNE loop_ps_delay ; Outer not zero: another full sweep (~1000 cycles) Outer loop: ~1000 delay cycles | |||
| B51B | 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.
|
|||||||||||
| B51C | .write_ps_slot_byte_ff←2← B4F5 JSR← B4FC JSR | ||||||||||
| INY ; Step Y to next workspace slot byte | |||||||||||
| B51D | LDA addr_work ; Load buffer page byte from addr_work | ||||||||||
| B51F | STA (nfs_workspace),y ; Write at offset Y | ||||||||||
| B521 | LDA #&ff ; A=&FF: sentinel; fall into write_two_bytes_inc_y to store two of them 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.
|
||||||
| B523 | .write_two_bytes_inc_y←1← B4E7 JSR | |||||
| INY ; Step Y to next destination | ||||||
| B524 | STA (nfs_workspace),y ; Write A at workspace offset Y | |||||
| B526 | INY ; Step Y again | |||||
| B527 | STA (nfs_workspace),y ; Write A at the next offset (two consecutive copies) Write byte to workspace | |||||
| B529 | INY ; Final INY leaves Y pointing past the second write Advance Y | |||||
| B52A | RTS ; Return | |||||
Reverse-copy printer server name to TX bufferCopies 8 bytes from the RX buffer at offsets
|
||||
| B52B | .reverse_ps_name_to_tx←2← B416 JSR← B588 JSR | |||
| LDY #&18 ; Y=&18: source offset (start of PS name in RX buffer) Start of PS name at offset &1C | ||||
| B52D | .loop_push_ps_name←1← B533 BNE | |||
| LDA (net_rx_ptr),y ; Read RX byte at offset Y | ||||
| B52F | PHA ; Push it (the stack reverses the order) | |||
| B530 | INY ; Step source | |||
| B531 | CPY #&20 ; Reached &20 (one past the 8-byte name)? | |||
| B533 | BNE loop_push_ps_name ; No: continue pushing | |||
| B535 | LDY #&17 ; Y=&17: destination offset for the reversed name End of TX name field at &1B | |||
| B537 | .loop_pop_ps_name←1← B53D BNE | |||
| PLA ; Pull next pushed byte (LIFO -> reversed order) Pop byte (reversed order) | ||||
| B538 | STA (net_rx_ptr),y ; Store at destination offset Y | |||
| B53A | DEY ; Step destination back | |||
| B53B | CPY #&0f ; Reached &0F (one before the destination range)? Start of TX field (&0F)? | |||
| B53D | BNE loop_pop_ps_name ; No: continue popping | |||
| B53F | LDA net_rx_ptr_hi ; Copy net_rx_ptr_hi as the TX page (TX shares the same page as RX for this packet) Copy RX page to TX | |||
| B541 | STA net_tx_ptr_hi ; Set net_tx_ptr_hi | |||
| B543 | LDA #&0c ; TX low byte = &0C: skip past the TX header to where the reversed name lives TX offset &10 | |||
| B545 | STA net_tx_ptr ; Set net_tx_ptr lo | |||
| B547 | LDY #3 ; Y=3: copy 4-byte TX header (offsets 3..0) | |||
| B549 | .loop_copy_tx_hdr←1← B54F BPL | |||
| LDA ps_tx_header_template,y ; Read template byte | ||||
| B54C | STA (net_tx_ptr),y ; Write to TX buffer at offset Y | |||
| B54E | DEY ; Step backwards | |||
| B54F | BPL loop_copy_tx_hdr ; Loop while Y >= 0 | |||
| B551 | RTS ; Return | |||
Printer-server TX header template (4 bytes)Four bytes copied to the head of the printer-server transmit
buffer by |
|
| B552 | .ps_tx_header_template←1← B549 LDA |
| EQUB &80 ; Control byte &80 (immediate TX) | |
| B553 | EQUB &9F ; Port &9F (printer server) |
| B554 | EQUB &FF ; Station &FF (any) |
| B555 | 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.
|
|||||||
| B556 | .print_station_addr←4← A3BE JSR← B607 JSR← B63F JSR← B697 JSR | ||||||
| PHP ; Save caller's V (controls leading-zero padding via the BVS at &B566) Save V flag (controls padding) | |||||||
| B557 | LDA fs_work_6 ; Read network number (fs_work_6) | ||||||
| B559 | BEQ skip_if_local_net ; Network 0 means local: skip the 'NN.' prefix | ||||||
| B55B | JSR print_decimal_3dig ; Network non-zero: print as 3-digit decimal | ||||||
| B55E | LDA #&2e ; A='.': separator between network and station '.' separator | ||||||
| B560 | JSR osasci ; Print the dot | ||||||
| B563 | BIT always_set_v_byte ; Set V so the next BVS branches over the padding (we just printed digits, no padding needed) Set V (suppress station padding) | ||||||
| B566 | .skip_if_local_net←1← B559 BEQ | ||||||
| BVS local_net_prefix ; V set: skip leading-space padding | |||||||
| B568 | JSR print_inline ; V clear (caller wanted padding): print 4 leading spaces via inline string Print 4 spaces (padding) | ||||||
| B56B | EQUS " " | ||||||
| B56F | .local_net_prefix←1← B566 BVS | ||||||
| LDA fs_work_5 ; Read station number (fs_work_5) | |||||||
| B571 | PLP ; Restore caller's V (so print_decimal_3dig honours its own leading-zero suppression) Restore flags | ||||||
| B572 | JMP print_decimal_3dig ; Tail-call print_decimal_3dig for the station number Print station as 3 digits | ||||||
Printer-server slot TXCB template (12 bytes)12-byte Econet TXCB template for printer-server slot buffers.
Copied by Structure: 4-byte header (control, port, station, network)
followed by two 4-byte buffer descriptors (lo address, hi page,
end lo, end hi). End bytes |
|
| B575 | EQUB &80 ; Offset 0: txcb_ctrl = &80 (standard) |
| B576 | EQUB &9F ; Offset 1: txcb_port = &9F (PS port) |
| B577 | EQUB &00 ; Offset 2: dest station (placeholder, &00) |
| B578 | EQUB &00 ; Offset 3: dest network (placeholder, &00) |
| B579 | EQUB &10 ; Offset 4: buf1 start lo = &10 |
| B57A | EQUB &00 ; Offset 5: buf1 start hi (page from net_rx_ptr) |
| B57B | EQUB &FF ; Offset 6: buf1 end lo placeholder = &FF |
| B57C | EQUB &FF ; Offset 7: buf1 end hi placeholder = &FF |
| B57D | EQUB &18 ; Offset 8: buf2 start lo = &18 |
| B57E | EQUB &00 ; Offset 9: buf2 start hi (page from net_rx_ptr) |
| B57F | EQUB &FF ; Offset 10: buf2 end lo placeholder = &FF |
| B580 | EQUB &FF ; Offset 11: buf2 end hi placeholder = &FF |
*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.
|
||||
| B581 | .cmd_pollps | |||
| STY ws_ptr_hi ; Save command line pointer high | ||||
| B583 | JSR init_spool_drive ; Initialise spool/print drive | |||
| B586 | STA fs_work_4 ; Save spool drive number | |||
| B588 | JSR reverse_ps_name_to_tx ; Copy PS name to TX buffer | |||
| B58B | JSR init_ps_slot_from_rx ; Init PS slot from RX data | |||
| B58E | LDY ws_ptr_hi ; Restore command line pointer | |||
| B590 | JSR save_ptr_to_spool_buf ; Save pointer to spool buffer | |||
| B593 | LDA (fs_options),y ; Get first argument character | |||
| B595 | CMP #&0d ; End of command line? | |||
| B597 | BEQ no_poll_name_given ; Yes: no argument given | |||
| B599 | CLV ; Clear V (= explicit PS name given) | |||
| B59A | JSR is_decimal_digit ; Is first char a decimal digit? | |||
| B59D | BCC skip_if_no_poll_arg ; Yes: station number, skip PS name | |||
| B59F | TYA ; PS name follows | |||
| B5A0 | PHA ; Save Y | |||
| B5A1 | JSR load_ps_server_addr ; Load PS server address | |||
| B5A4 | PLA ; Restore Y | |||
| B5A5 | TAY ; Back to Y register | |||
| B5A6 | JSR parse_fs_ps_args ; Parse FS/PS arguments | |||
| B5A9 | LDY #&7a ; Offset &7A in slot buffer | |||
| B5AB | LDA fs_work_5 ; Get parsed station low | |||
| B5AD | STA (work_ae),y ; Store station number low | |||
| B5AF | INY ; Y=&7b | |||
| B5B0 | LDA fs_work_6 ; Get parsed network number | |||
| B5B2 | STA (work_ae),y ; Store station number high | |||
| B5B4 | LDY #&10 ; Offset &14 in TX buffer | |||
| B5B6 | JSR copy_ps_data ; Copy PS data to TX buffer | |||
| B5B9 | LDA addr_work ; Get buffer page high | |||
| B5BB | STA net_tx_ptr_hi ; Set TX pointer high byte | |||
| B5BD | LDA #&78 ; Offset &78 in buffer | |||
| B5BF | STA net_tx_ptr ; Set TX pointer low byte | |||
| B5C1 | BNE done_poll_name_parse ; ALWAYS branch | |||
| B5C3 | .no_poll_name_given←1← B597 BEQ | |||
| BIT always_set_v_byte ; Set V (= no explicit PS name) | ||||
| B5C6 | .skip_if_no_poll_arg←1← B59D BCC | |||
| BVS done_poll_name_parse ; V set (no arg): skip to send | ||||
| B5C8 | LDX #6 ; Max 6 characters for PS name | |||
| B5CA | LDY #&10 ; Buffer offset for PS name | |||
| B5CC | LDA #&20 ; Space character | |||
| B5CE | .loop_pad_poll_name←1← B5D2 BNE | |||
| STA (net_rx_ptr),y ; Fill buffer position with space | ||||
| B5D0 | INY ; Next position | |||
| B5D1 | DEX ; Count down | |||
| B5D2 | BNE loop_pad_poll_name ; Loop until 6 spaces filled | |||
| B5D4 | JSR save_ptr_to_os_text ; Save pointer to OS text | |||
| B5D7 | LDY ws_ptr_hi ; Restore command line pointer | |||
| B5D9 | JSR gsinit ; Initialise string reading | |||
| B5DC | BEQ done_poll_name_parse ; Empty string: skip to send | |||
| B5DE | LDX #6 ; Max 6 characters | |||
| B5E0 | STY ws_ptr_hi ; Save updated string pointer | |||
| B5E2 | LDY #&10 ; Buffer offset for PS name | |||
| B5E4 | STY table_idx ; Save buffer position | |||
| B5E6 | .loop_read_poll_char←1← B5F2 BNE | |||
| LDY ws_ptr_hi ; Restore string pointer | ||||
| B5E8 | JSR gsread ; Read next char from string | |||
| B5EB | STY ws_ptr_hi ; Save updated string pointer | |||
| B5ED | BCS done_poll_name_parse ; End of string: go to send | |||
| B5EF | JSR store_char_uppercase ; Store char uppercased in buffer | |||
| B5F2 | BNE loop_read_poll_char ; Loop if more chars to copy | |||
| B5F4 | .done_poll_name_parse←4← B5C1 BNE← B5C6 BVS← B5DC BEQ← B5ED BCS | |||
| LDA #&80 ; Enable escape checking | ||||
| B5F6 | STA need_release_tube ; Set escapable flag | |||
| B5F8 | JSR send_net_packet ; Send the poll request packet | |||
| B5FB | JSR pop_requeue_ps_scan ; Pop and requeue PS scan | |||
| B5FE | JSR print_printer_server_is ; Print 'Printer server ' | |||
| B601 | JSR load_ps_server_addr ; Load PS server address | |||
| B604 | BIT always_set_v_byte ; Set V and N flags | |||
| B607 | JSR print_station_addr ; Print station address | |||
| B60A | JSR print_inline ; Print ' "' | |||
| B60D | EQUS " "" | |||
| B60F | LDY #&18 ; Y=&18: name field offset in RX buffer | |||
| B611 | .loop_print_poll_name←1← B61D BNE | |||
| LDA (net_rx_ptr),y ; Get character from name field | ||||
| B613 | CMP #&20 ; Is it a space? | |||
| B615 | BEQ done_poll_name_print ; Yes: end of name | |||
| B617 | JSR osasci ; Write character | |||
| B61A | INY ; Next character | |||
| B61B | CPY #&1e ; Past end of name field? | |||
| B61D | BNE loop_print_poll_name ; No: continue printing name | |||
| B61F | .done_poll_name_print←1← B615 BEQ | |||
| JSR print_inline ; Print '"' + CR | ||||
| B622 | EQUS ""." | |||
| B624 | NOP ; Bit-7 terminator from preceding stringhi | |||
| B625 | .loop_pollps_next_slot←1← B6A3 BNE | |||
| PLA ; Pop saved slot index | ||||
| B626 | BEQ return_from_poll_slots ; Zero: all slots done, return | |||
| B628 | PHA ; Save slot offset | |||
| B629 | TAY ; Transfer to Y | |||
| B62A | LDA (nfs_workspace),y ; Read slot status byte | |||
| B62C | BPL done_poll_slot_mark ; Bit 7 clear: slot inactive | |||
| B62E | INY ; Advance to station number | |||
| B62F | INY ; Offset+2 in slot | |||
| B630 | LDA (nfs_workspace),y ; Read station number low | |||
| B632 | STA fs_work_5 ; Store station low | |||
| B634 | INY ; Next byte (offset+3) | |||
| B635 | LDA (nfs_workspace),y ; Read network number | |||
| B637 | STA fs_work_6 ; Store network number | |||
| B639 | INY ; Next byte (offset+4) | |||
| B63A | LDA (nfs_workspace),y ; Read status page pointer | |||
| B63C | STA work_ae ; Store pointer low | |||
| B63E | CLV ; Clear V flag | |||
| B63F | JSR print_station_addr ; Print station address (V=0) | |||
| B642 | JSR print_inline ; Print ' is ' | |||
| B645 | EQUS " is " | |||
| B649 | LDX #0 ; X=0: indexed-indirect access mode | |||
| B64B | LDA (work_ae,x) ; Read printer status byte | |||
| B64D | BNE check_poll_jammed ; Non-zero: not ready | |||
| B64F | JSR print_inline ; Print 'ready' | |||
| B652 | EQUS "ready" | |||
| B657 | CLV ; Ensure V clear so next BVC always taken | |||
| B658 | BVC done_poll_status_line ; ALWAYS branch | |||
| B65A | .check_poll_jammed←1← B64D BNE | |||
| CMP #2 ; Status = 2? | ||||
| B65C | BNE check_poll_busy ; No: check for busy | |||
| B65E | .print_poll_jammed←1← B66C BNE | |||
| JSR print_inline ; Print 'jammed' | ||||
| B661 | EQUS "jammed" | |||
| B667 | CLV ; Clear V | |||
| B668 | BVC done_poll_status_line ; ALWAYS branch | |||
| B66A | .check_poll_busy←1← B65C BNE | |||
| CMP #1 ; Status = 1? | ||||
| B66C | BNE print_poll_jammed ; Not 1 or 2: default to jammed | |||
| B66E | JSR print_inline ; Print 'busy' | |||
| B671 | EQUS "busy" | |||
| B675 | INC work_ae ; Advance work_ae to next status byte (lo) | |||
| B677 | LDA (work_ae,x) ; Read client station number | |||
| B679 | STA fs_work_5 ; Store station low | |||
| B67B | BEQ done_poll_status_line ; Zero: no client info, skip | |||
| B67D | JSR print_inline ; Print ' with station ' | |||
| B680 | EQUS " with station " | |||
| B68E | INC work_ae ; Advance work_ae to next status byte (lo) | |||
| B690 | LDA (work_ae,x) ; Read network number byte via (work_ae,X) | |||
| B692 | STA fs_work_6 ; Store network number | |||
| B694 | BIT always_set_v_byte ; Set V flag | |||
| B697 | JSR print_station_addr ; Print client station address | |||
| B69A | .done_poll_status_line←3← B658 BVC← B668 BVC← B67B BEQ | |||
| JSR osnewl ; Write newline (characters 10 and 13) | ||||
| B69D | .done_poll_slot_mark←1← B62C BPL | |||
| PLA ; Retrieve slot offset | ||||
| B69E | TAY ; Transfer to Y | |||
| B69F | LDA #&3f ; Mark slot as processed (&3F) | |||
| B6A1 | STA (nfs_workspace),y ; Write marker to workspace | |||
| B6A3 | BNE loop_pollps_next_slot ; ALWAYS branch | |||
| B6A5 | .return_from_poll_slots←1← B626 BEQ | |||
| RTS ; Return | ||||
Initialise PS slot buffer from template dataCopies the 12-byte
|
||||
| B6A6 | .init_ps_slot_from_rx←1← B58B JSR | |||
| LDY #&78 ; Start at offset &78 | ||||
| B6A8 | .loop_copy_slot_tmpl←1← B6BA BNE | |||
| LDA ps_print_template,y ; Load template byte | ||||
| B6AB | CPY #&7d ; At offset &7D? | |||
| B6AD | BEQ subst_rx_page_byte ; Yes: substitute RX page | |||
| B6AF | CPY #&81 ; At offset &81? | |||
| B6B1 | BNE store_slot_tmpl_byte ; No: use template byte | |||
| B6B3 | .subst_rx_page_byte←1← B6AD BEQ | |||
| LDA net_rx_ptr_hi ; Use RX buffer page instead | ||||
| B6B5 | .store_slot_tmpl_byte←1← B6B1 BNE | |||
| STA (work_ae),y ; Store byte in slot buffer | ||||
| B6B7 | INY ; Next offset | |||
| B6B8 | CPY #&84 ; Past end of slot (&84)? | |||
| B6BA | BNE loop_copy_slot_tmpl ; No: continue copying | |||
| B6BC | 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.
|
||||
| B6BD | .store_char_uppercase←2← B411 JSR← B5EF JSR | |||
| LDY table_idx ; Y = current buffer position | ||||
| B6BF | AND #&7f ; Strip high bit | |||
| B6C1 | CMP #&61 ; Is it lowercase 'a' or above? | |||
| B6C3 | BCC done_uppercase_store ; Below 'a': not lowercase | |||
| B6C5 | CMP #&7b ; Above 'z'? | |||
| B6C7 | BCS done_uppercase_store ; Yes: not lowercase | |||
| B6C9 | AND #&5f ; Convert to uppercase | |||
| B6CB | .done_uppercase_store←2← B6C3 BCC← B6C7 BCS | |||
| STA (net_rx_ptr),y ; Store in RX buffer | ||||
| B6CD | INY ; Next buffer position | |||
| B6CE | STY table_idx ; Update buffer position | |||
| B6D0 | DEX ; Decrement character count | |||
| B6D1 | RTS ; Return (Z set if count=0) | |||
*Prot command handlerLoads
The ANFS protection state lives in CMOS bit 6 of byte
|
||||
| B6D2 | .cmd_prot | |||
| LDA #&ff ; Load &FF (protect) | ||||
| B6D4 | BNE unprot_clear ; ALWAYS branch | |||
*Unprot command handlerLoads
|
||||
| B6D6 | .cmd_unprot | |||
| LDA #0 ; Load &00 (unprotect) | ||||
| B6D8 | .unprot_clear←1← B6D4 BNE | |||
| PHP ; Save Z flag (1 = unprot, 0 = prot) for later | ||||
| B6D9 | JSR set_via_shadow_pair ; Mirror A into shadow ACR / shadow IER | |||
| B6DC | LDX #&11 ; X=&11: CMOS offset for Econet flags | |||
| B6DE | JSR osbyte_a1 ; OSBYTE &A1 reads CMOS byte &11 -> Y | |||
| B6E1 | TYA ; A = current CMOS byte | |||
| B6E2 | PLP ; Restore the saved Z flag | |||
| B6E3 | BEQ unprot_check ; Z=1: unprot path | |||
| B6E5 | ORA #&40 ; Set bit 6 (protection on) | |||
| B6E7 | BNE unprot_apply ; ALWAYS branch to write-back | |||
| B6E9 | .unprot_check←1← B6E3 BEQ | |||
| AND #&bf ; Clear bit 6 (protection off) | ||||
| B6EB | .unprot_apply←1← B6E7 BNE | |||
| TAY ; Y = new flag byte | ||||
| B6EC | LDA #&a2 ; OSBYTE &A2: write CMOS byte | |||
| B6EE | .loop_match_prot_attr | |||
| LDX #&11 ; X=&11: CMOS offset for Econet flags | ||||
| B6F0 | JMP osbyte ; Tail-call OSBYTE | |||
*Wipe command handlerSetup half of *Wipe. Masks owner access via
|
||||
| B6F3 | .cmd_wipe | |||
| JSR mask_owner_access ; Reset access flags before parsing the new argument | ||||
| B6F6 | LDA #0 ; A=0: clear the file-iteration counter | |||
| B6F8 | STA fs_work_5 ; Store iteration counter (steps to next file each loop) | |||
| B6FA | JSR save_ptr_to_os_text ; Save text pointer for re-reading the wildcard each iteration | |||
| B6FD | JSR parse_filename_arg ; Parse the wildcard filename into the &C030 buffer | |||
| B700 | INX ; Step X past the CR terminator (so X = filename length+1) | |||
| B701 | STX fs_work_6 ; Save end-of-buffer offset | |||
| fall through ↓ | ||||
Build 'examine directory' TXCB for next wipe iterationIssues FS function-code 1 ('examine directory entry') for the current iteration in fs_work_5. Writes the function code into TXCB[5] and TXCB[7], copies the iteration index to TXCB[6], and falls through to the TXCB-build / send sequence. Single caller (the BNE retry at &B73F that loops cmd_wipe over each match). |
|
| B703 | .request_next_wipe←1← B73F JMP |
| LDA #1 ; FS function code byte 0 = 1 (examine) | |
| B705 | STA hazel_txcb_data ; TXCB[5] = 1: 'examine directory entry' |
| B708 | STA hazel_txcb_count ; TXCB[7] = 1: ditto for the second buffer slot |
| B70B | LDX fs_work_5 ; Load current iteration index |
| B70D | STX hazel_txcb_flag ; TXCB[6] = iteration index (which directory entry) |
| B710 | LDX #3 ; X=3: copy starting at TX[3] (after the FS header bytes) |
| B712 | JSR copy_arg_to_buf ; Copy the parsed filename into the TX buffer |
| B715 | LDY #3 ; Y=3: FS function code 'Examine' |
| B717 | LDA #&80 ; A=&80: set bit 7 of need_release_tube to flag long-lived TX |
| B719 | STA need_release_tube ; Store flag |
| B71B | JSR save_net_tx_cb ; Send the examine request and wait for reply |
| B71E | LDA hazel_txcb_data ; Read FS reply byte 0 (status code) |
| B721 | BNE check_wipe_attr ; Non-zero status: process the response |
| B723 | LDA #osbyte_flush_buffer_class ; OSBYTE &0F: flush input buffer class |
| B725 | LDX #1 ; X=1: flush keyboard buffer |
| B727 | JSR osbyte ; Flush keyboard buffer (clear pending Y/N keypress) |
| B72A | LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: scan keyboard from key 16 (clear keypress queue) |
| B72C | JSR osbyte ; Run the scan |
| B72F | LDY #0 ; Y=0: no key |
| B731 | LDA #osbyte_write_keys_pressed ; OSBYTE &78: write keys-pressed state |
| B733 | JMP osbyte ; Tail-call OSBYTE: clean up and return |
| B736 | .check_wipe_attr←1← B721 BNE |
| LDA hazel_txcb_end ; Read attribute byte from FS reply (TXCB[&2F]) | |
| B739 | .loop_check_if_locked←1← B749 BNE |
| CMP #&4c ; Is it 'L' (locked)? | |
| B73B | BNE check_wipe_dir ; Not locked: check for directory |
| B73D | .skip_wipe_locked←1← B7BA JMP |
| INC fs_work_5 ; Locked: skip this file, advance to next | |
| B73F | JMP request_next_wipe ; Loop back to request the next directory entry |
| B742 | .check_wipe_dir←1← B73B BNE |
| CMP #&44 ; Is it 'D' (directory)? | |
| B744 | BNE show_wipe_prompt ; Not a directory: prompt the user |
| B746 | LDA hazel_examine_attr ; Directory: check second attribute byte (size) |
| B749 | BNE loop_check_if_locked ; Loop back to attribute test (re-checks if non-empty) |
| B74B | .show_wipe_prompt←1← B744 BNE |
| LDX #1 ; X=1: scan name starting at TX[1] | |
| B74D | LDY fs_work_6 ; Y = end-of-buffer offset (saved earlier in fs_work_6) |
| B74F | .loop_copy_wipe_name←1← B75C BNE |
| LDA hazel_txcb_flag,x ; Read filename byte from TX[6+X] | |
| B752 | JSR print_char_no_spool ; Print via *SPOOL-bypassing OSASCI |
| B755 | STA hazel_parse_buf,y ; Also store into the parse buffer for later use |
| B758 | INY ; Step parse-buffer offset |
| B759 | INX ; Step TX-buffer offset |
| B75A | CPX #&0c ; Reached &0C (12 chars)? |
| B75C | BNE loop_copy_wipe_name ; No: continue copying |
| B75E | JSR print_inline_no_spool ; Print '(?/' prompt prefix and read response |
| B761 | EQUS "(?/" ; Inline string '(?/' is read by the hook above |
| B764 | NOP ; NOP -- bit-7 terminator + resume opcode for the '(?/' stringhi |
| B765 | JSR prompt_yn ; Print 'Y/N) ' via prompt_yn (reads keypress) |
| B768 | CMP #&3f ; Was the keypress '?' (help)? |
| B76A | BNE check_wipe_response ; Not '?': process Y/N response |
| B76C | LDA #&0d ; '?': print CR before help text |
| B76E | JSR print_byte_no_spool ; Print CR character |
| B771 | LDX #2 ; X=2: start of name in TX[2] |
| B773 | .loop_print_wipe_info←1← B77C BNE |
| LDA hazel_txcb_data,x ; Read name byte from TX[5+X] (FS reply) | |
| B776 | JSR print_char_no_spool ; Print name char (no spool) |
| B779 | INX ; Advance index |
| B77A | CPX #&3e ; End of TX[5+X] name field at offset &3E? |
| B77C | BNE loop_print_wipe_info ; No: continue printing |
| B77E | JSR print_inline_no_spool ; Print 'Wipe? ' help suffix via inline string |
| B781 | EQUS " (" |
| B783 | NOP ; Bit-7 terminator + resume |
| B784 | JSR prompt_yn ; Re-prompt user with prompt_yn |
| B787 | .check_wipe_response←1← B76A BNE |
| AND #&df ; Mask to upper-case ('A'..'Z' map to themselves) | |
| B789 | CMP #&59 ; Was the response 'Y'? |
| B78B | BNE skip_wipe_to_next ; No: skip this entry, advance to next |
| B78D | JSR print_char_no_spool ; Yes: echo the keypress |
| B790 | LDX #0 ; X=0: start scanning the parse-buffer name |
| B792 | LDA hazel_parse_buf,x ; Read first parse-buffer byte at hazel_parse_buf |
| B795 | CMP #&0d ; Is it CR (no path component)? |
| B797 | BEQ use_wipe_leaf_name ; Yes: use leaf-name only path at &B7BD |
| B799 | .loop_build_wipe_cmd←1← B7AE BNE |
| LDA hazel_parse_buf,x ; Read parse-buffer byte at hazel_parse_buf+X | |
| B79C | CMP #&0d ; Is it CR (end of name)? |
| B79E | BNE skip_if_not_space ; No: check for space separator |
| B7A0 | LDA #&2e ; CR: substitute '.' so the dir prefix terminates with a separator |
| B7A2 | .skip_if_not_space←1← B79E BNE |
| CMP #&20 ; Is it space? | |
| B7A4 | BNE store_wipe_tx_char ; No: store byte as-is |
| B7A6 | .set_wipe_cr_end←1← B7C9 BEQ |
| LDA #&0d ; Yes: substitute CR (end-of-cmd) | |
| B7A8 | .store_wipe_tx_char←1← B7A4 BNE |
| STA hazel_txcb_data,x ; Store byte into TX[5+X] (delete-command buffer) | |
| B7AB | INX ; Advance index |
| B7AC | CMP #&0d ; Was that byte CR (just stored)? |
| B7AE | BNE loop_build_wipe_cmd ; No: continue copying |
| B7B0 | LDY #&14 ; Y=&14: FS function code &14 = delete |
| B7B2 | JSR save_net_tx_cb ; Send the delete request and wait for reply |
| B7B5 | DEC fs_work_5 ; Decrement iteration counter so we re-examine the now-shifted-up slot |
| B7B7 | .skip_wipe_to_next←1← B78B BNE |
| JSR print_newline_no_spool ; Print newline before next entry | |
| B7BA | JMP skip_wipe_locked ; Loop back to skip_wipe_locked (= request next entry) |
| B7BD | .use_wipe_leaf_name←1← B797 BEQ |
| DEX ; DEX: pre-decrement before the INX in the loop | |
| B7BE | .loop_copy_wipe_leaf←1← B7C7 BNE |
| INX ; Advance index | |
| B7BF | LDA hazel_parse_buf_1,x ; Read parse-buffer byte at hazel_parse_buf_1+X (skip CR at hazel_parse_buf) |
| B7C2 | STA hazel_txcb_data,x ; Store into TX[5+X] (delete-command buffer) |
| B7C5 | CMP #&20 ; Reached space (end-of-leaf)? |
| B7C7 | BNE loop_copy_wipe_leaf ; No: continue copying |
| B7C9 | BEQ set_wipe_cr_end ; 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.
|
||||
| B7CB | .prompt_yn←2← B765 JSR← B784 JSR | |||
| JSR print_inline_no_spool ; Print 'Y/N) ' via the inline-string helper | ||||
| B7CE | EQUS "Y/N) " ; Inline string body — bytes consumed by print_inline_no_spool (above) | |||
| 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).
|
||||||
| B7D3 | .flush_and_read_char | |||||
| LDA #osbyte_flush_buffer_class ; OSBYTE &0F: flush buffer class | ||||||
| B7D5 | LDX #1 ; X=1: flush input buffers | |||||
| B7D7 | JSR osbyte ; Flush keyboard buffer before read | |||||
| B7DA | JSR osrdch ; Read character from input stream | |||||
| B7DD | BCC return_6 ; C clear: character read OK | |||||
| B7DF | JMP raise_escape_error ; Escape pressed: raise error | |||||
| B7E2 | .return_6←1← B7DD 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).
|
||||
| B7E3 | .init_channel_table←1← 8B9F JSR | |||
| LDA #0 ; A=0: clear value | ||||
| B7E5 | TAY ; Y=0: start index | |||
| B7E6 | .loop_clear_chan_table←1← B7EA BNE | |||
| STA hazel_fcb_addr_lo,y ; Clear channel table entry | ||||
| B7E9 | INY ; Next entry | |||
| B7EA | BNE loop_clear_chan_table ; Loop until all 256 bytes cleared | |||
| B7EC | LDY #&0b ; Offset &0F in receive buffer | |||
| B7EE | LDA (net_rx_ptr),y ; Get number of available channels | |||
| B7F0 | SEC ; Prepare subtraction | |||
| B7F1 | SBC #&0c ; Subtract 'Z' to get negative count | |||
| B7F3 | TAY ; Y = negative channel count (index) | |||
| B7F4 | LDA #&40 ; Channel marker &40 (available) | |||
| B7F6 | .loop_mark_chan_avail←1← B7FC BPL | |||
| STA hazel_fcb_addr_lo,y ; Mark channel slot as available | ||||
| B7F9 | DEY ; Previous channel slot | |||
| B7FA | CPY #&b8 ; Reached start of channel range? | |||
| B7FC | BPL loop_mark_chan_avail ; No: continue marking channels | |||
| B7FE | INY ; Point to first channel slot | |||
| B7FF | LDA #&c0 ; Active channel marker &C0 | |||
| B801 | STA hazel_fcb_addr_lo,y ; Mark first channel as active | |||
| B804 | 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.
|
|||||||
| B805 | .attr_to_chan_index←5← 93FE JSR← 9414 JSR← 9F5B JSR← 9F99 JSR← BADC JSR | ||||||
| PHP ; Save flags | |||||||
| B806 | SEC ; Prepare subtraction | ||||||
| B807 | SBC #&20 ; Subtract &20 to get table index | ||||||
| B809 | BMI error_chan_out_of_range ; Negative: out of valid range | ||||||
| B80B | CMP #&10 ; Above maximum channel index &0F? | ||||||
| B80D | BCC return_chan_index ; In range: valid index | ||||||
| B80F | .error_chan_out_of_range←1← B809 BMI | ||||||
| LDA #&ff ; Out of range: return &FF (invalid) | |||||||
| B811 | .return_chan_index←1← B80D BCC | ||||||
| PLP ; Restore flags | |||||||
| B812 | TAX ; X = channel index (or &FF) | ||||||
| B813 | 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.
|
||||
| B814 | .check_chan_char←2← A084 JSR← B88C JSR | |||
| CMP #&20 ; Below space? | ||||
| B816 | BCC err_net_chan_invalid ; Yes: invalid channel character | |||
| B818 | CMP #&30 ; Below '0'? | |||
| B81A | BCC lookup_chan_by_char ; In range &20-&2F: look up channel | |||
| fall through ↓ | ||||
Raise 'Net channel' error (saving channel char on stack)Pushes the bad channel character on the stack, then falls through to error_chan_not_found which loads error code &DE and tail-calls error_inline_log with the inline string 'Net channel'. The PHA at entry differs from the &B81D error_chan_not_found alt-entry: this form is reached when the caller has the channel character in A and wants it preserved on the stack for the error handler to inspect. Never returns -- error_inline_log triggers a BRK.
|
||||
| B81C | .err_net_chan_invalid←2← 9EC2 JMP← B816 BCC | |||
| PHA ; Save channel character | ||||
| B81D | .error_chan_not_found←1← B84F BEQ | |||
| LDA #&de ; Error code &DE | ||||
| B81F | .err_net_chan_not_found | |||
| JSR error_inline_log ; Generate 'Net channel' error | ||||
| B822 | EQUS "Net channel." | |||
| B82E | JSR false_ref_6f6e ; Error string continuation (unreachable) | |||
| B831 | STZ tx_buffer_scratch,x ; Clear tx_buffer_scratch+X scratch | |||
| B833 | EQUS "on this file server" | |||
| B846 | EQUB &00 | |||
| fall through ↓ | ||||
Look up channel by character codeSubtracts
|
|||||||
| B847 | .lookup_chan_by_char←2← A1F3 JSR← B81A BCC | ||||||
| PHA ; Save channel character | |||||||
| B848 | SEC ; Prepare subtraction | ||||||
| B849 | SBC #&20 ; Convert char to table index | ||||||
| B84B | TAX ; X = channel table index | ||||||
| B84C | LDA hazel_fcb_slot_attr,x ; Look up network number for channel | ||||||
| B84F | BEQ error_chan_not_found ; Zero: channel not found, raise error | ||||||
| B851 | JSR match_station_net ; Check station/network matches current | ||||||
| B854 | BNE error_chan_not_here ; No match: build detailed error msg | ||||||
| B856 | PLA ; Discard saved channel character | ||||||
| B857 | LDA hazel_fcb_status,x ; Load channel status flags | ||||||
| B85A | RTS ; Return; A = channel flags | ||||||
| B85B | .error_chan_not_here←1← B854 BNE | ||||||
| LDA #&de ; Error code &DE | |||||||
| B85D | STA error_text ; Store error code in error block | ||||||
| B860 | LDA #0 ; BRK opcode | ||||||
| B862 | STA error_block ; Store BRK at start of error block | ||||||
| B865 | TAX ; X=0: copy index | ||||||
| B866 | .loop_copy_chan_err_str←1← B86D BNE | ||||||
| INX ; Advance copy position | |||||||
| B867 | LDA net_chan_err_strings,x ; Load 'Net channel' string byte | ||||||
| B86A | STA error_text,x ; Copy to error text | ||||||
| B86D | BNE loop_copy_chan_err_str ; Continue until NUL terminator | ||||||
| B86F | STX fs_load_addr_2 ; Save end-of-string position | ||||||
| B871 | STX fs_work_4 ; Save for suffix append | ||||||
| B873 | PLA ; Retrieve channel character | ||||||
| B874 | JSR append_space_and_num ; Append ' N' (channel number) | ||||||
| B877 | LDY fs_work_4 ; Load 'Net channel' end position | ||||||
| B879 | .loop_append_err_suffix←1← B881 BNE | ||||||
| INY ; Skip past NUL to suffix string | |||||||
| B87A | INX ; Advance destination position | ||||||
| B87B | LDA net_chan_err_strings,y ; Load ' not on this...' suffix byte | ||||||
| B87E | STA error_text,x ; Append to error message | ||||||
| B881 | BNE loop_append_err_suffix ; Continue until NUL | ||||||
| B883 | 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.
|
||||
| B886 | .store_result_check_dir←2← BB6D JSR← BBF3 JSR | |||
| LDA hazel_chan_attr ; Load current channel attribute | ||||
| B889 | 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.
|
||||
| B88C | .check_not_dir←2← 9EF5 JSR← A170 JSR | |||
| JSR check_chan_char ; Validate and look up channel | ||||
| B88F | AND #2 ; Test directory flag (bit 1) | |||
| B891 | BEQ return_7 ; Not a directory: return OK | |||
| B893 | LDA #&a8 ; Error code &A8 | |||
| B895 | JSR error_inline_log ; Generate 'Is a dir.' error | |||
| B898 | EQUS "Is a directory." | |||
| B8A7 | .return_7←1← B891 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.
|
||||||
| B8A8 | .alloc_fcb_slot←7← A02A JSR← A05F JSR← A5C6 JSR← A663 JSR← A68E JSR← A6C5 JSR← B8DF JSR | |||||
| PHA ; Save channel attribute | ||||||
| B8A9 | LDX #&20 ; Start scanning from FCB slot &20 | |||||
| B8AB | .loop_scan_fcb_slots←1← B8B3 BNE | |||||
| LDA hazel_fcb_addr_mid,x ; Load FCB station byte | ||||||
| B8AE | BEQ done_found_free_slot ; Zero: slot is free, use it | |||||
| B8B0 | INX ; Try next slot | |||||
| B8B1 | CPX #&30 ; Past last FCB slot &2F? | |||||
| B8B3 | BNE loop_scan_fcb_slots ; No: check next slot | |||||
| B8B5 | PLA ; No free slot: discard saved attribute | |||||
| B8B6 | LDA #0 ; A=0: return failure (Z set) | |||||
| B8B8 | RTS ; Return | |||||
| B8B9 | .done_found_free_slot←1← B8AE BEQ | |||||
| PLA ; Restore channel attribute | ||||||
| B8BA | STA hazel_fcb_addr_mid,x ; Store attribute in FCB slot | |||||
| B8BD | LDA #0 ; A=0: clear value | |||||
| B8BF | STA hazel_fcb_addr_lo_minus20,x ; Clear FCB transfer count low | |||||
| B8C2 | STA hazel_fcb_addr_mid_minus20,x ; Clear FCB transfer count mid | |||||
| B8C5 | STA hazel_fcb_addr_lo,x ; Clear FCB transfer count high | |||||
| B8C8 | LDA hazel_fs_station ; Load current station number | |||||
| B8CB | STA hazel_fcb_addr_hi,x ; Store station in FCB | |||||
| B8CE | LDA hazel_fs_network ; Load current network number | |||||
| B8D1 | STA hazel_fcb_slot_attr,x ; Store network in FCB | |||||
| B8D4 | TXA ; Get FCB slot index | |||||
| B8D5 | PHA ; Save slot index | |||||
| B8D6 | SEC ; Prepare subtraction | |||||
| B8D7 | SBC #&20 ; Convert slot to channel index (0-&0F) | |||||
| B8D9 | TAX ; X = channel index | |||||
| B8DA | PLA ; Restore A = FCB slot index | |||||
| B8DB | 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.
|
|||||||||
| B8DC | .alloc_fcb_or_error←2← 9FD9 JSR← A522 JSR | ||||||||
| PHA ; Save argument | |||||||||
| B8DD | LDA #0 ; A=0: allocate any available slot | ||||||||
| B8DF | JSR alloc_fcb_slot ; Try to allocate an FCB slot | ||||||||
| B8E2 | BNE return_alloc_success ; Success: slot allocated | ||||||||
| B8E4 | LDA #&c0 ; Error code &C0 | ||||||||
| B8E6 | JSR error_inline_log ; Generate 'No more FCBs' error | ||||||||
| B8E9 | EQUS "No more FCBs." | ||||||||
| B8F6 | .return_alloc_success←1← B8E2 BNE | ||||||||
| PLA ; Restore argument | |||||||||
| B8F7 | 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.
|
||||
| B8F8 | .close_all_net_chans←3← 9785 JSR← 9813 JSR← A6D5 JSR | |||
| CLC ; C=0: close all matching channels | ||||
| B8F9 | .skip_set_carry | |||
| BIT always_set_v_byte ; Branch always to scan entry | ||||
| 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.
|
||||||
| B8FC | .scan_fcb_flags←1← A099 JSR | |||||
| LDX #&10 ; Start from FCB slot &10 | ||||||
| B8FE | .loop_scan_fcb_down←4← B90A BVC← B914 BVS← B919 BNE← B923 BEQ | |||||
| DEX ; Previous FCB slot | ||||||
| B8FF | BPL skip_if_slots_done ; More slots to check | |||||
| B901 | RTS ; All FCB slots processed, return | |||||
| B902 | .skip_if_slots_done←1← B8FF BPL | |||||
| LDA hazel_fcb_status,x ; Load channel flags for this slot | ||||||
| B905 | TAY ; Save flags in Y | |||||
| B906 | AND #2 ; Test active flag (bit 1) | |||||
| B908 | BEQ done_check_station ; Not active: check station match | |||||
| B90A | BVC loop_scan_fcb_down ; V clear (close all): next slot | |||||
| B90C | BCC done_check_station ; C clear: check station match | |||||
| B90E | TYA ; Restore original flags | |||||
| B90F | AND #&df ; Clear write-pending flag (bit 5) | |||||
| B911 | STA hazel_fcb_status,x ; Update channel flags | |||||
| B914 | BVS loop_scan_fcb_down ; Next slot (V always set here) | |||||
| B916 | .done_check_station←2← B908 BEQ← B90C BCC | |||||
| JSR match_station_net ; Check if channel belongs to station | ||||||
| B919 | BNE loop_scan_fcb_down ; No match: skip to next slot | |||||
| B91B | LDA #0 ; A=0: clear channel | |||||
| B91D | STA hazel_fcb_status,x ; Clear channel flags (close it) | |||||
| B920 | STA hazel_fcb_slot_attr,x ; Clear network number | |||||
| B923 | BEQ loop_scan_fcb_down ; Continue to next slot | |||||
| fall through ↓ | ||||||
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.
|
|||||||
| B925 | .match_station_net←7← A64A JSR← A675 JSR← A6AC JSR← AA0A JSR← AFC2 JSR← B851 JSR← B916 JSR | ||||||
| LDA hazel_fcb_state_byte,x ; Load FCB station number | |||||||
| B928 | EOR hazel_fs_station ; Compare with current station | ||||||
| B92B | BNE return_from_match_stn ; Different: Z=0, no match | ||||||
| B92D | LDA hazel_fcb_network,x ; Load FCB network number | ||||||
| B930 | EOR hazel_fs_network ; Compare with current network | ||||||
| B933 | .return_from_match_stn←1← B92B 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.
|
|||||||||
| B934 | .find_open_fcb←2← BA33 JSR← BB21 JSR | ||||||||
| LDX hazel_cur_fcb_index ; Load current FCB index | |||||||||
| B937 | BIT always_set_v_byte ; Set V flag (first pass marker) | ||||||||
| B93A | .loop_find_fcb←4← B949 BVC← B94F BPL← B96C BVS← B973 BNE | ||||||||
| INX ; Next FCB slot | |||||||||
| B93B | CPX #&10 ; Past end of table (&10)? | ||||||||
| B93D | BNE skip_if_no_wrap ; No: continue checking | ||||||||
| B93F | LDX #0 ; Wrap around to slot 0 | ||||||||
| B941 | .skip_if_no_wrap←1← B93D BNE | ||||||||
| CPX hazel_cur_fcb_index ; Back to starting slot? | |||||||||
| B944 | BNE done_check_fcb_status ; No: check this slot | ||||||||
| B946 | BVC loop_scan_empty_fcb ; V clear (second pass): scan empties | ||||||||
| B948 | CLV ; Clear V for second pass | ||||||||
| B949 | BVC loop_find_fcb ; Continue scanning | ||||||||
| B94B | .done_check_fcb_status←1← B944 BNE | ||||||||
| LDA hazel_fcb_flags,x ; Load FCB status flags | |||||||||
| B94E | ROL ; Shift bit 7 (in-use) into carry | ||||||||
| B94F | BPL loop_find_fcb ; Not in use: skip | ||||||||
| B951 | AND #4 ; Test bit 2 (modified flag) | ||||||||
| B953 | BNE skip_if_modified_fcb ; Modified: check further conditions | ||||||||
| B955 | .done_select_fcb←1← B975 BEQ | ||||||||
| DEX ; Adjust for following INX | |||||||||
| B956 | .loop_scan_empty_fcb←2← B946 BVC← B961 BPL | ||||||||
| INX ; Next FCB slot | |||||||||
| B957 | CPX #&10 ; Past end of table? | ||||||||
| B959 | BNE done_test_empty_slot ; No: continue | ||||||||
| B95B | LDX #0 ; Wrap around to slot 0 | ||||||||
| B95D | .done_test_empty_slot←1← B959 BNE | ||||||||
| LDA hazel_fcb_flags,x ; Load FCB status flags | |||||||||
| B960 | ROL ; Shift bit 7 into carry | ||||||||
| B961 | BPL loop_scan_empty_fcb ; Not in use: continue scanning | ||||||||
| B963 | SEC ; Set carry | ||||||||
| B964 | ROR ; Restore original flags | ||||||||
| B965 | STA hazel_fcb_flags,x ; Save flags back (mark as found) | ||||||||
| B968 | LDX hazel_cur_fcb_index ; Restore original FCB index | ||||||||
| B96B | RTS ; Return with found slot in X | ||||||||
| B96C | .skip_if_modified_fcb←1← B953 BNE | ||||||||
| BVS loop_find_fcb ; V set (first pass): skip modified | |||||||||
| B96E | LDA hazel_fcb_flags,x ; Load FCB status flags | ||||||||
| B971 | AND #&20 ; Test bit 5 (offset pending) | ||||||||
| B973 | BNE loop_find_fcb ; Bit 5 set: skip this slot | ||||||||
| B975 | BEQ done_select_fcb ; Use this slot | ||||||||
| fall through ↓ | |||||||||
Initialise byte counters for wipe/transferSets
|
||||||
| B977 | .init_wipe_counters←2← B9BA JSR← BA65 JSR | |||||
| LDY #1 ; Initial pass count = 1 | ||||||
| B979 | STY hazel_pass_counter ; Store pass counter | |||||
| B97C | DEY ; Y=0 | |||||
| B97D | STY hazel_byte_counter_lo ; Clear byte counter low | |||||
| B980 | STY hazel_offset_counter ; Clear offset counter | |||||
| B983 | STY hazel_transfer_flag ; Clear transfer flag | |||||
| B986 | TYA ; A=0 | |||||
| B987 | LDX #2 ; Clear 3 counter bytes | |||||
| B989 | .loop_clear_counters←1← B98D BPL | |||||
| STA hazel_xfer_init_zeros,x ; Clear counter byte | ||||||
| B98C | DEX ; Next byte | |||||
| B98D | BPL loop_clear_counters ; Loop for indices 2, 1, 0 | |||||
| B98F | STX hazel_sentinel_cd ; Store &FF as sentinel in xfer_sentinel_1 | |||||
| B992 | STX hazel_sentinel_ce ; Store &FF as sentinel in xfer_sentinel_2 | |||||
| B995 | LDX #&ca ; X=&CA: workspace offset | |||||
| B997 | LDY #&c2 ; Y=&10: page &10 | |||||
| B999 | 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.
|
||||
| B99A | .start_wipe_pass←2← BA2A JSR← BB54 JSR | |||
| JSR verify_ws_checksum ; Verify workspace checksum integrity | ||||
| B99D | STX hazel_cur_fcb_index ; Save current FCB index | |||
| B9A0 | LDA hazel_fcb_flags,x ; Load FCB status flags | |||
| B9A3 | ROR ; Shift bit 0 (active) into carry | |||
| B9A4 | BCC done_clear_fcb_active ; Not active: clear status and return | |||
| B9A6 | LDA hazel_station_lo ; Save current station low to stack | |||
| B9A9 | PHA ; Push station low | |||
| B9AA | LDA hazel_station_hi ; Save current station high | |||
| B9AD | PHA ; Push station high | |||
| B9AE | LDA hazel_fcb_station_lo,x ; Load FCB station low | |||
| B9B1 | STA hazel_station_lo ; Set as working station low | |||
| B9B4 | LDA hazel_fcb_station_hi,x ; Load FCB station high | |||
| B9B7 | STA hazel_station_hi ; Set as working station high | |||
| B9BA | JSR init_wipe_counters ; Reset transfer counters | |||
| B9BD | DEC hazel_offset_counter ; Set offset to &FF (no data yet) | |||
| B9C0 | DEC hazel_pass_counter ; Set pass counter to 0 (flush mode) | |||
| B9C3 | LDX hazel_cur_fcb_index ; Reload FCB index | |||
| B9C6 | TXA ; Transfer to A | |||
| B9C7 | CLC ; Prepare addition | |||
| B9C8 | ADC #&c3 ; Add &11 for buffer page offset | |||
| B9CA | STA hazel_buf_addr_hi ; Store buffer address high byte | |||
| B9CD | LDA hazel_fcb_flags,x ; Load FCB status flags | |||
| B9D0 | AND #&20 ; Test bit 5 (has saved offset) | |||
| B9D2 | BEQ done_restore_offset ; No offset: skip restore | |||
| B9D4 | LDA hazel_fcb_offset_save,x ; Load saved byte offset | |||
| B9D7 | STA hazel_offset_counter ; Restore offset counter | |||
| B9DA | .done_restore_offset←1← B9D2 BEQ | |||
| LDA hazel_fcb_attr_ref,x ; Load FCB attribute reference | ||||
| B9DD | STA hazel_chan_ref ; Store as current reference | |||
| B9E0 | TAX ; Transfer to X | |||
| B9E1 | JSR read_rx_attribute ; Read saved receive attribute | |||
| B9E4 | PHA ; Push to stack | |||
| B9E5 | TXA ; Restore attribute to A | |||
| B9E6 | STA (net_rx_ptr),y ; Set attribute in receive buffer | |||
| B9E8 | LDX #&ca ; X=&CA: workspace offset | |||
| B9EA | LDY #&c2 ; Y=&10: page &10 | |||
| B9EC | LDA #0 ; A=0: standard transfer mode | |||
| B9EE | JSR send_and_receive ; Send data and receive response | |||
| B9F1 | LDX hazel_cur_fcb_index ; Reload FCB index | |||
| B9F4 | PLA ; Restore saved receive attribute | |||
| B9F5 | JSR store_rx_attribute ; Restore receive attribute | |||
| B9F8 | PLA ; Restore station high | |||
| B9F9 | STA hazel_station_hi ; Store station high | |||
| B9FC | PLA ; Restore station low | |||
| B9FD | STA hazel_station_lo ; Store station low | |||
| BA00 | .done_clear_fcb_active←1← B9A4 BCC | |||
| LDA #&dc ; Mask &DC: clear bits 0, 1, 5 | ||||
| BA02 | AND hazel_fcb_flags,x ; Clear active and offset flags | |||
| BA05 | STA hazel_fcb_flags,x ; Update FCB status | |||
| BA08 | 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.
|
||||
| BA09 | .save_fcb_context←2← BACC JSR← BC85 JSR | |||
| LDX #&0c ; Copy 13 bytes (indices 0 to &0C) | ||||
| BA0B | .loop_save_tx_context←1← BA15 BPL | |||
| LDA hazel_txcb_port,x ; Load TX buffer byte | ||||
| BA0E | STA hazel_ctx_buffer,x ; Save to context buffer at &10D9 | |||
| BA11 | LDA fs_load_addr,x ; Load workspace byte from fs_load_addr | |||
| BA13 | PHA ; Save to stack | |||
| BA14 | DEX ; Next byte down | |||
| BA15 | BPL loop_save_tx_context ; Loop for all 13 bytes | |||
| BA17 | CPY #0 ; Y=0? (no FCB to process) | |||
| BA19 | BNE done_save_context ; Non-zero: scan and process FCBs | |||
| BA1B | JMP loop_restore_workspace ; Y=0: skip to restore workspace | |||
| BA1E | .done_save_context←1← BA19 BNE | |||
| PHP ; Save flags | ||||
| BA1F | LDX #&ff ; X=&FF: start scanning from -1 | |||
| BA21 | .loop_find_pending_fcb←2← BA25 BPL← BA28 BPL | |||
| INX ; Next FCB slot | ||||
| BA22 | LDA hazel_fcb_flags,x ; Load FCB status flags | |||
| BA25 | BPL loop_find_pending_fcb ; Bit 7 clear: not pending, skip | |||
| BA27 | ASL ; Shift bit 6 to bit 7 | |||
| BA28 | BPL loop_find_pending_fcb ; Bit 6 clear: skip | |||
| BA2A | JSR start_wipe_pass ; Flush this FCB's pending data | |||
| BA2D | LDA #&40 ; Pending marker &40 | |||
| BA2F | STA hazel_fcb_flags,x ; Mark FCB as pending-only | |||
| BA32 | PHP ; Save flags | |||
| BA33 | JSR find_open_fcb ; Find next available FCB slot | |||
| BA36 | PLP ; Restore flags | |||
| BA37 | LDA hazel_chan_attr ; Load current channel attribute | |||
| BA3A | STA hazel_chan_ref ; Store as current reference | |||
| BA3D | PHA ; Save attribute | |||
| BA3E | SEC ; Prepare attribute-to-channel conversion | |||
| BA3F | SBC #&20 ; Convert attribute (&20+) to channel index | |||
| BA41 | TAY ; Y = attribute index | |||
| BA42 | LDA hazel_fcb_slot_attr,y ; Load station for this attribute | |||
| BA45 | STA hazel_txcb_data ; Store station in TX buffer | |||
| BA48 | PLA ; Restore attribute | |||
| BA49 | STA hazel_fcb_attr_ref,x ; Store attribute in FCB slot | |||
| BA4C | LDA hazel_station_lo ; Load working station low | |||
| BA4F | STA hazel_fcb_station_lo,x ; Store in TX buffer | |||
| BA52 | LDA hazel_station_hi ; Load working station high | |||
| BA55 | STA hazel_fcb_station_hi,x ; Store in TX buffer | |||
| BA58 | TXA ; Get FCB slot index | |||
| BA59 | CLC ; Prepare addition | |||
| BA5A | ADC #&c3 ; Add &11 for buffer page offset | |||
| BA5C | STA hazel_buf_addr_hi ; Store buffer address high byte | |||
| BA5F | PLP ; Restore flags | |||
| BA60 | BVC done_init_wipe ; V clear: skip directory request | |||
| BA62 | JSR flush_fcb_if_station_known ; Command byte = 0 | |||
| BA65 | .done_init_wipe←1← BA60 BVC | |||
| JSR init_wipe_counters ; Reset transfer counters | ||||
| BA68 | JSR read_rx_attribute ; Read saved receive attribute | |||
| BA6B | PHA ; Function code &0D | |||
| BA6C | LDA hazel_chan_ref ; Load current reference | |||
| BA6F | STA (net_rx_ptr),y ; Set in receive buffer | |||
| BA71 | LDY #&c2 ; Y=&10: page &10 | |||
| BA73 | LDA #2 ; A=2: transfer mode 2 | |||
| BA75 | JSR send_and_receive ; Send and receive data | |||
| BA78 | PLA ; Restore receive attribute | |||
| BA79 | JSR store_rx_attribute ; Restore receive attribute | |||
| BA7C | LDX hazel_cur_fcb_index ; Reload FCB index | |||
| BA7F | LDA hazel_pass_counter ; Load pass counter | |||
| BA82 | BNE done_calc_offset ; Non-zero: data received, calc offset | |||
| BA84 | LDA hazel_offset_counter ; Load offset counter | |||
| BA87 | BEQ done_set_fcb_active ; Zero: no data received at all | |||
| BA89 | .done_calc_offset←1← BA82 BNE | |||
| LDA hazel_offset_counter ; Load offset counter | ||||
| BA8C | EOR #&ff ; Negate (ones complement) | |||
| BA8E | CLC ; Clear carry for add | |||
| BA8F | ADC #1 ; Complete twos complement negation | |||
| BA91 | STA hazel_fcb_offset_save,x ; Store negated offset in FCB | |||
| BA94 | LDA #&20 ; Set bit 5 (has saved offset) | |||
| BA96 | ORA hazel_fcb_flags,x ; Add to FCB flags | |||
| BA99 | STA hazel_fcb_flags,x ; Update FCB status | |||
| BA9C | LDA hazel_buf_addr_hi ; Load buffer address high byte | |||
| BA9F | STA fs_load_addr_3 ; Set pointer high byte | |||
| BAA1 | LDA #0 ; A=0: pointer low byte and clear val | |||
| BAA3 | STA fs_load_addr_2 ; Set pointer low byte | |||
| BAA5 | LDY hazel_fcb_offset_save,x ; Load negated offset (start of clear) | |||
| BAA8 | .loop_clear_buffer←1← BAAB BNE | |||
| STA (fs_load_addr_2),y ; Clear buffer byte | ||||
| BAAA | INY ; Next byte | |||
| BAAB | BNE loop_clear_buffer ; Loop until page boundary | |||
| BAAD | .done_set_fcb_active←1← BA87 BEQ | |||
| LDA #2 ; Set bit 1 (active flag) | ||||
| BAAF | ORA hazel_fcb_flags,x ; Add active flag to status | |||
| BAB2 | STA hazel_fcb_flags,x ; Update FCB status | |||
| BAB5 | LDY #0 ; Y=0: start restoring workspace | |||
| fall through ↓ | ||||
Pop 13 saved workspace bytes back to fs_load_addr+Y=0..&0C loop: PLA / STA fs_load_addr,Y / INY / CPY #&0D / BNE. Restores the 13-byte FS-options block that save_fcb_context pushed on the stack, undoing the protection the wipe/scan path put in place. Two callers: the JMP at &BA1B (close-and-restore exit) and the BNE retry at &BABE. |
|
| BAB7 | .loop_restore_workspace←2← BA1B JMP← BABE BNE |
| PLA ; Restore workspace byte from stack | |
| BAB8 | STA fs_load_addr,y ; Store to fs_load_addr workspace |
| BABB | INY ; Next byte |
| BABC | CPY #&0d ; Restored all 13 bytes? |
| BABE | BNE loop_restore_workspace ; No: continue restoring |
| fall through ↓ | |
Restore saved catalog entry to TX bufferCopies 13 bytes (Y=&0C..0) from
|
|
| BAC0 | .restore_catalog_entry←1← BCB5 JSR |
| LDY #&0c ; Copy 13 bytes (indices 0 to &0C) | |
| BAC2 | .loop_restore_tx_buf←1← BAC9 BPL |
| LDA hazel_ctx_buffer,y ; Load saved catalog byte from &10D9 | |
| BAC5 | STA hazel_txcb_port,y ; Restore to TX buffer |
| BAC8 | DEY ; Next byte down |
| BAC9 | BPL loop_restore_tx_buf ; Loop for all bytes |
| BACB | RTS ; Return |
Save FCB context, fall into find_matching_fcbSingle-instruction wrapper at the top of the per-iteration FCB search retry: JSR save_fcb_context to preserve the current attempt's state (offset, station, network), then fall through into find_matching_fcb. Single caller (the BNE retry at &BAEB). Used once the first scan past slot &0F has failed and the search needs to restart from slot 0 with the saved context restored. |
|
| BACC | .loop_save_before_match←1← BAEB 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 hazel_chan_attr. 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).
|
||||||
| BACF | .find_matching_fcb←3← A115 JSR← BB88 JSR← BC25 JSR | |||||
| LDX #&ff ; X=&FF: start scanning from -1 | ||||||
| BAD1 | .loop_reload_attr←2← BB0D BNE← BB15 BNE | |||||
| LDY hazel_chan_attr ; Load channel attribute to match | ||||||
| BAD4 | .loop_next_fcb_slot←2← BAF3 BEQ← BAF9 BNE | |||||
| INX ; Next FCB slot | ||||||
| BAD5 | CPX #&10 ; Past end of table (&10)? | |||||
| BAD7 | BNE done_test_fcb_active ; No: check this slot | |||||
| BAD9 | LDA hazel_chan_attr ; Load channel attribute | |||||
| BADC | JSR attr_to_chan_index ; Convert to channel index | |||||
| BADF | LDA hazel_fcb_addr_hi,x ; Load station for this channel | |||||
| BAE2 | STA hazel_station_hi ; Store as match target station high | |||||
| BAE5 | LDA hazel_fcb_addr_mid,x ; Load port for this channel | |||||
| BAE8 | STA hazel_station_lo ; Store as match target station low | |||||
| BAEB | JMP loop_save_before_match ; Save context and rescan from start | |||||
| BAEE | .done_test_fcb_active←1← BAD7 BNE | |||||
| LDA hazel_fcb_flags,x ; Load FCB status flags | ||||||
| BAF1 | AND #2 ; Test active flag (bit 1) | |||||
| BAF3 | BEQ loop_next_fcb_slot ; Not active: skip to next | |||||
| BAF5 | TYA ; Get attribute to match | |||||
| BAF6 | CMP hazel_fcb_attr_ref,x ; Compare with FCB attribute ref | |||||
| BAF9 | BNE loop_next_fcb_slot ; No attribute match: skip | |||||
| BAFB | STX hazel_cur_fcb_index ; Save matching FCB index | |||||
| BAFE | PHP ; Save flags from attribute compare | |||||
| BAFF | SEC ; Prepare subtraction | |||||
| BB00 | SBC #&20 ; Convert attribute to channel index | |||||
| BB02 | PLP ; Restore flags from attribute compare | |||||
| BB03 | TAY ; Y = channel index | |||||
| BB04 | LDX hazel_cur_fcb_index ; Reload FCB index | |||||
| BB07 | LDA hazel_fcb_addr_mid,y ; Load channel station byte | |||||
| BB0A | CMP hazel_fcb_station_lo,x ; Compare with FCB station | |||||
| BB0D | BNE loop_reload_attr ; Station mismatch: try next | |||||
| BB0F | LDA hazel_fcb_addr_hi,y ; Load channel network byte | |||||
| BB12 | CMP hazel_fcb_station_hi,x ; Compare with FCB network | |||||
| BB15 | BNE loop_reload_attr ; Network mismatch: try next | |||||
| BB17 | LDA hazel_fcb_flags,x ; Load FCB flags | |||||
| BB1A | BPL return_test_offset ; Bit 7 clear: no pending flush | |||||
| BB1C | AND #&7f ; Clear pending flag (bit 7) | |||||
| BB1E | STA hazel_fcb_flags,x ; Update FCB status | |||||
| BB21 | JSR find_open_fcb ; Find new open FCB slot | |||||
| BB24 | LDA hazel_fcb_flags,x ; Reload FCB flags | |||||
| BB27 | .return_test_offset←1← BB1A BPL | |||||
| AND #&20 ; Test bit 5 (has offset data) | ||||||
| BB29 | RTS ; Return; Z=1 no offset, Z=0 has data | |||||
Increment 3-byte FCB transfer countIncrements hazel_fcb_addr_lo+X (low), cascading overflow to hazel_fcb_addr_mid+X (mid) and hazel_fcb_addr_hi+X (high).
|
||||
| BB2A | .inc_fcb_byte_count←2← BBCD JSR← BC65 JSR | |||
| INC hazel_fcb_addr_lo,x ; Increment byte count low | ||||
| BB2D | BNE return_from_inc_fcb_count ; No overflow: done | |||
| BB2F | INC hazel_fcb_addr_mid,x ; Increment byte count mid | |||
| BB32 | BNE return_from_inc_fcb_count ; No overflow: done | |||
| BB34 | INC hazel_fcb_addr_hi,x ; Increment byte count high | |||
| BB37 | .return_from_inc_fcb_count←2← BB2D BNE← BB32 BNE | |||
| RTS ; Return | ||||
Process all active FCB slotsSaves 9 zero-page bytes (
|
||||
| BB38 | .process_all_fcbs←9← 8D9D JSR← 9078 JSR← 9778 JSR← 9EC9 JSR← 9F0E JSR← 9FB1 JSR← A06B JSR← A175 JSR← A9DF JSR | |||
| PHX ; Save X on entry | ||||
| BB39 | PHY ; Save Y across the body | |||
| BB3A | LDX #&f7 ; X=&F7: save 9 workspace bytes (&F7..&FF) | |||
| BB3C | .loop_save_fcb_workspace←1← BB41 BMI | |||
| LDA fcb_workspace_idx_base,x ; Load workspace byte | ||||
| BB3F | PHA ; Push fs_options | |||
| BB40 | INX ; Next byte | |||
| BB41 | BMI loop_save_fcb_workspace ; X<0: more bytes to save | |||
| BB43 | LDX #&0f ; Start from FCB slot &0F | |||
| BB45 | STX hazel_cur_fcb_index ; Store as current FCB index | |||
| BB48 | .loop_process_fcb←1← BB5B BPL | |||
| LDX hazel_cur_fcb_index ; Load current FCB index | ||||
| BB4B | TYA ; Get filter attribute | |||
| BB4C | BEQ done_flush_fcb ; Zero: process all FCBs | |||
| BB4E | CMP hazel_fcb_attr_ref,x ; Compare with FCB attribute ref | |||
| BB51 | BNE done_advance_fcb ; No match: skip this FCB | |||
| BB53 | .done_flush_fcb←1← BB4C BEQ | |||
| PHA ; Save filter attribute | ||||
| BB54 | JSR start_wipe_pass ; Flush pending data for this FCB | |||
| BB57 | PLY | |||
| BB58 | .done_advance_fcb←1← BB51 BNE | |||
| DEC hazel_cur_fcb_index ; Previous FCB index | ||||
| BB5B | BPL loop_process_fcb ; More slots: continue loop | |||
| BB5D | LDX #8 ; X=8: restore 9 workspace bytes | |||
| BB5F | .loop_restore_fcb_workspace←1← BB63 BPL | |||
| PLA ; Restore fs_block_offset | ||||
| BB60 | STA fs_work_4,x ; Restore workspace byte | |||
| BB62 | DEX ; Next byte down | |||
| BB63 | BPL loop_restore_fcb_workspace ; More bytes: continue restoring | |||
| BB65 | PLY | |||
| BB66 | PLX | |||
| BB67 | RTS ; Return | |||
BGETV vector handler: read byte from open fileReached via the BGETV vector at
|
|||||||||
| BB68 | .bgetv_handler | ||||||||
| STY hazel_chan_attr ; Save channel attribute | |||||||||
| BB6B | TXA ; Save caller's X | ||||||||
| BB6C | PHA ; Push X | ||||||||
| BB6D | JSR store_result_check_dir ; Store result and check not directory | ||||||||
| BB70 | LDA hazel_fcb_status,x ; Load channel flags | ||||||||
| BB73 | AND #&20 ; Test write-only flag (bit 5) | ||||||||
| BB75 | BEQ done_read_fcb_byte ; Not write-only: proceed with read | ||||||||
| BB77 | LDA #&d4 ; Error code &D4 | ||||||||
| BB79 | JSR error_inline_log ; Generate 'Write only' error | ||||||||
| BB7C | EQUS "Write only." | ||||||||
| BB87 | .done_read_fcb_byte←1← BB75 BEQ | ||||||||
| CLV ; Clear V (first-pass matching) | |||||||||
| BB88 | JSR find_matching_fcb ; Find FCB matching this channel | ||||||||
| BB8B | BEQ done_load_from_buf ; No offset: read byte from buffer | ||||||||
| BB8D | LDA hazel_fcb_addr_lo,y ; Load byte count for matching FCB | ||||||||
| BB90 | CMP hazel_fcb_offset_save,x ; Compare with buffer offset limit | ||||||||
| BB93 | BCC done_load_from_buf ; Below offset: data available | ||||||||
| BB95 | LDA hazel_fcb_status,y ; Load channel flags for FCB | ||||||||
| BB98 | TAX ; Transfer to X for testing | ||||||||
| BB99 | AND #&40 ; Test bit 6 (EOF already signalled) | ||||||||
| BB9B | BNE error_end_of_file ; EOF already set: raise error | ||||||||
| BB9D | TXA ; Restore flags | ||||||||
| BB9E | ORA #&40 ; Set EOF flag (bit 6) | ||||||||
| BBA0 | STA hazel_fcb_status,y ; Update channel flags with EOF | ||||||||
| BBA3 | LDA #0 ; A=0: clear receive attribute | ||||||||
| BBA5 | JSR store_rx_attribute ; Clear receive attribute (A=0) | ||||||||
| BBA8 | PLA ; Restore caller's X | ||||||||
| BBA9 | TAX ; X restored | ||||||||
| BBAA | LDA #&fe ; A=&FE: EOF marker byte | ||||||||
| BBAC | LDY hazel_chan_attr ; Restore channel attribute | ||||||||
| BBAF | SEC ; C=1: end of file | ||||||||
| BBB0 | RTS ; Return | ||||||||
| BBB1 | .error_end_of_file←1← BB9B BNE | ||||||||
| LDA #&df ; Error code &DF | |||||||||
| BBB3 | JSR error_inline_log ; Generate 'End of file' error | ||||||||
| BBB6 | EQUS "End of file." | ||||||||
| BBC2 | .done_load_from_buf←2← BB8B BEQ← BB93 BCC | ||||||||
| LDA hazel_fcb_addr_lo,y ; Load current byte count (= offset) | |||||||||
| BBC5 | PHA ; Save byte count | ||||||||
| BBC6 | TYA ; Get FCB slot index | ||||||||
| BBC7 | TAX ; X = FCB slot for byte count inc | ||||||||
| BBC8 | LDA #0 ; A=0: clear receive attribute | ||||||||
| BBCA | JSR store_rx_attribute ; Clear receive attribute (A=0) | ||||||||
| BBCD | JSR inc_fcb_byte_count ; Increment byte count for this FCB | ||||||||
| BBD0 | PLA ; Restore byte count (= buffer offset) | ||||||||
| BBD1 | TAY ; Y = offset into data buffer | ||||||||
| BBD2 | LDA hazel_cur_fcb_index ; Load current FCB index | ||||||||
| BBD5 | CLC ; Prepare addition | ||||||||
| BBD6 | ADC #&c3 ; Add &11 for buffer page offset | ||||||||
| BBD8 | STA fs_load_addr_3 ; Set pointer high byte | ||||||||
| BBDA | LDA #0 ; A=0: pointer low byte | ||||||||
| BBDC | STA fs_load_addr_2 ; Set pointer low byte | ||||||||
| BBDE | PLA ; Restore caller's X | ||||||||
| BBDF | TAX ; X restored | ||||||||
| BBE0 | LDA (fs_load_addr_2),y ; Read data byte from buffer | ||||||||
| BBE2 | LDY hazel_chan_attr ; Restore channel attribute | ||||||||
| BBE5 | CLC ; C=0: byte read successfully | ||||||||
| BBE6 | RTS ; Return; A = data byte | ||||||||
BPUTV vector handler: write byte to open fileReached via the BPUTV vector at
|
|||||||||
| BBE7 | .bputv_handler | ||||||||
| STY hazel_chan_attr ; Save channel attribute | |||||||||
| BBEA | PHA ; Save data byte | ||||||||
| BBEB | TAY ; Y = data byte | ||||||||
| BBEC | TXA ; Save caller's X | ||||||||
| BBED | PHA ; Push X | ||||||||
| BBEE | TYA ; Restore data byte to A | ||||||||
| BBEF | PHA ; Push data byte for later | ||||||||
| BBF0 | STA hazel_saved_byte ; Save data byte in workspace | ||||||||
| BBF3 | JSR store_result_check_dir ; Store result and check not directory | ||||||||
| BBF6 | LDA hazel_fcb_status,x ; Load channel flags | ||||||||
| BBF9 | BMI done_test_write_flag ; Bit 7 set: channel open, proceed | ||||||||
| BBFB | LDA #&c1 ; Error &C1: Not open for update | ||||||||
| BBFD | JSR error_inline_log ; Raise error with inline string | ||||||||
| BC00 | EQUS "Not open for update." | ||||||||
| BC14 | .done_test_write_flag←1← BBF9 BMI | ||||||||
| AND #&20 ; Test write flag (bit 5) | |||||||||
| BC16 | BEQ done_find_write_fcb ; Not write-capable: use buffer path | ||||||||
| BC18 | LDY hazel_fcb_slot_attr,x ; Load reply port for this channel | ||||||||
| BC1B | PLA ; Restore data byte | ||||||||
| BC1C | JSR send_wipe_request ; Send byte directly to server | ||||||||
| BC1F | JMP done_inc_byte_count ; Update byte count and return | ||||||||
| BC22 | .done_find_write_fcb←1← BC16 BEQ | ||||||||
| BIT always_set_v_byte ; Set V flag (alternate match mode) | |||||||||
| BC25 | JSR find_matching_fcb ; Find matching FCB for channel | ||||||||
| BC28 | LDA hazel_fcb_addr_lo,y ; Load byte count for FCB | ||||||||
| BC2B | CMP #&ff ; Buffer full (&FF bytes)? | ||||||||
| BC2D | BNE done_check_buf_offset ; No: store byte in buffer | ||||||||
| BC2F | JSR flush_fcb_with_init ; Save X | ||||||||
| BC32 | .done_check_buf_offset←1← BC2D BNE | ||||||||
| CMP hazel_fcb_offset_save,x ; Push Y | |||||||||
| BC35 | BCC done_set_dirty_flag ; Below offset: skip offset update | ||||||||
| BC37 | ADC #0 ; Carry set from BCS/BCC above | ||||||||
| BC39 | STA hazel_fcb_offset_save,x ; Update buffer offset in FCB | ||||||||
| BC3C | BNE done_set_dirty_flag ; Non-zero: keep offset flag | ||||||||
| BC3E | LDA #&df ; Mask &DF: clear bit 5 | ||||||||
| BC40 | AND hazel_fcb_flags,x ; Clear offset flag | ||||||||
| BC43 | STA hazel_fcb_flags,x ; Update FCB status | ||||||||
| BC46 | .done_set_dirty_flag←2← BC35 BCC← BC3C BNE | ||||||||
| LDA #1 ; Set bit 0 (dirty/active) | |||||||||
| BC48 | ORA hazel_fcb_flags,x ; Add to FCB flags | ||||||||
| BC4B | STA hazel_fcb_flags,x ; Update FCB status | ||||||||
| BC4E | LDA hazel_fcb_addr_lo,y ; Load byte count (= write position) | ||||||||
| BC51 | PHA ; Save count | ||||||||
| BC52 | TYA ; Get FCB slot index | ||||||||
| BC53 | TAX ; X = FCB slot | ||||||||
| BC54 | PLA ; Restore byte count | ||||||||
| BC55 | TAY ; Y = buffer write offset | ||||||||
| BC56 | LDA hazel_cur_fcb_index ; Load current FCB index | ||||||||
| BC59 | CLC ; Prepare addition | ||||||||
| BC5A | ADC #&c3 ; Add &11 for buffer page offset | ||||||||
| BC5C | STA fs_load_addr_3 ; Set pointer high byte | ||||||||
| BC5E | LDA #0 ; A=0: pointer low byte | ||||||||
| BC60 | STA fs_load_addr_2 ; Set pointer low byte | ||||||||
| BC62 | PLA ; Restore data byte | ||||||||
| BC63 | STA (fs_load_addr_2),y ; Write data byte to buffer | ||||||||
| fall through ↓ | |||||||||
Increment FCB byte count, clear rx attr, restore callerJSRs inc_fcb_byte_count for the active FCB, then A=0 / JSR store_rx_attribute (clears the receive-attribute byte). Pulls saved X back into X (caller's value), discards the saved data byte on the stack and returns. Single caller (the OSBPUT/PRINT path at &BC1F). |
|
| BC65 | .done_inc_byte_count←1← BC1F JMP |
| JSR inc_fcb_byte_count ; Increment byte count for this FCB | |
| BC68 | LDA #0 ; A=0: clear receive attribute |
| BC6A | JSR store_rx_attribute ; Clear receive attribute (A=0) |
| BC6D | PLA ; Restore caller's X |
| BC6E | TAX ; X restored |
| BC6F | PLA ; Discard saved data byte |
| BC70 | LDY hazel_chan_attr ; Restore channel attribute |
| BC73 | 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.
|
|||||||||||
| BC74 | .flush_fcb_if_station_known←1← BA62 JSR | ||||||||||
| PHA ; Save A | |||||||||||
| BC75 | PHX ; Save X | ||||||||||
| BC76 | PHY | ||||||||||
| BC77 | LDA hazel_fcb_slot_attr,y ; Read FCB slot attribute byte | ||||||||||
| BC7A | BNE store_station_and_flush ; Non-zero: station known -> store_station_and_flush | ||||||||||
| 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.
|
|||||||||||
| BC7C | .flush_fcb_with_init←1← BC2F JSR | ||||||||||
| PHA ; Save attribute byte (saved-station-test path) | |||||||||||
| BC7D | PHX ; Save X again | ||||||||||
| BC7E | PHY ; Save Y | ||||||||||
| BC7F | LDA hazel_fcb_slot_attr,y ; Load station for this channel | ||||||||||
| BC82 | PHA ; Save station on stack | ||||||||||
| BC83 | LDY #0 ; Y=0: reset index | ||||||||||
| BC85 | JSR save_fcb_context ; Save current FCB context | ||||||||||
| BC88 | PLA ; Restore station from stack | ||||||||||
| BC89 | .store_station_and_flush←1← BC7A BNE | ||||||||||
| STA hazel_txcb_data ; Store station in command buffer | |||||||||||
| BC8C | PLY | ||||||||||
| BC8D | PHY ; Save Y again for the next iteration | ||||||||||
| BC8E | PHA ; Save station for later restore | ||||||||||
| BC8F | LDX #0 ; X=0 | ||||||||||
| BC91 | STX hazel_txcb_flag ; Clear function code | ||||||||||
| BC94 | LDA hazel_fcb_addr_lo,y ; Load byte count lo from FCB | ||||||||||
| BC97 | STA hazel_txcb_count ; Store as data byte count | ||||||||||
| BC9A | LDA hazel_fcb_addr_mid,y ; Load byte count mid from FCB | ||||||||||
| BC9D | STA hazel_txcb_result ; Store as reply command byte | ||||||||||
| BCA0 | LDA hazel_fcb_addr_hi,y ; Load byte count hi from FCB | ||||||||||
| BCA3 | STA hazel_exec_addr ; Store as load vector field | ||||||||||
| BCA6 | LDY #&0d ; Y=&0D: TX command byte offset | ||||||||||
| BCA8 | LDX #5 ; X=5: send 5 bytes | ||||||||||
| BCAA | JSR save_net_tx_cb ; Send flush request to server | ||||||||||
| BCAD | PLA ; Restore station from stack | ||||||||||
| BCAE | TAY ; Y=station for wipe request | ||||||||||
| BCAF | LDA hazel_saved_byte ; Load saved data byte | ||||||||||
| BCB2 | JSR send_wipe_request ; Send close/wipe request to server | ||||||||||
| BCB5 | JSR restore_catalog_entry ; Restore catalog state after flush | ||||||||||
| BCB8 | PLY | ||||||||||
| BCB9 | PLX | ||||||||||
| BCBA | PLA ; Restore A | ||||||||||
| BCBB | 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.
|
||||||
| BCBC | .send_wipe_request←2← BC1C JSR← BCB2 JSR | |||||
| STY hazel_net_reply_buf_2 ; Store reply port | ||||||
| BCBF | STA hazel_net_reply_buf_3 ; Store data byte | |||||
| BCC2 | TYA ; Save Y | |||||
| BCC3 | PHA ; Push Y to stack | |||||
| BCC4 | TXA ; Save X | |||||
| BCC5 | PHA ; Push X to stack | |||||
| BCC6 | LDA #&90 ; Function code &90 | |||||
| BCC8 | STA hazel_net_reply_buf_0 ; Store in send buffer | |||||
| BCCB | JSR init_txcb ; Initialise TX control block | |||||
| BCCE | LDA #&dc ; TX start address low = &DC | |||||
| BCD0 | STA txcb_start ; Set TX start in control block | |||||
| BCD2 | LDA #&e0 ; TX end address low = &E0 | |||||
| BCD4 | STA txcb_end ; Set TX end in control block | |||||
| BCD6 | LDA #9 ; Expected reply port = 9 | |||||
| BCD8 | STA hazel_net_reply_buf_1 ; Store reply port in buffer | |||||
| BCDB | LDX #&c0 ; TX control = &C0 | |||||
| BCDD | LDY #0 ; Y=0: no timeout | |||||
| BCDF | LDA hazel_net_reply_buf_2 ; Load reply port for addressing | |||||
| BCE2 | JSR send_disconnect_reply ; Send packet to server | |||||
| BCE5 | LDA hazel_net_reply_buf_1 ; Load reply status | |||||
| BCE8 | BEQ done_toggle_station ; Zero: success | |||||
| BCEA | STA hazel_fs_last_error ; Store error code | |||||
| BCED | LDX #0 ; X=0: copy index | |||||
| BCEF | .loop_copy_wipe_err_msg←1← BCFA BNE | |||||
| LDA hazel_net_reply_buf_0,x ; Load error message byte | ||||||
| BCF2 | STA error_block,x ; Copy to error block | |||||
| BCF5 | CMP #&0d ; Is it CR (end of message)? | |||||
| BCF7 | BEQ done_terminate_wipe_err ; Yes: terminate string | |||||
| BCF9 | INX ; Next byte | |||||
| BCFA | BNE loop_copy_wipe_err_msg ; Continue copying error message | |||||
| BCFC | .done_terminate_wipe_err←1← BCF7 BEQ | |||||
| LDA #0 ; NUL terminator | ||||||
| BCFE | STA error_block,x ; Terminate error string in block | |||||
| BD01 | DEX ; Back up position for error check | |||||
| BD02 | JMP check_net_error_code ; Process and raise network error | |||||
| BD05 | .done_toggle_station←1← BCE8 BEQ | |||||
| LDX hazel_chan_attr ; Load channel attribute index | ||||||
| BD08 | LDA hazel_fcb_state_byte,x ; Load station number for channel | |||||
| BD0B | EOR #1 ; Toggle bit 0 (alternate station) | |||||
| BD0D | STA hazel_fcb_state_byte,x ; Update station number | |||||
| BD10 | PLA ; Restore X | |||||
| BD11 | TAX ; X restored | |||||
| BD12 | PLA ; Restore Y | |||||
| BD13 | TAY ; Y restored | |||||
| BD14 | 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.
|
||||||||
| BD15 | .send_and_receive←2← B9EE JSR← BA75 JSR | |||||||
| JSR set_options_ptr ; Set up FS options pointer | ||||||||
| BD18 | 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.
|
||||||
| BD1B | .read_rx_attribute←4← 99DF JSR← 99F2 JSR← B9E1 JSR← BA68 JSR | |||||
| LDY #&0a ; Y=&0A: receive attribute offset | ||||||
| BD1D | LDA (net_rx_ptr),y ; Read byte from receive buffer | |||||
| BD1F | 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.
|
|||||||
| BD20 | .store_rx_attribute←10← 8B96 JSR← 9EF9 JSR← 9FB9 JSR← 9FFD JSR← B889 JSR← B9F5 JSR← BA79 JSR← BBA5 JSR← BBCA JSR← BC6A JSR | ||||||
| LDY #&0a ; Y=&0A: receive attribute offset | |||||||
| BD22 | STA (net_rx_ptr),y ; Store byte to receive buffer | ||||||
| BD24 | RTS ; Return | ||||||
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. |
|
| BD25 | .abort_if_escape←1← BD59 JSR |
| BIT escape_flag ; Test bit 7 of escape flag | |
| BD27 | BMI error_escape_pressed ; Escape pressed: handle abort |
| BD29 | RTS ; No escape: return |
| BD2A | .error_escape_pressed←1← BD27 BMI |
| JSR close_ws_file ; Close the open file | |
| BD2D | .escape_error_close←1← 9722 JMP |
| JSR osnewl ; Write newline (characters 10 and 13) | |
| BD30 | LDA #osbyte_acknowledge_escape ; Acknowledge escape condition |
| BD32 | JSR osbyte ; Clear escape condition and perform escape effects |
| BD35 | LDA #&11 ; Error number &11 |
| BD37 | JSR error_inline ; Generate 'Escape' BRK error |
| BD3A | 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.
|
||||
| BD41 | .cmd_dump | |||
| JSR open_file_for_read ; Open the file (handle stored in ws_page) | ||||
| BD44 | LDX #&14 ; X=&14: 21-byte stack buffer for dump line state 21 bytes to push (0-&14) | |||
| BD46 | LDA #0 ; A=0: zero-fill | |||
| BD48 | .loop_push_zero_buf←1← BD4A BPL | |||
| PHA ; Push zero | ||||
| BD49 | DEX ; Step counter | |||
| BD4A | BPL loop_push_zero_buf ; Loop while X >= 0 (21 zeros) | |||
| BD4C | TSX ; Capture stack pointer for later restore | |||
| BD4D | JSR init_dump_buffer ; Parse address range and validate against file extent Set up buffer pointer and parse args | |||
| BD50 | LDA (work_ae),y ; Read low nibble of starting address | |||
| BD52 | AND #&f0 ; Mask high nibble (top 4 bits) | |||
| BD54 | BEQ loop_dump_line ; Aligned (high nibble zero): skip the header print Skip header if 16-byte aligned | |||
| BD56 | JSR print_dump_header ; Print 'Address: 00 01 ... 0F: ASCII data' header Print column header for offset start | |||
| fall through ↓ | ||||
*DUMP per-line read loopBody of cmd_dump's outer line loop. Calls abort_if_escape, then reads up to 16 bytes from the open file via OSBGET into the line buffer at (work_ae). On EOF mid-line, breaks to clean-up; on a full line, falls through to the formatting and print stage. Reachable from the alignment branch at &BD54 and the per-line tail at &BDF9. |
|
| BD59 | .loop_dump_line←2← BD54 BEQ← BDF9 JMP |
| JSR abort_if_escape ; Test escape and abort if pressed | |
| BD5C | LDA #&ff ; A=&FF: count counter starts here so first INC -> 0 |
| BD5E | STA osword_flag ; Save counter (-1) |
| BD60 | .loop_read_dump_byte←1← BD6F BNE |
| LDY ws_page ; Y = file handle | |
| BD62 | JSR osbget ; Read one byte via OSBGET (C set on EOF) |
| BD65 | BCS done_check_dump_eof ; EOF: finish off this line then exit |
| BD67 | INC osword_flag ; Increment count counter |
| BD69 | LDY osword_flag ; Y = current count (also buffer offset) |
| BD6B | STA (work_ae),y ; Store byte in 16-byte line buffer at (work_ae)+Y |
| BD6D | CPY #&0f ; Done all 16 bytes? |
| BD6F | BNE loop_read_dump_byte ; No: read next byte |
| BD71 | CLC ; C clear: not EOF (clean line) |
| BD72 | .done_check_dump_eof←1← BD65 BCS |
| PHP ; Save the EOF/clean flag | |
| BD73 | LDA osword_flag ; Reload counter byte |
| BD75 | BPL done_check_boundary ; Bit 7 clear (counter is 0..&7F): bytes were read |
| BD77 | LDX #&15 ; EOF and no bytes: clean up and exit |
| fall through ↓ | |
Drain saved bytes off stack and closePulls X+1 bytes off the 6502 stack (clearing the temporary 21-byte buffer cmd_dump uses to render each line) and tail-jumps to close_ws_file. Reached from the in-line BPL at &BD7B and the fall-through tail at &BDFE.
|
||||
| BD79 | .loop_pop_stack_buf←2← BD7B BPL← BDFE JMP | |||
| PLA ; Restore one stack byte | ||||
| BD7A | DEX ; Step | |||
| BD7B | BPL loop_pop_stack_buf ; Loop while X >= 0 (22 pulls) | |||
| BD7D | JMP close_ws_file ; Tail-jump to close_ws_file | |||
| BD80 | .done_check_boundary←1← BD75 BPL | |||
| LDY #&10 ; Y=&10: read displayed-address byte 0 | ||||
| BD82 | LDA (work_ae),y ; Read low byte | |||
| BD84 | AND #&f0 ; Top nibble | |||
| BD86 | BNE done_start_dump_addr ; Non-zero: not a 256-byte boundary, skip header Non-zero: header already current | |||
| BD88 | JSR print_dump_header ; Boundary: print column header | |||
| BD8B | .done_start_dump_addr←1← BD86 BNE | |||
| LDY #&13 ; Y=&13: highest byte of 4-byte address | ||||
| BD8D | .loop_print_addr_byte←1← BD97 BNE | |||
| LDA (work_ae),y ; Read address byte (highest first) | ||||
| BD8F | PHA ; Save it (print_hex_byte clobbers A) | |||
| BD90 | JSR print_hex_byte ; Print as 2 hex digits | |||
| BD93 | PLA ; Restore A | |||
| BD94 | DEY ; Step backwards | |||
| BD95 | CPY #&0f ; Reached low byte (offset &0F)? | |||
| BD97 | BNE loop_print_addr_byte ; No: continue printing | |||
| BD99 | INY ; Y=&10: low byte of address | |||
| BD9A | CLC ; Clear C | |||
| BD9B | ADC #&10 ; Bump address by 16 bytes for next line Add 16 to lowest address byte | |||
| BD9D | PHP ; Save C from the add | |||
| BD9E | .loop_inc_dump_addr←1← BDA9 BNE | |||
| PLP ; Restore C from previous step | ||||
| BD9F | STA (work_ae),y ; Store updated address byte | |||
| BDA1 | INY ; Step Y up | |||
| BDA2 | LDA (work_ae),y ; Read next byte | |||
| BDA4 | ADC #0 ; Add carry from below | |||
| BDA6 | PHP ; Save C | |||
| BDA7 | CPY #&14 ; Done all 4 bytes (Y=&14)? | |||
| BDA9 | BNE loop_inc_dump_addr ; No: continue propagating | |||
| BDAB | PLP ; Restore final C | |||
| BDAC | JSR print_inline ; Print ' : ' separator before hex byte field Print address/data separator | |||
| BDAF | EQUS " : " | |||
| BDB2 | LDY #0 ; Y=0: start of buffer | |||
| BDB4 | LDX osword_flag ; X = byte counter (-1 initially, INC'd to 0..&0F) X = bytes read (counter for display) | |||
| BDB6 | .loop_print_dump_hex←1← BDC1 BPL | |||
| LDA (work_ae),y ; Read byte from buffer | ||||
| BDB8 | JSR print_hex_and_space ; Print as hex + space | |||
| fall through ↓ | ||||
*DUMP per-column advance and end-of-line checkINY (next buffer offset), CPY #&10. End -> done_print_separator. Otherwise DEX (decrement byte counter); BPL loop_print_dump_hex to print the next byte. Single caller (the BPL at &BDCC after short-line padding).
|
||||||
| BDBB | .loop_next_dump_col←1← BDCC JMP | |||||
| INY ; Step buffer offset | ||||||
| BDBC | CPY #&10 ; Done all 16? | |||||
| BDBE | BEQ done_print_separator ; Yes: print separator before ASCII field | |||||
| BDC0 | DEX ; Step counter (Y was off-by-one from line read) Decrement remaining data bytes | |||||
| BDC1 | BPL loop_print_dump_hex ; Have a real byte? Print it | |||||
| BDC3 | PHY ; End of partial line: pad with 3 spaces | |||||
| BDC4 | JSR print_inline ; Print ' ' inline | |||||
| BDC7 | EQUS " " | |||||
| BDCA | NOP ; NOP -- bit-7 terminator + harmless resume opcode | |||||
| BDCB | PLY ; Restore Y | |||||
| BDCC | JMP loop_next_dump_col ; Continue padding the rest of the hex column | |||||
| BDCF | .done_print_separator←1← BDBE BEQ | |||||
| DEX ; Counter has finished -- step it once more for the ASCII test | ||||||
| BDD0 | JSR print_inline ; Print ': ' inline (ASCII field separator) | |||||
| BDD3 | EQUS ": " | |||||
| BDD5 | LDY #0 ; Y=0: rewind to start of line buffer | |||||
| BDD7 | JSR advance_x_by_8 ; Skip 8 padding spaces if needed (advance_x_by_8) | |||||
| BDDA | .loop_print_dump_ascii←1← BDF1 BPL | |||||
| LDA (work_ae),y ; Read line buffer byte | ||||||
| BDDC | AND #&7f ; Mask off bit 7 (DEL/inverted) | |||||
| BDDE | CMP #&20 ; Below ' '? (control char) | |||||
| BDE0 | BCS done_test_del ; Yes: skip to substitution | |||||
| BDE2 | .skip_non_printable←1← BDE6 BEQ | |||||
| LDA #&2e ; Substitute '.' for non-printables | ||||||
| BDE4 | .done_test_del←1← BDE0 BCS | |||||
| CMP #&7f ; Compare with DEL | ||||||
| BDE6 | BEQ skip_non_printable ; Equal: also non-printable, substitute '.' | |||||
| BDE8 | JSR osasci ; Print the (possibly substituted) character | |||||
| BDEB | INY ; Step Y | |||||
| BDEC | CPY #&10 ; Done 16 chars? | |||||
| BDEE | BEQ done_end_dump_line ; Yes: end this line | |||||
| BDF0 | DEX ; Step counter back | |||||
| BDF1 | BPL loop_print_dump_ascii ; Loop while X >= 0 | |||||
| BDF3 | .done_end_dump_line←1← BDEE BEQ | |||||
| JSR osnewl ; Print newline at end of line | ||||||
| BDF6 | PLP ; Restore EOF flag | |||||
| BDF7 | BCS done_dump_eof ; EOF: tidy up and exit | |||||
| BDF9 | JMP loop_dump_line ; More to dump: jump to next line | |||||
| BDFC | .done_dump_eof←1← BDF7 BCS | |||||
| LDX #&14 ; X=&14: balance the loop_pop_stack_buf counter | ||||||
| BDFE | JMP loop_pop_stack_buf ; Tail-jump to clean up the 21-byte stack buffer and close the file | |||||
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.
|
||||
| BE01 | .print_dump_header←2← BD56 JSR← BD88 JSR | |||
| LDA (work_ae),y ; Read low nibble of starting address from (work_ae),Y | ||||
| BE03 | PHA ; Save it (we'll print it 16 times incrementing each iteration) | |||
| BE04 | JSR print_inline ; Print 'CR>Address : ' header via inline string | |||
| BE07 | EQUS ".Address : " ; *Dump column header | |||
| BE13 | LDX #&0f ; X=&0F: print 16 column-number digits | |||
| BE15 | PLA ; Pull the starting low nibble back into A | |||
| BE16 | .loop_print_hex_row←1← BE1F BPL | |||
| JSR print_hex_and_space ; Print A as two hex digits + space | ||||
| BE19 | SEC ; Set C ready for the increment | |||
| BE1A | ADC #0 ; A += 1 (column index increments, with C set on entry) | |||
| BE1C | AND #&0f ; Wrap to nibble (0..15) | |||
| BE1E | DEX ; Step column counter | |||
| BE1F | BPL loop_print_hex_row ; Loop while X >= 0 (16 iterations) | |||
| BE21 | JSR print_inline ; Print ': ASCII dataCR>CR>' trailer via inline | |||
| BE24 | EQUS ": ASCII data.." ; *Dump trailer | |||
| BE35 | NOP ; NOP -- bit-7 terminator + harmless resume opcode | |||
| BE36 | RTS ; Return | |||
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.
|
||||
| BE37 | .print_hex_and_space←2← BDB8 JSR← BE16 JSR | |||
| PHA ; Save A so the caller can re-use the value | ||||
| BE38 | JSR print_hex_byte ; Print A as two hex digits | |||
| BE3B | LDA #&20 ; A=' ': trailing column separator | |||
| BE3D | JSR osasci ; Print the space via OSASCI | |||
| BE40 | .done_print_hex_space | |||
| PLA ; Restore caller's A | ||||
| BE41 | RTS ; Return | |||
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.
|
|||||||||
| BE42 | .parse_dump_range←2← BEB2 JSR← BF3E JSR | ||||||||
| TYA ; Move command-line offset Y into A for the X copy Save command line offset to X | |||||||||
| BE43 | TAX ; X = current command-line offset (live cursor) X tracks current position | ||||||||
| BE44 | LDA #0 ; A=0: zero-fill value | ||||||||
| BE46 | TAY ; Y=0: accumulator index | ||||||||
| BE47 | .loop_clear_hex_accum←1← BE4C BNE | ||||||||
| STA (work_ae),y ; Zero accumulator byte at (work_ae)+Y | |||||||||
| BE49 | INY ; Step accumulator | ||||||||
| BE4A | CPY #4 ; Done all 4 bytes? | ||||||||
| BE4C | BNE loop_clear_hex_accum ; No: continue clearing | ||||||||
| fall through ↓ | |||||||||
*DUMP / *LIST hex-address parser per-character bodyReload command-line offset from X, INX (step cursor), TAY (use as indirect index), read (os_text_ptr),Y. Branches: CR -> done; space -> end of token; otherwise validate hex digit and shift it into the 4-byte accumulator. Single caller (the BNE retry at &BE95).
|
||||
| BE4E | .loop_parse_hex_digit←1← BE95 JMP | |||
| TXA ; Reload command-line offset | ||||
| BE4F | INX ; Step cursor | |||
| BE50 | TAY ; Y = stepped cursor (for the indirect read) | |||
| BE51 | LDA (os_text_ptr),y ; Read next command-line byte | |||
| BE53 | CMP #&0d ; CR? (end of address) | |||
| BE55 | BEQ done_test_hex_space ; Yes: range parsed -- exit via space-skip | |||
| BE57 | CMP #&20 ; Space? | |||
| BE59 | BEQ done_test_hex_space ; Yes: also a separator -- exit | |||
| BE5B | CMP #&30 ; Below '0'? | |||
| BE5D | BCC error_bad_hex_value ; Yes: not hex -- raise 'Bad hex' | |||
| BE5F | CMP #&3a ; Above '9'? | |||
| BE61 | BCC done_mask_hex_digit ; No: it's '0'-'9' -- skip the letter handling Yes: is a decimal digit | |||
| BE63 | AND #&5f ; Force uppercase via AND #&5F | |||
| BE65 | ADC #&b8 ; Add &B8: 'A' (=&41) becomes &F9 with C set; 'F' becomes &FE; this maps 'A'-'F' to &FA-&FF in C Map 'A'-'F' → &FA-&FF (C=0 here) | |||
| BE67 | BCS error_bad_hex_value ; Carry out of ADC: digit was below 'A' -> bad hex Carry set: char > 'F', error | |||
| BE69 | CMP #&fa ; Below &FA? (i.e. before 'A' in mapped range) Below &FA? (i.e. was < 'A') | |||
| BE6B | BCC error_bad_hex_value ; Yes (out of [&FA,&FF]): bad hex | |||
| BE6D | .done_mask_hex_digit←1← BE61 BCC | |||
| AND #&0f ; Keep low nibble (0-15) | ||||
| BE6F | PHA ; Push the new nibble | |||
| BE70 | TXA ; Push X (current command-line offset) | |||
| BE71 | PHA ; Preserve on stack | |||
| BE72 | LDX #4 ; X=4: rotate the 4-byte accumulator left 4 times 4 bits to shift in | |||
| BE74 | .loop_shift_nibble←1← BE8A BNE | |||
| LDY #0 ; Y=0: byte index for the rotate | ||||
| BE76 | TYA ; A=0 (and C clear from TYA's flags) | |||
| BE77 | .loop_rotate_hex_accum←1← BE83 BNE | |||
| PHA ; Save A onto stack so we can use PHP/PLP to round-trip carry through the rotate Transfer carry bit to flags via stack | ||||
| BE78 | PLP ; Pull flags (effectively C clear from the TYA above; on later iterations C carries the bit shifted out) C = bit shifted out of prev iter | |||
| BE79 | LDA (work_ae),y ; Read next accumulator byte | |||
| BE7B | ROL ; Shift in C from below, shift out top bit to C Rotate left through carry | |||
| BE7C | STA (work_ae),y ; Write back | |||
| BE7E | PHP ; Save the new C | |||
| BE7F | PLA ; Pull A back (PHA earlier) | |||
| BE80 | INY ; Step accumulator byte | |||
| BE81 | CPY #4 ; Done all 4 bytes? | |||
| BE83 | BNE loop_rotate_hex_accum ; No: rotate next byte | |||
| BE85 | PHA ; PHA/PLP: bring saved C into flag register | |||
| BE86 | PLP ; C = overflow bit | |||
| BE87 | BCS error_hex_overflow ; C set: a bit fell off the top -- overflow Overflow: address too large | |||
| BE89 | DEX ; Step rotate counter | |||
| BE8A | BNE loop_shift_nibble ; Loop while X != 0 (4 rotates total) | |||
| BE8C | PLA ; Pull saved X (command-line offset) | |||
| BE8D | TAX ; Restore X | |||
| BE8E | PLA ; Pull saved nibble into A | |||
| BE8F | LDY #0 ; Y=0: low byte of accumulator | |||
| BE91 | ORA (work_ae),y ; OR new nibble into accumulator[0] | |||
| BE93 | STA (work_ae),y ; Write back | |||
| BE95 | JMP loop_parse_hex_digit ; Loop for next hex digit | |||
| BE98 | .error_hex_overflow←1← BE87 BCS | |||
| PLA ; Discard saved nibble | ||||
| BE99 | PLA ; Discard saved X | |||
| BE9A | SEC ; Set C: signal overflow to caller | |||
| BE9B | RTS ; Return with C=1 | |||
| BE9C | .error_bad_hex_value←3← BE5D BCC← BE67 BCS← BE6B BCC | |||
| JSR close_ws_file ; Close the dump file before raising the error Close open file before error | ||||
| BE9F | JMP err_bad_hex ; Raise 'Bad hex' error; never returns | |||
| BEA2 | .loop_skip_hex_spaces←1← BEA7 BEQ | |||
| INY ; Step past current space | ||||
| BEA3 | .done_test_hex_space←2← BE55 BEQ← BE59 BEQ | |||
| LDA (os_text_ptr),y ; Read next byte | ||||
| BEA5 | CMP #&20 ; Still a space? | |||
| BEA7 | BEQ loop_skip_hex_spaces ; Yes: keep skipping | |||
| BEA9 | CLC ; Clear C: signal success | |||
| BEAA | RTS ; Return | |||
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.
|
||||
| BEAB | .init_dump_buffer←1← BD4D JSR | |||
| INX ; Step Y past the *Dump command name into the argument X+1: first byte of buffer | ||||
| BEAC | STX work_ae ; Save the cursor offset | |||
| BEAE | LDX #1 ; Set bit 0 of addr_work to 1 -- 'mode' flag for parse_dump_range below Buffer is on stack in page 1 | |||
| BEB0 | STX addr_work ; Save mode flag | |||
| BEB2 | JSR parse_dump_range ; Parse the start address (max 4 hex digits) Parse start offset from command line | |||
| BEB5 | BCS error_outside_file ; Overflow: too many digits | |||
| BEB7 | TYA ; Save current Y (cursor after start address) A = command line offset after parse | |||
| BEB8 | PHA ; Push it | |||
| BEB9 | LDY ws_page ; Y = file handle saved in ws_page | |||
| BEBB | LDX #&aa ; X=&AA: zero-page address for OSARGS result | |||
| BEBD | LDA #2 ; A=2: OSARGS sub-fn 2 = read sequential file extent A=2: read file extent (length) | |||
| BEBF | JSR osargs ; Get file size into 4 bytes at &AA | |||
| BEC2 | LDY #3 ; Y=3: compare 4-byte values (high to low) | |||
| BEC4 | .loop_cmp_file_length←1← BECC BPL | |||
| LDA osword_flag,y ; Read file size byte at &AA+Y | ||||
| BEC7 | CMP (work_ae),y ; Compare with parsed start address (work_ae+Y) Compare with start offset byte | |||
| BEC9 | BNE done_check_outside ; Mismatch: branch decides which is bigger | |||
| BECB | DEY ; Step to next byte | |||
| BECC | BPL loop_cmp_file_length ; Loop while Y >= 0 (covers indices 3, 2, 1, 0) More bytes to compare | |||
| BECE | BMI done_advance_start ; All bytes equal: start = extent (allowed); jump to the post-validation path All equal: start = length, within file | |||
| BED0 | .done_check_outside←1← BEC9 BNE | |||
| BCC error_outside_file ; C clear: parsed_start > file_size -- reject Length < start: outside file | ||||
| BED2 | LDY #&ff ; Y=&FF: signal 'no copy needed' to the loop below Y=&FF: length > start, flag for later | |||
| BED4 | BNE done_advance_start ; Always taken: skip directly to advance phase Continue to copy start address | |||
| BED6 | .error_outside_file←2← BEB5 BCS← BED0 BCC | |||
| JSR close_ws_file ; Close the file before raising | ||||
| BED9 | LDA #&b7 ; A=&B7: 'Outside file' error code | |||
| BEDB | JSR error_inline ; Raise via inline string; never returns | |||
| BEDE | EQUS "Outside file." ; *Dump range error | |||
| BEEB | .loop_copy_osword_data←1← BEF3 BNE | |||
| .loop_copy_start_addr←1← BEF3 BNE | ||||
| LDA (work_ae),y ; Copy file-extent byte from osword_flag to (work_ae) Load start address byte from buffer | ||||
| BEED | STA osword_flag,y ; Store it (used as default end address) | |||
| BEF0 | .done_advance_start←2← BECE BMI← BED4 BNE | |||
| INY ; Step Y | ||||
| BEF1 | CPY #4 ; Done all 4 bytes? | |||
| BEF3 | BNE loop_copy_osword_data ; No: continue copying | |||
| BEF5 | LDX #&aa ; X=&AA: zero-page source for the OSARGS write-back | |||
| BEF7 | LDY ws_page ; Y = file handle | |||
| BEF9 | LDA #1 ; A=1: OSARGS sub-fn 1 = write sequential file pointer A=1: write file pointer | |||
| BEFB | JSR osargs ; Set the file's read pointer to the parsed start OSARGS: set file pointer | |||
| BEFE | PLA ; Pull saved cursor offset | |||
| BEFF | TAY ; Restore into Y | |||
| BF00 | LDA (os_text_ptr),y ; Read next command-line byte | |||
| BF02 | CMP #&0d ; CR (end of args)? | |||
| BF04 | BNE done_parse_disp_base ; No: there's a second arg -- handle below | |||
| BF06 | LDY #1 ; Y=1: copy os_text_ptr (2 bytes) to work_ae as a displacement-base hint Copy 2 bytes: os_text_ptr to buffer | |||
| BF08 | .loop_copy_osfile_ptr←1← BF0E BPL | |||
| LDA os_text_ptr,y ; Read os_text_ptr+Y | ||||
| BF0B | STA (work_ae),y ; Save in work_ae+Y | |||
| BF0D | DEY ; Step backwards | |||
| BF0E | BPL loop_copy_osfile_ptr ; Loop while Y >= 0 | |||
| BF10 | LDA #osfile_read_catalogue_info ; A=5: OSFILE sub-fn 5 = read catalogue info | |||
| BF12 | LDX work_ae ; X = filename pointer low (work_ae) | |||
| BF14 | LDY addr_work ; Y = filename pointer high (addr_work) | |||
| BF16 | JSR osfile ; Read load address into work_ae+0..3 | |||
| BF19 | LDY #2 ; Y=2: shift 3 bytes down 2 positions to drop the first 2 bytes (action code + a flag) Start at OSFILE +2 (load addr byte 0) | |||
| BF1B | .loop_shift_osfile_data←1← BF26 BNE | |||
| LDA (work_ae),y ; Read source byte | ||||
| BF1D | DEY ; Y -= 2 (destination) | |||
| BF1E | DEY ; Continue decrement | |||
| BF1F | STA (work_ae),y ; Store at destination | |||
| BF21 | INY ; Y += 3 to advance to next source | |||
| BF22 | INY ; (continued) | |||
| BF23 | INY ; (continued) | |||
| BF24 | CPY #6 ; Done 6 bytes shifted? | |||
| BF26 | BNE loop_shift_osfile_data ; No: continue | |||
| BF28 | DEY ; Y -= 2: position at high byte of load address Y=6 after loop exit | |||
| BF29 | DEY ; Y=4: check from buf[4] downward | |||
| BF2A | .loop_check_ff_addr←1← BF31 BNE | |||
| LDA (work_ae),y ; Read load-address byte at Y | ||||
| BF2C | CMP #&ff ; Is it &FF (signals no real load address)? | |||
| BF2E | BNE done_add_disp_base ; No: have a real load address; add it as displacement No: valid load address, use it | |||
| BF30 | DEY ; Yes: step back to next higher byte | |||
| BF31 | BNE loop_check_ff_addr ; Loop until Y=0 | |||
| BF33 | LDY #3 ; All four bytes were &FF: zero out the load address Clear all 4 bytes | |||
| BF35 | LDA #0 ; A=0 | |||
| BF37 | .loop_zero_load_addr←1← BF3A BPL | |||
| STA (work_ae),y ; Zero work_ae+Y | ||||
| BF39 | DEY ; Step backwards | |||
| BF3A | BPL loop_zero_load_addr ; Loop while Y >= 0 | |||
| BF3C | BMI done_add_disp_base ; Always taken (after BPL drops out): skip second-arg path Continue to compute display address | |||
| BF3E | .done_parse_disp_base←1← BF04 BNE | |||
| JSR parse_dump_range ; Parse end-address argument | ||||
| BF41 | BCC done_add_disp_base ; Success: continue with displacement-add | |||
| BF43 | JSR close_ws_file ; Parse error: close file then raise 'Bad address' Invalid: close file before error | |||
| BF46 | LDA #&fc ; A=&FC: 'Bad address' error code | |||
| BF48 | JSR error_bad_inline ; Raise; never returns | |||
| BF4B | EQUS "address." | |||
| BF53 | .done_add_disp_base←3← BF2E BNE← BF3C BMI← BF41 BCC | |||
| LDY #0 ; Y=0: start of work_ae | ||||
| BF55 | LDX #4 ; X=4: 4-byte add | |||
| BF57 | CLC ; Clear C for the add | |||
| BF58 | .loop_add_disp_bytes←1← BF62 BNE | |||
| LDA (work_ae),y ; Read low byte of address from (work_ae)+Y | ||||
| BF5A | ADC osword_flag,y ; Add osword_flag+Y (low byte of length, with carry propagating) Add start offset byte | |||
| BF5D | STA osword_flag,y ; Store sum back to osword_flag+Y | |||
| BF60 | INY ; Advance to next byte | |||
| BF61 | DEX ; Decrement byte counter | |||
| BF62 | BNE loop_add_disp_bytes ; Loop until 4 bytes added | |||
| BF64 | LDY #&14 ; Y=&14: target offset = workspace+&13 (top of end-addr field, stored hi-byte-first) Point past end of address area | |||
| BF66 | LDX #3 ; X=3: source = osword_flag+3 (top byte of sum) | |||
| BF68 | .loop_store_disp_addr←1← BF6E BPL | |||
| DEY ; Pre-decrement Y (so first store is to offset &13) | ||||
| BF69 | LDA osword_flag,x ; Read sum byte from osword_flag+X | |||
| BF6B | STA (work_ae),y ; Store at (work_ae)+Y | |||
| BF6D | DEX ; Decrement source index | |||
| BF6E | BPL loop_store_disp_addr ; Loop until X wraps below 0 | |||
| BF70 | RTS ; Return | |||
Close file handle stored in workspaceLoads the file handle from ws_page and closes it via OSFIND with A=0.
|
||||
| BF71 | .close_ws_file←5← BD2A JSR← BD7D JMP← BE9C JSR← BED6 JSR← BF43 JSR | |||
| LDY ws_page ; Y = saved file handle from ws_page | ||||
| BF73 | LDA #osfind_close ; A=0: OSFIND close | |||
| BF75 | JMP osfind ; Tail-call OSFIND to close the handle | |||
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.
|
|||||||
| BF78 | .open_file_for_read←1← BD41 JSR | ||||||
| PHP ; Save flags so caller's NZC survive | |||||||
| BF79 | TYA ; Move command-line offset Y into A for the add A=filename offset from Y | ||||||
| BF7A | CLC ; Clear C for the 16-bit add | ||||||
| BF7B | ADC os_text_ptr ; A = os_text_ptr_lo + Y (filename address low byte) Add text pointer low byte | ||||||
| BF7D | PHA ; Push it (we need to restore os_text_ptr after OSFIND) Save filename address low | ||||||
| BF7E | TAX ; Move filename low into X (OSFIND wants the address in X/Y) X=filename address low (for OSFIND) | ||||||
| BF7F | LDA #0 ; A=0: zero high byte before the carry-add | ||||||
| BF81 | ADC os_text_ptr_hi ; Add os_text_ptr_hi with carry from the low add Add text pointer high byte + carry | ||||||
| BF83 | PHA ; Push filename high byte for the restore | ||||||
| BF84 | TAY ; Move filename high into Y | ||||||
| BF85 | LDA #osfind_open_input ; A=&40: OSFIND open-for-input mode | ||||||
| BF87 | JSR osfind ; Open the file; returns handle in A (zero on failure) | ||||||
| BF8A | TAY ; Copy returned handle into Y (also sets Z if zero) | ||||||
| BF8B | STA ws_page ; Stash the handle in ws_page for later close | ||||||
| BF8D | BNE restore_text_ptr ; Non-zero: open succeeded, skip error path | ||||||
| BF8F | LDA #&d6 ; A=&D6: 'Not found' error code | ||||||
| BF91 | JSR error_inline ; Raise the error with the inline string below; never returns Raise 'Not found' error | ||||||
| BF94 | EQUS "Not found." | ||||||
| BF9E | .restore_text_ptr←1← BF8D BNE | ||||||
| PLA ; Restore the saved filename high byte into os_text_ptr_hi -- but wait, this writes the FILENAME address into os_text_ptr; the caller intentionally moves os_text_ptr to scan past the filename below Restore text pointer high from stack | |||||||
| BF9F | STA os_text_ptr_hi ; Store as os_text_ptr_hi | ||||||
| BFA1 | PLA ; Restore filename low byte into os_text_ptr_lo (so (os_text_ptr) now points at the filename) Restore text pointer low from stack | ||||||
| BFA2 | STA os_text_ptr ; Store as os_text_ptr lo | ||||||
| BFA4 | LDY #0 ; Y=0: scan from start of filename | ||||||
| BFA6 | .loop_skip_filename←1← BFAF BNE | ||||||
| INY ; Step to next byte | |||||||
| BFA7 | LDA (os_text_ptr),y ; Read filename byte | ||||||
| BFA9 | CMP #&0d ; Hit CR? End of command line | ||||||
| BFAB | BEQ done_skip_filename ; Yes: filename ended at CR (no trailing spaces) Yes: finished parsing filename | ||||||
| BFAD | CMP #&20 ; Hit space? End of filename | ||||||
| BFAF | BNE loop_skip_filename ; No (still inside filename): keep scanning | ||||||
| BFB1 | .loop_skip_fn_spaces←1← BFB6 BEQ | ||||||
| INY ; Step past spaces | |||||||
| BFB2 | LDA (os_text_ptr),y ; Read next byte | ||||||
| BFB4 | CMP #&20 ; Still a space? | ||||||
| BFB6 | BEQ loop_skip_fn_spaces ; Yes: keep skipping | ||||||
| BFB8 | .done_skip_filename←1← BFAB BEQ | ||||||
| PLP ; Done: Y points just past the filename and any spaces Restore caller flags | |||||||
| BFB9 | RTS ; Restore caller's flags | ||||||
Advance X by 16 via nested JSR + fall-throughNote: the name is historical and misleading -- this routine
actually advances
|
|||||||||
| BFBA | .advance_x_by_8←3← 9EDA JSR← AC32 JSR← BDD7 JSR | ||||||||
| JSR advance_x_by_4 ; First INX-by-4 via JSR; falls into advance_x_by_4 for the second four JSR+fall-through: 8+8=16 INXs total | |||||||||
| fall through ↓ | |||||||||
Advance X by 8 via JSR and fall-throughNote: the name is historical and misleading -- this routine
actually advances
|
|||||||||
| BFBD | .advance_x_by_4←1← BFBA JSR | ||||||||
| JSR inx4 ; JSR inx4 (4 INX); RTS returns here, then falls into inx4 again for the implicit second four 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.
|
|||||||||||
| BFC0 | .inx4←1← BFBD JSR | ||||||||||
| INX ; X += 4 | |||||||||||
| BFC1 | INX ; (continued) | ||||||||||
| BFC2 | INX ; (continued) | ||||||||||
| BFC3 | INX ; (continued) | ||||||||||
| BFC4 | RTS ; Return; caller is either an explicit JSR (so X has advanced by 4) or advance_x_by_8's fall-through (so X has advanced by 8 total) Return | ||||||||||
ROM-tail &FF padding (33 bytes positioning the HAZEL indexing bases)33 bytes of These bytes exist purely to push the indexing-base labels to
specific addresses immediately before |
|
| BFC5 | EQUB &FF, &FF ; ROM-tail padding (2 bytes &FF) |
| BFC7 | EQUB &FF ; ROM-tail padding (1 byte &FF; on its own line for annotation) Padding; next byte is reloc_p5_src |
| BFC8 | EQUB &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF, ; ROM-tail &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF, ; padding (30 &FF, &FF, &FF, &FF, &FF, &FF, &FF, &FF, ; bytes &FF) &FF, &FF, &FF, &FF, &FF, &FF |
HAZEL Y-indexed access bases (3 labels at the ROM tail)Three labels positioned The trick: HAZEL begins at
Each loop's CPY/BNE guard stops Y before it would land inside
the ROM tail, so the actual workspace data lives entirely in
HAZEL. The labels themselves never have their own bytes read --
the |
||||||||||||||||||||||||||||||||||||
| BFE6 | .hazel_minus_1a←1← AC5A LDA | |||||||||||||||||||||||||||||||||||
EQUB &FF, &FF, &FF, &FF, &FF, ; Base for hazel_minus_1a,Y
&FF, &FF, &FF, &FF, &FF, ; reads in
&FF, &FF, &FF, &FF, &FF, ; loop_copy_txcb_init --
&FF, &FF, &FF, &FF, &FF, ; &BFE6 + Y reaches into
&FF, &FF, &FF, &FF ; HAZEL for Y >= &1A |
||||||||||||||||||||||||||||||||||||
| BFFE | .hazel_minus_2←3← 8B67 STA← 9066 LDA← AC85 LDA | |||||||||||||||||||||||||||||||||||
EQUB &FF ; Base for hazel_minus_2,Y reads/writes --
&BFFE + Y reaches into HAZEL for Y >= 2 (used by
loop_copy_fs_ctx, loop_restore_ctx,
loop_copy_ws_to_pb) |
||||||||||||||||||||||||||||||||||||
| BFFF | .hazel_minus_1←2← A9D1 LDA← A9E6 STA | |||||||||||||||||||||||||||||||||||
EQUB &FF ; Base for hazel_minus_1,Y reads/writes --
&BFFF + Y reaches into HAZEL for Y >= 1 (used by
loop_copy_station, osword_13_set_station) |
||||||||||||||||||||||||||||||||||||
