Acorn ADFS 1.30

Updated 9 May 2026

← All Acorn ADFS versions

; Sideways ROM header
8000 .language_entry
.rom_header
EQUB &00, &00, &00
8003 .service_entry
JMP service_handler
8006 .rom_type
EQUB &82
8007 .copyright_offset
EQUB copyright - rom_header
8008 .binary_version
EQUB &30
8009 .title
EQUS "Acorn ADFS", &00
8014 .version
EQUS "1.30"
8018 .copyright
EQUS &00, "(C)1983 Acorn", &00

Claim Tube if present

Claim the Tube for a data transfer if a Tube is present. Copies the 4-byte transfer address from the control block to workspace and sets the Tube-in-use flag.

8027 .claim_tube←3← 8111 JSR← 8B61 JSR← BB67 JSR
LDY #4 ; Y=4: copy 4 bytes
8029 BIT zp_adfs_flags ; Is Tube present?
802B BPL return_1 ; No, return immediately
802D .copy_tube_addr_loop←1← 8033 BNE
LDA (zp_ctrl_blk_lo),y ; Copy 4-byte transfer address
802F STA wksp_tube_transfer_addr,y ; Store in Tube transfer workspace
8032 DEY ; Next byte
8033 BNE copy_tube_addr_loop ; Loop for 4 bytes
8035 LDA zp_adfs_flags ; Set bit 6: Tube in use
8037 ORA #&40 ; Set Tube-in-use flag
8039 STA zp_adfs_flags ; Store updated flags
803B .claim_tube_retry←4← 8040 BCC← A438 JSR← B839 JSR← B9A3 JSR
LDA #&c4 ; Claim Tube with A=&C4
803D JSR tube_entry ; Call Tube host to claim
8040 BCC claim_tube_retry ; Loop until claim succeeds
8042 .return_1←1← 802B BPL
RTS ; Return

Release Tube if in use

Release the Tube after a data transfer if it was claimed. Checks zp_adfs_flags bit 6 and clears it after release.

8043 .release_tube←5← 818A JSR← 8402 JSR← B8DB JSR← B9FB JMP← BFE0 JSR
BIT zp_adfs_flags ; Check Tube-in-use flag
8045 BVC return_2 ; Not in use, return immediately
8047 LDA #&84 ; Release Tube with A=&84
8049 JSR tube_entry ; Call Tube host to release
804C PHP ; Save interrupt state
804D SEI ; Disable interrupts
804E LDA zp_adfs_flags ; Clear bit 6: Tube no longer in use
8050 AND #&bf ; Clear Tube-in-use bit
8052 STA zp_adfs_flags ; Store updated flags
8054 PLP ; Restore interrupt state
8055 .return_2←1← 8045 BVC
RTS ; Return

Read SCSI status with settling

Read the SCSI status register, waiting for the value to settle. Reads the status twice and loops until consecutive reads match. Also stores result in zp_scsi_status.

On ExitAsettled SCSI status byte
Xcorrupted
Ycorrupted
8056 .scsi_get_status←5← 806A JSR← 8078 JSR← 8197 JSR← 8310 JSR← AB80 JSR
PHP ; Save processor flags
8057 .scsi_read_settle_loop←1← 8061 BNE
LDA fred_hard_drive_1 ; Read SCSI status register
805A STA zp_scsi_status ; Store first reading
805C LDA fred_hard_drive_1 ; Read SCSI status register again
805F CMP zp_scsi_status ; Has it settled?
8061 BNE scsi_read_settle_loop ; No, try again
8063 PLP ; Restore processor flags
8064 RTS ; Return

SCSI bus selection and command phase

Select a SCSI device on the bus and begin the command phase. Asserts the target's SCSI ID on the data bus and waits for the BSY signal to be asserted by the target.

8065 .scsi_start_command←3← 80F6 JSR← 823A JSR← 8B46 JSR
LDY #0 ; Y=0 for normal start
8067 .scsi_start_command2←1← AACA JSR
LDA #1 ; SCSI ID bit pattern = 1 (drive 0)
8069 PHA ; Save SCSI ID on stack
806A .wait_bus_free_loop←1← 806F BNE
JSR scsi_get_status ; Wait for BSY to deassert
806D AND #2 ; Check BSY bit
806F BNE wait_bus_free_loop ; Loop while BSY asserted
8071 PLA ; Retrieve SCSI ID
8072 STA fred_hard_drive_0 ; Assert ID on SCSI data bus
8075 STA fred_hard_drive_2 ; Assert SEL to select target
8078 .wait_target_bsy_loop←1← 807D BEQ
JSR scsi_get_status ; Wait for target to assert BSY
807B AND #2 ; Check BSY bit
807D BEQ wait_target_bsy_loop ; Loop until BSY asserted
807F .return_3←1← 80A7 BEQ
RTS ; Return

Set retry count for disc operation

Set the retry counter to the default value (16).

8080 .command_set_retries←4← 809F JSR← 8AF8 JSR← AB2D JSR← AC96 JSR
LDA wksp ; Default retry count from workspace
8083 STA zp_retry_count ; Store in retry counter
8085 RTS ; Return
8086 .escape_during_retry←1← 80B1 BMI
JMP error_escape_ack_invalidate_reload_fsm ; Escape during retry: abort

Execute disc command with control block at (X,Y)

Execute a disc operation using the control block pointed to by X (low) and Y (high). Handles both hard drive (SCSI) and floppy disc operations with retry logic.

On EntryXcontrol block address low byte
Ycontrol block address high byte
On ExitAresult code (0 = success, Z set)
Xcontrol block address low (preserved)
Ycontrol block address high (preserved)
8089 .command_exec_xy←6← 828B JSR← 8A74 JSR← 8ABF JSR← 9D52 JSR← A0D7 JSR← A16B JSR
JSR wait_ensuring ; Wait if files being ensured
808C STX zp_ctrl_blk_lo ; Store control block address low
808E STY zp_ctrl_blk_hi ; Store control block address high
8090 JSR check_dir_loaded ; Ensure directory is loaded
8093 LDY #5 ; Byte 5 of control block = command
8095 LDA (zp_ctrl_blk_lo),y ; Get command byte from control block
8097 CMP #&2f ; Format track?
8099 BEQ command_exec_start_exec ; Yes, skip retries
809B CMP #&1b ; Seek?
809D BEQ command_exec_start_exec ; Yes, skip retries
809F JSR command_set_retries ; Set default retry count
80A2 BPL dispatch_hd_or_floppy ; Always branch (retry count >= 0)
80A4 .command_exec_retry_loop←1← 80C4 BPL
JSR command_exec_start_exec ; Execute the disc operation
80A7 BEQ return_3 ; Success, return
80A9 CMP #4 ; Not-ready error?
80AB BNE dispatch_hd_or_floppy ; No, check if retries exhausted
80AD LDY #&19 ; Delay loop for not-ready
80AF .check_escape_during_retry←3← 80B6 BNE← 80B9 BNE← 80BC BNE
BIT zp_escape_flag ; Check for Escape during delay
80B1 BMI escape_during_retry ; Escape pressed, abort
80B3 SEC ; Set carry for subtraction
80B4 SBC #1 ; Decrement delay low byte
80B6 BNE check_escape_during_retry ; Inner loop not done
80B8 DEX ; Decrement delay mid byte
80B9 BNE check_escape_during_retry ; Mid loop not done
80BB DEY ; Decrement delay high byte
80BC BNE check_escape_during_retry ; Outer loop not done
80BE .dispatch_hd_or_floppy←2← 80A2 BPL← 80AB BNE
CMP #&40 ; Drive-not-present error?
80C0 BEQ command_exec_start_exec ; Yes, no point retrying
80C2 DEC zp_retry_count ; Decrement retry counter
80C4 BPL command_exec_retry_loop ; More retries remaining
80C6 .command_exec_start_exec←4← 8099 BEQ← 809D BEQ← 80A4 JSR← 80C0 BEQ
LDA zp_adfs_flags ; Check zp_flags for hard drive
80C8 AND #&20 ; Bit 5: hard drive present?
80CA BNE hd_command ; Yes, use hard drive command
80CC .command_exec_floppy_op←1← 80F4 BMI
JSR floppy_command_ind ; Floppy disc operation
80CF BEQ return_4 ; Success, return
80D1 PHA ; Save error code
80D2 LDY #6 ; Byte 6: drive + sector high
80D4 LDA (zp_ctrl_blk_lo),y ; Get drive+sector byte from blk
80D6 ORA wksp_current_drive ; Combine with current drive number
80D9 STA wksp_err_sector_hi ; Store in error sector workspace
80DC INY
80DD LDA (zp_ctrl_blk_lo),y ; Byte 7: sector mid
80DF STA wksp_err_sector_mid ; Store sector mid byte
80E2 INY
80E3 LDA (zp_ctrl_blk_lo),y ; Byte 8: sector low
80E5 STA wksp_err_sector ; Store sector low byte
80E8 PLA ; Retrieve error code
80E9 STA wksp_err_code ; Store error code
80EC .return_4←1← 80CF BEQ
RTS ; Return

Execute hard drive SCSI command

Execute a disc operation via the SCSI hard drive interface. Sends the SCSI command bytes from the control block at (&B0), performs data transfer (direct or via Tube), and reads the SCSI status and message phases.

Falls back to floppy if drive bit 7 is set.

On ExitAresult code (0 = success, Z set)
Xcontrol block address low (restored)
Ycontrol block address high (restored)
80ED .hd_command←1← 80CA BNE
LDY #6 ; Byte 6: drive + sector b16-b20
80EF LDA (zp_ctrl_blk_lo),y ; Get byte from control block
80F1 ORA wksp_current_drive ; Combine with current drive
80F4 BMI command_exec_floppy_op ; Bit 7 set = floppy drive
80F6 JSR scsi_start_command ; Select SCSI device and begin command
80F9 INY ; Byte 7: sector b8-b15
80FA LDA (zp_ctrl_blk_lo),y ; Get byte from control block
80FC STA zp_mem_ptr_lo ; Store as memory address low
80FE INY ; Byte 8: sector b0-b7
80FF LDA (zp_ctrl_blk_lo),y ; Get byte from control block
8101 STA zp_mem_ptr_hi ; Store as memory address high
8103 INY ; Byte 9: transfer address high
8104 LDA (zp_ctrl_blk_lo),y ; Get byte from control block
8106 CMP #&fe ; Address >= &FE00?
8108 BCC skip_tube_claim ; No, claim Tube for normal transfer
810A INY ; Byte 10: next address byte
810B LDA (zp_ctrl_blk_lo),y ; Get byte from control block
810D CMP #&ff ; Address = &FFxx (host memory)?
810F BEQ send_scsi_command_bytes ; Yes, skip Tube claim
8111 .skip_tube_claim←1← 8108 BCC
JSR claim_tube
8114 .send_scsi_command_bytes←1← 810F BEQ
LDY #5 ; Byte 5: SCSI command byte
8116 LDA (zp_ctrl_blk_lo),y ; Get byte from control block
8118 JSR scsi_send_byte_a ; Send SCSI command byte
811B INY ; Byte 6: drive + sector high
811C LDA (zp_ctrl_blk_lo),y ; Get byte from control block
811E ORA wksp_current_drive ; Combine with current drive for LUN
8121 STA wksp_current_drive_hi ; Save combined drive/LUN
8124 JMP send_next_cmd_byte ; Jump into command send loop
8127 .send_cmd_byte_loop←1← 8134 BNE
LDA (zp_ctrl_blk_lo),y ; Get next command byte
8129 .send_next_cmd_byte←1← 8124 JMP
JSR scsi_send_byte_a ; Send command byte to target
812C JSR scsi_wait_for_req ; Wait for SCSI REQ signal
812F BPL check_256_byte_transfer ; Status phase? Done sending command
8131 BVS check_256_byte_transfer ; Message phase? Done sending command
8133 INY ; Next command byte
8134 BNE send_cmd_byte_loop ; More command bytes to send
8136 .check_256_byte_transfer←2← 812F BPL← 8131 BVS
LDY #5 ; Check for 256-byte sector transfer
8138 LDA (zp_ctrl_blk_lo),y ; Get command byte
813A AND #&fd ; Mask to read/write bits
813C EOR #8 ; Is it a read/write 256-byte command?
813E BEQ hd_data_transfer_256 ; Yes, use optimised transfer
8140 JSR scsi_wait_for_req ; Wait for data phase
8143 CLC ; C=0: write direction
8144 BVC start_byte_transfer ; I/O bit clear? Writing
8146 SEC ; C=1: read direction
8147 .start_byte_transfer←1← 8144 BVC
LDY #0 ; Y=0: byte counter for 256-byte page
8149 BIT zp_adfs_flags ; Tube in use?
814B BVC wait_data_phase ; No, direct memory transfer
814D LDX #&27 ; X=&27: Tube workspace addr low
814F LDY #&10 ; Y=&10: Tube workspace page
8151 LDA #0 ; A=0 (direction flag)
8153 PHP ; Save direction flag
8154 ROL ; Rotate carry into bit 0
8155 JSR tube_start_xfer ; Start Tube transfer
8158 PLP ; Restore processor flags
8159 .wait_data_phase←5← 814B BVC← 8171 BNE← 8175 JMP← 8180 BCC← 8188 BCS
JSR scsi_wait_for_req ; Wait for SCSI REQ
815C BMI command_done ; Status phase, transfer done
815E BIT zp_adfs_flags ; Tube in use?
8160 BVS read_scsi_via_tube ; Yes, use Tube path
8162 BCS read_scsi_to_memory ; Reading from SCSI?
8164 LDA (zp_mem_ptr_lo),y ; Writing: get byte from memory
8166 STA fred_hard_drive_0 ; Write to SCSI data register
8169 BCC advance_memory_page ; Always branch to increment
816B .read_scsi_to_memory←1← 8162 BCS
LDA fred_hard_drive_0 ; Reading: get byte from SCSI
816E STA (zp_mem_ptr_lo),y ; Store in memory
8170 .advance_memory_page←1← 8169 BCC
INY ; Next byte
8171 BNE wait_data_phase ; Continue until page done
8173 INC zp_mem_ptr_hi ; Increment page pointer
8175 JMP wait_data_phase ; Continue transfer
8178 .read_scsi_via_tube←1← 8160 BVS
BCS write_tube_to_scsi ; Reading from SCSI via Tube?
817A LDA tube_data_register_3 ; Writing via Tube: read from Tube R3
817D STA fred_hard_drive_0 ; Write to SCSI data register
8180 BCC wait_data_phase ; Always branch back
8182 .write_tube_to_scsi←1← 8178 BCS
LDA fred_hard_drive_0 ; Reading via Tube: read from SCSI
8185 STA tube_data_register_3 ; Write to Tube R3
8188 BCS wait_data_phase ; Always branch back
fall through ↓

Complete SCSI command and read status

Release the Tube, then read the SCSI status and message bytes to determine the outcome of the command.

On ExitAresult code (0 = success, &7F-masked error)
Xcontrol block address low (restored)
Ycontrol block address high (restored)
818A .command_done←7← 815C BMI← 81C1 BMI← 8205 JMP← 8328 JMP← 8BB0 JMP← AB59 JSR← ACC6 JSR
JSR release_tube ; Release Tube if claimed
818D .wait_status_phase←1← 819C BEQ
JSR scsi_wait_for_req ; Wait for SCSI REQ (status phase)
8190 LDA fred_hard_drive_0 ; Read status byte from SCSI data
8193 JSR scsi_wait_for_req ; Wait for SCSI REQ (message phase)
8196 TAY ; Save status in Y
8197 JSR scsi_get_status ; Read SCSI status register
819A AND #1 ; Check BSY still asserted
819C BEQ wait_status_phase ; Loop until bus free
819E TYA ; Retrieve status byte
819F LDX fred_hard_drive_0 ; Read final data byte
81A2 BEQ check_scsi_error_bit ; Status OK?
81A4 JMP unrecoverable_scsi_error ; No, return error &FF
81A7 .check_scsi_error_bit←1← 81A2 BEQ
TAX ; Transfer status to X
81A8 AND #2 ; Check error bit in status
81AA BEQ return_scsi_result ; No error, return success
81AC JMP scsi_request_sense ; Error: do SCSI Request Sense
81AF .return_scsi_result←1← 81AA BEQ
LDA #0 ; A=0: success return code
81B1 .mask_error_code←2← 827F JMP← 8284 JMP
LDX zp_ctrl_blk_lo ; Restore control block pointer
81B3 LDY zp_ctrl_blk_hi ; Restore Y
81B5 AND #&7f ; Mask to 7-bit error code
81B7 RTS ; Return

SCSI 256-byte sector data transfer

Transfer complete 256-byte sectors between SCSI bus and memory (direct or via Tube). Optimised inner loop with no per-byte SCSI REQ polling.

81B8 .hd_data_transfer_256←1← 813E BEQ
LDY #0 ; Y=0: byte counter
81BA BIT zp_adfs_flags ; Tube in use?
81BC BVS setup_tube_write_256 ; Yes, use Tube 256-byte transfer
81BE .wait_req_and_transfer←2← 81CF BVC← 81DB BVS
JSR scsi_wait_for_req ; Wait for SCSI REQ
81C1 BMI command_done ; Status phase, done
81C3 BVS read_sector_byte_loop ; I/O bit: reading from SCSI?
81C5 .write_sector_byte_loop←1← 81CB BNE
LDA (zp_mem_ptr_lo),y ; Writing: get byte from memory
81C7 STA fred_hard_drive_0 ; Write to SCSI data register
81CA INY ; Next byte
81CB BNE write_sector_byte_loop ; Continue for 256 bytes
81CD INC zp_mem_ptr_hi ; Next page
81CF BVC wait_req_and_transfer ; Continue transfer
81D1 .read_sector_byte_loop←2← 81C3 BVS← 81D7 BNE
LDA fred_hard_drive_0 ; Reading: get byte from SCSI
81D4 STA (zp_mem_ptr_lo),y ; Store in memory
81D6 INY ; Next byte
81D7 BNE read_sector_byte_loop ; Continue for 256 bytes
81D9 INC zp_mem_ptr_hi ; Next page
81DB BVS wait_req_and_transfer ; Continue transfer
81DD .increment_tube_xfer_addr←2← 821C JSR← 8234 JSR
INC wksp_tube_xfer_addr_2 ; Increment low byte of transfer addr
81E0 BNE load_tube_workspace_ptr ; No wrap: skip mid byte increment
81E2 INC wksp_tube_xfer_addr_3 ; Increment mid byte
81E5 BNE load_tube_workspace_ptr ; No wrap: skip high byte increment
81E7 INC wksp_csd_drive_temp ; Increment high byte
81EA .load_tube_workspace_ptr←2← 81E0 BNE← 81E5 BNE
LDX #&27 ; X=&27: Tube workspace addr low
81EC LDY #&10 ; Y=&10: Tube workspace page
81EE RTS ; Return

Start Tube transfer with interrupts disabled

Disable interrupts then call the Tube host code at &0406 to initiate a data transfer.

On EntryATube transfer type (6=write, 7=read)
81EF .tube_start_xfer_sei←2← 820D JSR← 8225 JSR
SEI ; Disable interrupts for Tube xfer
fall through ↓

Start Tube transfer

Call the Tube host code at &0406 to initiate a data transfer. Followed by a delay for Tube synchronisation.

On EntryATube transfer type
81F0 .tube_start_xfer←1← 8155 JSR
JSR tube_entry ; Call Tube host code at &0406
81F3 LDY #0 ; Y=0
81F5 .tube_delay
JSR tube_delay2 ; Delay for Tube synchronisation
81F8 .tube_delay2←3← 81F5 JSR← 8BA2 JSR← B9E2 JSR
JSR return_5 ; Nested JSR/RTS delay
81FB .return_5←1← 81F8 JSR
RTS ; Return (also used as delay)
81FC .setup_tube_write_256←1← 81BC BVS
LDX #&27 ; X=&27: Tube workspace addr low
81FE LDY #&10 ; Y=&10: Tube workspace page
8200 .wait_tube_data_phase←2← 8220 BVC← 8238 BVS
JSR scsi_wait_for_req ; Wait for SCSI REQ
8203 BPL set_tube_write_direction ; Data phase?
8205 JMP command_done ; No, status phase - done
8208 .set_tube_write_direction←1← 8203 BPL
BVS set_tube_read_direction ; I/O bit: reading from SCSI?
820A PHP ; Save flags before SEI
820B LDA #6 ; Tube transfer type 6 (write)
820D JSR tube_start_xfer_sei ; Start Tube transfer with SEI
8210 .tube_write_byte_loop←1← 821A BNE
NOP ; NOP timing delay for Tube
8211 NOP ; NOP timing delay
8212 NOP ; NOP timing delay
8213 LDA tube_data_register_3 ; Read byte from Tube R3
8216 STA fred_hard_drive_0 ; Write to SCSI data register
8219 INY ; Next byte
821A BNE tube_write_byte_loop ; Continue for 256 bytes
821C JSR increment_tube_xfer_addr ; Increment transfer address
821F PLP ; Restore flags, continue transfer
8220 BVC wait_tube_data_phase ; Continue outer transfer loop
8222 .set_tube_read_direction←1← 8208 BVS
PHP ; Save flags for read path
8223 LDA #7 ; Tube transfer type 7 (read)
8225 JSR tube_start_xfer_sei ; Start Tube transfer with SEI
8228 .tube_read_byte_loop←1← 8232 BNE
NOP ; NOP timing delay for Tube
8229 NOP ; NOP timing delay
822A NOP ; NOP timing delay
822B LDA fred_hard_drive_0 ; Read byte from SCSI data register
822E STA tube_data_register_3 ; Write to Tube R3
8231 INY ; Next byte
8232 BNE tube_read_byte_loop ; Continue for 256 bytes
8234 JSR increment_tube_xfer_addr ; Increment transfer address
8237 PLP ; Restore flags, continue transfer
8238 BVS wait_tube_data_phase ; Continue outer transfer loop
fall through ↓

SCSI Request Sense command

Send a SCSI Request Sense command (opcode 3) to retrieve extended error information after a failed operation. Stores the 4-byte sense data in the error workspace.

On ExitAerror code from sense data (&FF if unrecoverable)
Xcorrupted
Ycorrupted
823A .scsi_request_sense←1← 81AC JMP
JSR scsi_start_command ; Select SCSI device
823D LDA #3 ; SCSI Request Sense command = 3
823F TAX ; X=3: receive 4 sense bytes
8240 TAY ; Y=3: send 3 more command bytes
8241 JSR scsi_send_byte_a ; Send command byte
8244 LDA wksp_current_drive_hi ; Get LUN bits from drive number
8247 AND #&e0 ; Isolate LUN (bits 5-7)
8249 JSR scsi_send_byte_a ; Send LUN byte
824C .send_zero_bytes_loop←1← 8250 BPL
JSR scsi_send_byte_a ; Send remaining zero bytes
824F DEY ; Decrement byte counter
8250 BPL send_zero_bytes_loop ; Loop for 3 bytes
8252 .receive_sense_data_loop←1← 825C BPL
JSR scsi_wait_for_req ; Receive sense data bytes
8255 LDA fred_hard_drive_0 ; Read sense data from SCSI bus
8258 STA wksp_err_sector,x ; Store in error workspace
825B DEX ; Next byte
825C BPL receive_sense_data_loop ; Loop for 4 bytes
825E LDA wksp_current_drive_hi ; Get drive LUN bits
8261 AND #&e0 ; Isolate LUN
8263 ORA wksp_err_sector_hi ; Merge with error sector high byte
8266 STA wksp_err_sector_hi ; Store back
8269 JSR scsi_wait_for_req ; Wait for status phase
826C LDX wksp_err_code ; Get error code from workspace
826F LDA fred_hard_drive_0 ; Read status byte
8272 JSR scsi_wait_for_req ; Wait for message phase
8275 LDY fred_hard_drive_0 ; Read message byte
8278 BNE unrecoverable_scsi_error ; Message byte non-zero? Error
827A AND #2 ; Check status error bit
827C BNE unrecoverable_scsi_error ; Error bit set? Return error
827E TXA ; Transfer error code to A
827F JMP mask_error_code ; Return with error code
8282 .unrecoverable_scsi_error←3← 81A4 JMP← 8278 BNE← 827C BNE
LDA #&ff ; Unrecoverable SCSI error
8284 JMP mask_error_code ; Return &FF

Execute disc command from workspace control block

Execute a disc command using the control block at &1015. Generates a BRK error if the command fails.

8287 .exec_disc_op_from_wksp←8← 89CA JSR← 8A1A JSR← 8FAE JSR← 94C9 JMP← 9722 JSR← 973C JSR← A7F2 JMP← A813 JSR
LDX #&15 ; Point to workspace disc op block
8289 LDY #&10 ; Y=&10: workspace page
fall through ↓

Execute disc command and check for error

Execute disc command via command_exec_xy. On error, generate a BRK (never returns). On success, restore saved drive and return.

On EntryXcontrol block address low byte
Ycontrol block address high byte
828B .exec_disc_command←8← 8888 JSR← 88A5 JSR← 89E7 JSR← 8FD3 JSR← 97A5 JMP← A81A JMP← B50A JSR← B571 JSR
JSR command_exec_xy ; Execute disc command
828E BNE generate_error ; Error? Generate BRK
8290 RTS ; Return (success)
8291 .restore_drive_after_op←2← 829C BEQ← 82A0 BEQ
LDA wksp_saved_drive ; Restore saved drive number
8294 STA wksp_current_drive ; Set current drive
8297 JMP bad_parms_error ; Restore drive and raise error

Generate a BRK error

Generate a BRK error from the disc error code in A. Never returns to caller.

On EntryASCSI/disc error code
829A .generate_error←6← 828E BNE← 82FE BNE← 8A42 JMP← AB48 JMP← AB60 JMP← ACAF JMP
CMP #&25 ; Error code &25 = drive not present?
829C BEQ restore_drive_after_op ; Yes, restore drive and raise error
829E CMP #&65 ; Error code &65 = volume error?
82A0 BEQ restore_drive_after_op ; Yes, restore drive and raise error
82A2 CMP #&6f ; Error code &6F = drive overrun?
82A4 BNE check_escape_condition ; No, check other error codes
82A6 .error_escape_ack_invalidate_reload_fsm←1← 8086 JMP
LDA #osbyte_acknowledge_escape ; Acknowledge Escape condition
82A8 JSR osbyte ; Clear escape condition and perform escape effects
82AB JSR invalidate_fsm_and_dir ; Invalidate FSM and directory
82AE JSR reload_fsm_and_dir_then_brk
82B1 EQUB &11 ; Error &11: Escape
82B2 EQUS "Escape", &00
82B9 .check_escape_condition←1← 82A4 BNE
CMP #4 ; Error code &04 = drive not ready?
82BB BNE translate_scsi_error ; Not drive overrun, check other codes
82BD JSR reload_fsm_and_dir_then_brk
82C0 EQUB &CD ; Error &CD: Drive not ready
82C1 EQUS "Drive not ready", &00
82D1 .translate_scsi_error←1← 82BB BNE
CMP #&40 ; Error code &40 = write protected?
82D3 BEQ store_error_sector ; Yes, generate Disc protected error
82D5 JSR save_wksp_and_return ; Convert SCSI error to disc error
82D8 TAX ; X = suffix control
82D9 JSR generate_error_suffix_x
82DC EQUB &C7 ; Error &C7: Disc error
82DD EQUS "Disc error", &00
82E8 .store_error_sector←1← 82D3 BEQ
JSR generate_disc_error ; Write protected: save drive state
82EB EQUB &C9 ; Error &C9: Disc protected
82EC EQUS "Disc protected", &00
fall through ↓

Send one byte during SCSI command phase

Wait for SCSI REQ, then write byte A to the SCSI data bus. Returns only on success; generates BRK on error.

On EntryASCSI command byte to send
82FB .scsi_send_cmd_byte←6← AACE JSR← AAD7 JSR← AADD JSR← AAE3 JSR← AAE8 JSR← AAED JMP
JSR scsi_send_byte_wrapper ; Send byte A via SCSI
82FE BNE generate_error ; Non-zero result: SCSI error
8300 RTS ; Return (success)
8301 .scsi_send_byte_wrapper←1← 82FB JSR
JSR scsi_send_byte_a ; Send byte and return status
8304 RTS ; Return

Wait while files are being ensured

If zp_flags bit 0 (ensuring) is set, loop until it clears. This prevents disc operations during file ensure operations.

8305 .wait_ensuring←9← 8089 JSR← 830C BNE← 8AF5 JSR← 9E7F JSR← A347 JSR← A98F JSR← AAC7 JSR← B00D JSR← B39B JSR
LDA #1 ; A=1: test bit 0 of zp_flags
8307 PHP ; Save flags
8308 CLI ; Enable interrupts briefly
8309 PLP ; Restore flags
830A BIT zp_adfs_flags ; Bit 0 set (ensuring)?
830C BNE wait_ensuring ; Yes, keep waiting
830E RTS ; Return

Wait for SCSI REQ signal

Poll the SCSI status register until the REQ bit is asserted, indicating the target is ready for the next bus phase. Preserves A; N and V flags reflect SCSI bus phase.

On ExitApreserved
Xpreserved
Ypreserved
NC/D bit from SCSI status (set = command phase)
VMSG bit from SCSI status (set = message phase)
830F .scsi_wait_for_req←15← 812C JSR← 8140 JSR← 8159 JSR← 818D JSR← 8193 JSR← 81BE JSR← 8200 JSR← 8252 JSR← 8269 JSR← 8272 JSR← 831B JSR← 8B92 JSR← AB54 JSR← AB99 JSR← ACB7 JSR
PHA ; Save A on stack
8310 .poll_req_loop←1← 8315 BEQ
JSR scsi_get_status ; Read SCSI status
8313 AND #&20 ; Check REQ bit (bit 5)
8315 BEQ poll_req_loop ; Loop until REQ asserted
8317 PLA ; Restore A
8318 BIT zp_scsi_status ; Test C/D and MSG bits via BIT
831A RTS ; Return

Send byte A on SCSI bus after REQ

Wait for SCSI REQ then write A to the SCSI data register. May not return if MSG phase detected (unwinds call stack to command_done).

On EntryAbyte to send on SCSI bus
831B .scsi_send_byte_a←7← 8118 JSR← 8129 JSR← 8241 JSR← 8249 JSR← 824C JSR← 8301 JSR← 8B77 JSR
JSR scsi_wait_for_req ; Wait for SCSI REQ
831E BVS write_scsi_data_byte ; MSG phase? Abort command
8320 STA fred_hard_drive_0 ; Write data byte to SCSI bus
8323 LDA #0 ; A=0: success
8325 RTS ; Return (byte sent OK)
8326 .write_scsi_data_byte←1← 831E BVS
PLA ; Pop 2 return addresses from stack
8327 PLA ; Pop one return address
8328 JMP command_done ; Jump to status/message phase handler

Generate disc error with state recovery

Save the current drive state, reload FSM and directory, then generate a BRK error. The inline error number and message string follow the JSR instruction.

832B .generate_disc_error←6← 82E8 JSR← 85C8 JSR← 8656 JSR← 8664 JSR← 8FFA JSR← A6F9 JSR
LDX wksp_saved_drive ; Check if drive was already saved
832E INX ; Non-zero means drive already saved
832F BNE reload_fsm_and_dir_then_brk ; Already saved, just raise the error
8331 LDX wksp_alt_sector_hi ; Check alternative workspace
8334 INX ; Increment: non-zero?
8335 BNE check_for_on_channel ; Yes, skip CSD restore
8337 LDY #2 ; Copy CSD sector info to workspace
8339 .copy_error_string_loop←1← 8340 BPL
LDA wksp_csd_sector_lo,y ; Get CSD sector byte
833C STA wksp_csd_drive_sector,y ; Copy to CSD drive sector workspace
833F DEY ; Next byte
8340 BPL copy_error_string_loop ; Loop for 3 bytes
8342 .check_for_on_channel←1← 8335 BNE
LDA wksp_current_drive ; Save current drive for error message
8345 STA wksp_saved_drive ; Save current drive for error msg
fall through ↓

Reload FSM and directory then raise error

Reload the free space map and current directory from disc, then generate a BRK error. Used after operations that may have left the in-memory copies inconsistent.

8348 .reload_fsm_and_dir_then_brk←26← 82AE JSR← 82BD JSR← 832F BNE← 8737 JSR← 8982 JSR← 8BD7 JSR← 8BF0 JSR← 8D16 JSR← 8D53 JSR← 8DDE JSR← 8E1E JSR← 915C JSR← 91AD JSR← 91D7 JSR← 95A4 JSR← 99DA JSR← A00A JSR← A29B JSR← A389 JSR← A3F7 JSR← AA35 JSR← ACE9 JSR← AD5B JSR← B09D JSR← B1EB JSR← B4AE JSR
JSR save_wksp_and_return ; Ensure directory/FSM state is clean
834B LDA zp_adfs_flags ; Clear FSM-inconsistent flag (bit 4)
834D AND #&ef ; Clear FSM inconsistent flag
834F STA zp_adfs_flags ; Store updated flags
fall through ↓

Generate error without drive/sector suffix

Generate a BRK error from the disc error code without appending the drive:sector suffix.

8351 .generate_error_no_suffix←2← A6CD JSR← A73D JSR
LDX #0
fall through ↓

Generate error with suffix control in X

Generate a BRK error from the inline error data following the JSR. X controls whether the drive:sector suffix is appended. Never returns.

On EntryXnon-zero to append drive:sector suffix
8353 .generate_error_suffix_x←2← 82D9 JSR← ABB2 JSR
PLA ; Pop return address (inline data ptr)
8354 STA zp_mem_ptr_lo ; Store inline data pointer low
8356 PLA ; High byte of inline data address
8357 STA zp_mem_ptr_hi ; Store inline data pointer high
8359 LDA zp_adfs_flags ; Clear FSM-inconsistent flag (bit 4)
835B AND #&ef ; Mask off bit 4
835D STA zp_adfs_flags ; Store cleared flags
835F LDY #0 ; Y=0: index into inline error data
8361 .copy_error_msg_loop←1← 8367 BNE
INY ; Copy inline error message to page 1
8362 LDA (zp_mem_ptr_lo),y ; Read error msg byte from inline data
8364 STA brk_error_block,y ; Store in error block on page 1
8367 BNE copy_error_msg_loop ; Loop until zero terminator
8369 TXA ; X=0 means no suffix wanted
836A BEQ generate_error_skip_no_suffix ; Skip suffix, go to channel check
836C LDA #&20 ; Append space before suffix
836E STA brk_error_block,y ; Store space in error block
8371 TXA ; Check if suffix is hex or decimal
8372 CMP #&30 ; Suffix value >= '0'?
8374 BCS check_colon_suffix ; Yes, check for colon
8376 .append_hex_suffix←1← 837E BCS
JSR error_append_hex ; Append as hex number
8379 JMP append_drive_sector_suffix ; Jump to hex formatting
837C .check_colon_suffix←1← 8374 BCS
CMP #&3a ; Suffix value >= ':'?
837E BCS append_hex_suffix ; Yes, append as hex
8380 JSR error_append_dec ; Append as decimal number
8383 .append_drive_sector_suffix←1← 8379 JMP
LDX #4 ; Copy reversed ' at :' suffix
8385 .copy_at_string_loop←1← 838D BPL
INY ; Next position
8386 LDA str_at,x ; Get char from reversed string
8389 STA brk_error_block,y ; Store in error block
838C DEX ; Next character in reversed string
838D BPL copy_at_string_loop ; Loop for 5 chars of ' at :'
838F LDA wksp_err_sector_hi ; Get drive number from error sector
8392 ASL ; Shift drive bits into low nibble
8393 ROL ; Rotate drive bits to low nibble
8394 ROL ; Second rotate
8395 ROL ; Third rotate
8396 JSR hex_digit ; Convert to hex digit character
8399 INY ; Advance position
839A STA brk_error_block,y ; Store drive digit
839D LDA #&2f ; Append '/' separator
839F INY ; Advance to next position
83A0 STA brk_error_block,y ; Store separator in error block
83A3 LDA wksp_err_sector_hi ; Get sector high byte
83A6 AND #&1f ; Mask to 5-bit sector address
83A8 LDX #2 ; X=2: output 3 bytes of sector addr
83AA BNE append_sector_hex ; Always branch to loop entry
83AC .append_sector_bytes_loop←1← 83B3 BPL
LDA wksp_err_sector,x ; Get next sector byte from workspace
83AF .append_sector_hex←1← 83AA BNE
JSR error_append_hex ; Append as two hex digits
83B2 DEX ; Next byte
83B3 BPL append_sector_bytes_loop ; Loop for 3 sector bytes
83B5 INY ; Advance past suffix
83B6 LDA #0 ; Zero-terminate the error string
83B8 STA brk_error_block,y ; Store zero terminator
83BB .generate_error_skip_no_suffix←1← 836A BEQ
LDA wksp_cur_channel ; Check for open channel suffix
83BE BEQ raise_brk_error ; No channel active, skip
83C0 LDX #&0b ; X=&0B: copy 12-char ' on channel '
83C2 DEY ; Back up one position
83C3 .append_channel_suffix_loop←1← 83CB BPL
LDA str_on_channel,x ; Get char from reversed string
83C6 INY ; Advance position
83C7 STA brk_error_block,y ; Store in error block
83CA DEX ; Next character in reversed string
83CB BPL append_channel_suffix_loop ; Loop for 12 chars
83CD LDA wksp_cur_channel ; Get channel number
83D0 JSR error_append_dec ; Append as decimal digits
83D3 TYA ; Save current position
83D4 PHA ; Push Y on stack
83D5 LDA #&c6 ; OSBYTE &C6: read EXEC file handle
83D7 STA wksp_compaction_reported ; Store OSBYTE number in workspace
83DA JSR osbyte_y_ff_x_00 ; OSBYTE &C6: read/write EXEC handle
83DD CPX wksp_cur_channel ; Is EXEC on this channel?
83E0 PHP ; Save flags for comparison result
83E1 LDX #&99 ; X=&99: EXEC string address
83E3 PLP ; Restore flags
83E4 BEQ close_exec_or_spool ; Yes, close EXEC file
83E6 CPY wksp_cur_channel ; Is SPOOL on this channel?
83E9 BNE restore_error_position ; No, skip
83EB LDX #&9c ; Close SPOOL file (ptr at &9C)
83ED .close_exec_or_spool←1← 83E4 BEQ
JSR oscli_at_x ; Execute close via OSCLI
83F0 .restore_error_position←1← 83E9 BNE
PLA ; Restore Y (position)
83F1 TAY ; Transfer back to Y
83F2 .raise_brk_error←1← 83BE BEQ
LDA wksp_error_suppress ; Check for additional error handling
83F5 BNE copy_brk_block_loop ; Non-zero: skip workspace update
83F7 JSR load_dir_for_drive ; Update workspace checksum
83FA .copy_brk_block_loop←1← 83F5 BNE
LDA #0 ; Store BRK opcode at start of page 1
83FC STA brk_error_block ; Store BRK opcode (&00) at start
83FF STA brk_error_block_1,y ; Zero-terminate after channel suffix
8402 JSR release_tube ; Release Tube before raising error
8405 LDA brk_error_block_1 ; Check error code
8408 CMP #&c7 ; Is it &C7 (Disc error)?
840A BNE run_exec_or_spool ; No, just execute the BRK
840C LDX #&9c ; Close SPOOL before disc error
840E JSR oscli_at_x
8411 LDX #&99 ; Close EXEC before disc error
8413 JSR oscli_at_x
8416 JSR invalidate_fsm_and_dir ; Invalidate FSM/dir after disc error
8419 .run_exec_or_spool←1← 840A BNE
JMP brk_error_block ; Jump to BRK block on page 1

Error suffix string constants

Reversed string constants used when building error messages. str_at contains ': ta ' (reversed ' at :') appended to disc error messages, and str_on_channel contains ' lennahc no ' (reversed ' on channel') for channel-specific errors.

841C .str_at←1← 8386 LDA
EQUS ": ta "
8421 .str_on_channel←1← 83C3 LDA
EQUS " lennahc no "

Append byte as two hex digits to error block

Write the byte in A as two ASCII hex digits into the error block at the current position Y.

On EntryAbyte value to convert to hex
Yindex into brk_error_block
On ExitAASCII hex digit of low nibble
Xpreserved
Yadvanced by 2
842D .error_append_hex←2← 8376 JSR← 83AF JSR
PHA ; Save byte value
842E LSR ; Shift high nibble to low nibble
842F LSR ; (continued)
8430 LSR ; (continued)
8431 LSR ; (continued)
8432 JSR store_hex_nibble ; Output high nibble as hex digit
8435 PLA ; Restore original byte
8436 .store_hex_nibble←1← 8432 JSR
JSR hex_digit ; Convert low nibble and output
8439 INY ; Advance position in error block
843A STA brk_error_block,y ; Store hex digit character
843D RTS ; Return

Convert 4-bit value to ASCII hex digit

Convert a 4-bit value in A to an ASCII hex character ('0'-'9' or 'A'-'F'). The low nibble of A is used.

On EntryAvalue with hex digit in low nibble
On ExitAASCII hex character ('0'-'9' or 'A'-'F')
Xpreserved
Ypreserved
843E .hex_digit←3← 8396 JSR← 8436 JSR← 9324 JSR
AND #&0f ; Isolate low nibble
8440 ORA #&30 ; Merge with &30 for ASCII '0'-'?'
8442 CMP #&3a ; Result > '9' (i.e. A-F)?
8444 BCC return_6 ; No, digit is 0-9, done
8446 ADC #6 ; Add 7 to get 'A'-'F' (6 + carry)
8448 .return_6←1← 8444 BCC
RTS ; Return

Append byte as decimal digits to error block

Write the byte in A as up to three decimal digits into the error block at the current position Y, suppressing leading zeros.

On EntryAbyte value to convert to decimal
Yindex into brk_error_block
On ExitAcorrupted
Xcorrupted
Yadvanced past decimal digits
8449 .error_append_dec←2← 8380 JSR← 83D0 JSR
BIT divide_loop ; Set V flag for leading zero suppress
844C LDX #&64 ; X=100: divide by hundreds
844E JSR print_decimal_digit ; Output hundreds digit
8451 LDX #&0a ; X=10: divide by tens
8453 JSR print_decimal_digit ; Output tens digit
8456 CLV ; Clear V: always show units digit
8457 LDX #1 ; X=1: divide by ones
8459 .print_decimal_digit←2← 844E JSR← 8453 JSR
PHP ; Save V flag (leading zero suppress)
845A STX zp_mem_ptr_hi ; Store divisor
845C LDX #&2f ; X='/': ASCII digit will be X+1
845E SEC ; Set carry for subtraction
845F .divide_loop←2← 8449 BIT← 8462 BCS
INX ; Increment quotient digit
8460 SBC zp_mem_ptr_hi ; Subtract divisor
8462 BCS divide_loop ; Loop while result >= 0
8464 ADC zp_mem_ptr_hi ; Add divisor back (went too far)
8466 PLP ; Restore V flag
8467 PHA ; Save remainder
8468 TXA ; Get ASCII digit
8469 BVC store_digit ; V set: suppress leading zeros
846B CMP #&30 ; Is it '0'?
846D BEQ skip_leading_zero ; Yes, skip (suppress leading zero)
846F CLV ; Not zero: clear V, show from now on
8470 .store_digit←1← 8469 BVC
INY ; Advance position
8471 STA brk_error_block,y ; Store decimal digit
8474 .skip_leading_zero←1← 846D BEQ
PLA ; Restore remainder
8475 RTS ; Return

Mark FSM and directory as invalid

Set flags to indicate that the in-memory free space map and directory buffer may be stale and need reloading from disc.

On ExitAzero
Xcorrupted
Yzero
8476 .invalidate_fsm_and_dir←3← 82AB JSR← 8416 JSR← 9C0F JSR
LDX #&0c ; X=&0C: clear 12 workspace bytes
8478 LDA #&ff ; A=&FF: invalid marker
847A .invalidate_sectors_loop←1← 8481 BNE
STA wksp_csd_sector_temp,x ; Invalidate drive/sector workspace
847D STA wksp_csd_sector,x ; Invalidate CSD/lib/prev sectors
8480 DEX ; Next byte
8481 BNE invalidate_sectors_loop ; Loop for 12 bytes
8483 JSR copy_default_dir_name ; Reset CSD name to default
8486 JSR copy_default_dir_name
8489 LDY #0 ; Y=0: loop counter
848B TYA ; A=0: zero fill
848C .zero_buffers_loop←1← 8496 BNE
STA fsm_sector_1,y ; Zero FSM sector 1 buffer
848F STA fsm_sector_0,y ; Zero FSM sector 0 buffer
8492 STA dir_buffer,y ; Zero directory buffer
8495 INY ; Next byte
8496 BNE zero_buffers_loop ; Loop for 256 bytes
8498 RTS ; Return

OSCLI abbreviation strings

CR-terminated command abbreviation strings passed to OSCLI: str_exec_abbrev = 'E.' (*EXEC), str_spool_abbrev = 'SP.' (*SPOOL). Also includes str_yes (reversed 'YES' + CR for *DESTROY confirmation) and str_hugo (NUL + 'Hugo' directory identity string).

8499 .str_exec_abbrev
EQUS "E.", &0D ; "E." + CR: *EXEC abbreviation
849C .str_spool_abbrev
EQUS "SP.", &0D ; "SP." + CR: *SPOOL abbreviation

Call OSBYTE to read current value

Call OSBYTE with Y=&FF and X=0 to read the current value of the variable specified in A.

On EntryAOSBYTE number
On ExitAcorrupted
XOSBYTE result low byte
YOSBYTE result high byte
84A0 .osbyte_y_ff_x_00←3← 83DA JSR← 9BA7 JSR← 9C79 JSR
LDY #&ff ; Y=&FF: read current value
84A2 .osbyte_x_00
LDX #0 ; X=0: no modification
84A4 JMP osbyte ; Call OSBYTE

Execute OSCLI with string at X

Call OSCLI with the command string address in X (low byte).

On EntryXlow byte of command string address in page &84
On ExitAcorrupted
Xcorrupted
Ycorrupted
84A7 .oscli_at_x←3← 83ED JSR← 840E JSR← 8413 JSR
LDY #&84 ; Y=&84: high byte (string in this ROM)
84A9 JMP oscli ; Call OSCLI with (X,Y) address
84AC .str_yes←1← 9A18 CMP
EQUS &0D ; CR + "SEY": reversed "YES" + CR
84AD EQUS "SEY"
84B0 .str_hugo←2← 95F9 LDA← A6F1 LDA
EQUS &00 ; NUL + "Hugo": directory identity
84B1 EQUS "Hugo"

Release disc space back to free space map

Return the disc space occupied by the object at wksp_object_sector (3 bytes) with size at &1037-&1039 (3 bytes) back to the free space map. Searches for the correct position in the sorted FSM and merges with adjacent free entries where possible.

On EntryNOTEwksp_object_sector and wksp_object_size set in workspace
On ExitAcorrupted
Xcorrupted
Ycorrupted
84B5 .release_disc_space←5← 8F49 JMP← 9235 JSR← 9892 JSR← AF08 JSR← B45F JSR
LDA wksp_object_size ; Check if object has non-zero size
84B8 ORA wksp_object_size_mid ; OR with size mid byte
84BB ORA wksp_object_size_hi ; OR with size high byte
84BE BNE find_fsm_position ; Size is zero, nothing to release
84C0 RTS ; Size is zero: return
84C1 .find_fsm_position←1← 84BE BNE
LDX #0 ; X=0: start of FSM entries
84C3 .scan_fsm_entries_loop←1← 84DA BNE
CPX fsm_s1_end_of_list_ptr ; Past end of free space list?
84C6 BCS insert_new_fsm_entry ; Yes, insert at end
84C8 INX ; Advance X by 3 (entry size)
84C9 INX ; Advance X (2nd byte of entry)
84CA INX ; Advance X (3rd byte of entry)
84CB STX zp_mem_ptr_lo ; Save X for backtrack
84CD LDY #2 ; Y=2: compare 3-byte address
84CF .compare_fsm_addr_loop←1← 84DF BPL
DEX ; Back up X to compare bytes
84D0 LDA fsm_sector_0,x ; Get FSM entry address byte
84D3 CMP wksp_object_sector,y ; Compare with object sector byte
84D6 BCS check_exact_match ; FSM entry >= object? Found position
84D8 LDX zp_mem_ptr_lo ; Restore X, try next entry
84DA BNE scan_fsm_entries_loop ; Try next FSM entry
84DC .check_exact_match←1← 84D6 BCS
BNE found_insertion_point ; Exact match on this byte?
84DE DEY ; Compare next byte down
84DF BPL compare_fsm_addr_loop ; Continue comparing bytes
84E1 .found_insertion_point←1← 84DC BNE
LDX zp_mem_ptr_lo ; Back to entry start
84E3 DEX ; Back up to entry start
84E4 DEX ; 2nd byte back
84E5 DEX ; 3rd byte back
84E6 STX zp_mem_ptr_lo ; Save entry index for merge check
84E8 CLC ; C=0 for addition
84E9 PHP ; Save carry for multi-byte add
84EA LDY #0 ; Y=0: compare 3 address bytes
84EC .check_adjacent_to_next_loop←1← 8501 BNE
PLP ; Restore carry
84ED LDA wksp_object_sector,y ; Object sector + object size
84F0 ADC wksp_object_size,y ; Add object size byte
84F3 PHP ; Save carry
84F4 CMP fsm_sector_0,x ; Compare with FSM entry address
84F7 BEQ adjacent_next_byte ; Match? Object is adjacent to entry
84F9 PLP ; Restore carry after mismatch
84FA .insert_new_fsm_entry←1← 84C6 BCS
JMP check_merge_with_prev ; No match, insert new entry
84FD .adjacent_next_byte←1← 84F7 BEQ
INX ; Next compare byte
84FE INY ; Next object sector byte
84FF CPY #3 ; Compared all 3 bytes?
8501 BNE check_adjacent_to_next_loop ; No, continue comparing
8503 PLP ; Restore carry from addition
8504 LDX zp_mem_ptr_lo ; Get FSM entry index back
8506 BEQ add_size_to_existing_entry ; Entry 0: no preceding entry to merge
8508 CLC ; Clear carry for addition
8509 PHP ; Save carry for multi-byte add
850A LDY #0 ; Y=0: compare bytes of prev+size
850C .check_adjacent_to_prev_loop←1← 8523 BNE
PLP ; Restore carry
850D LDA fsm_s0_pre3,x ; Get prev entry address byte
8510 ADC fsm_s0_disc_size_mid,x ; Add prev entry length byte
8513 PHP ; Save carry
8514 CMP wksp_object_sector,y ; Compare prev+size with object sector
8517 BEQ adjacent_prev_byte ; Match: prev is adjacent (merge back)
8519 LDX zp_mem_ptr_lo ; No match: insert new entry
851B PLP ; Restore carry
851C JMP add_size_to_existing_entry ; Not adjacent: insert new entry
851F .adjacent_prev_byte←1← 8517 BEQ
INX ; Next byte
8520 INY ; Next object sector byte
8521 CPY #3 ; Compared all 3 bytes?
8523 BNE check_adjacent_to_prev_loop ; No, continue
8525 PLP ; Adjacent to prev: merge backward
8526 LDX zp_mem_ptr_lo ; Restore FSM index
8528 LDY #0 ; Y=0: add released size to prev length
852A CLC ; Clear carry for addition
852B PHP ; Save carry
852C .merge_with_prev_loop←1← 853B BNE
PLP ; Restore carry
852D LDA fsm_s0_disc_size_mid,x ; Get prev entry length byte
8530 ADC wksp_object_size,y ; Add released size byte
8533 STA fsm_s0_disc_size_mid,x ; Store updated length
8536 PHP ; Save carry for next byte
8537 INX ; Next entry byte
8538 INY ; Next size byte
8539 CPY #3 ; All 3 bytes?
853B BNE merge_with_prev_loop ; No, continue adding
853D PLP ; Restore carry
853E LDY #2 ; Y=2: check if merged entry is now
8540 LDX zp_mem_ptr_lo ; adjacent to the NEXT entry too
8542 CLC ; Clear carry for addition
8543 .check_triple_merge_loop←1← 854E BPL
LDA fsm_s0_disc_size_mid,x ; Get merged entry address byte
8546 ADC fsm_sector_1,x ; Add merged entry length byte
8549 STA fsm_s0_disc_size_mid,x ; Store sum (prev+released+next?)
854C INX ; Next byte
854D DEY ; Decrement counter
854E BPL check_triple_merge_loop ; Loop for 3 bytes
8550 .shift_entries_down_loop←1← 8562 BNE
CPX fsm_s1_end_of_list_ptr ; Check if past end of FSM list
8553 BCS shrink_fsm_list ; Yes: shrink list by removing entry
8555 LDA fsm_sector_1,x ; Get next entry length
8558 STA fsm_s0_disc_size_mid,x ; Store over current (shift down)
855B LDA fsm_sector_0,x ; Get next entry address
855E STA fsm_s0_pre3,x ; Store over current (shift down)
8561 INX ; Next entry
8562 BNE shift_entries_down_loop ; Loop shifting entries
8564 .shrink_fsm_list←1← 8553 BCS
DEX ; Adjust end-of-list pointer
8565 DEX ; Back 3 bytes
8566 DEX ; Back 3 bytes total
8567 STX fsm_s1_end_of_list_ptr ; Store new end-of-list pointer
856A RTS ; Return

Add released size to FSM entry

Copy the object sector address and add the released block size to an existing FSM length entry, merging adjacent free regions.

On EntryXFSM entry index into sector 0/1 buffers
On ExitAcorrupted
Xcorrupted
Y3
856B .add_size_to_existing_entry←2← 8506 BEQ← 851C JMP
LDY #0 ; Y=0: copy+add 3-byte address+length
856D CLC ; Clear carry for addition
856E PHP ; Save carry for multi-byte operation
856F .merge_forward_loop←1← 8584 BNE
LDA wksp_object_sector,y ; Get object sector byte
8572 STA fsm_sector_0,x ; Store as FSM entry address
8575 PLP ; Restore carry from prev iteration
8576 LDA fsm_sector_1,x ; Get current FSM length byte
8579 ADC wksp_object_size,y ; Add released size byte
857C STA fsm_sector_1,x ; Store updated length
857F PHP ; Save carry
8580 INY ; Next byte
8581 INX ; Next FSM byte
8582 CPY #3 ; All 3 bytes?
8584 BNE merge_forward_loop ; No, continue
8586 PLP ; Restore final carry
8587 RTS ; Return (merge complete)
8588 .check_merge_with_prev←1← 84FA JMP
LDX zp_mem_ptr_lo ; Get FSM entry index
858A BEQ insert_new_entry ; Entry 0: no predecessor, insert new
858C CLC ; Clear carry for addition
858D PHP ; Save carry for multi-byte add
858E LDY #0 ; Y=0: compare prev+size with object
8590 .compare_prev_plus_size_loop←1← 85A5 BNE
PLP ; Restore carry
8591 LDA fsm_s0_pre3,x ; Get prev entry address byte
8594 ADC fsm_s0_disc_size_mid,x ; Add prev entry length byte
8597 PHP ; Save carry
8598 CMP wksp_object_sector,y ; Compare with object sector byte
859B BEQ merge_size_into_prev ; Match: prev is adjacent
859D PLP ; Restore carry, no match
859E JMP insert_new_entry ; Not adjacent: insert new entry
85A1 .merge_size_into_prev←1← 859B BEQ
INX ; Next byte
85A2 INY ; Next object byte
85A3 CPY #3 ; All 3 bytes?
85A5 BNE compare_prev_plus_size_loop ; No, continue
85A7 PLP ; Restore carry (all matched)
85A8 LDY #0 ; Y=0: add released size to prev
85AA LDX zp_mem_ptr_lo ; Get FSM entry index
85AC CLC ; Clear carry for addition
85AD PHP ; Save carry for multi-byte add
85AE .add_size_to_prev_loop←1← 85BD BNE
PLP ; Restore carry
85AF LDA fsm_s0_disc_size_mid,x ; Get prev entry length byte
85B2 ADC wksp_object_size,y ; Add released size byte
85B5 STA fsm_s0_disc_size_mid,x ; Store updated length
85B8 PHP ; Save carry
85B9 INX ; Next FSM byte
85BA INY ; Next size byte
85BB CPY #3 ; All 3 bytes?
85BD BNE add_size_to_prev_loop ; No, continue
85BF PLP ; Restore carry
85C0 RTS ; Return (merge with prev complete)

Insert new entry into FSM

Check for room in the FSM. If full, raise Map full error. Otherwise shift entries up and insert the new entry at the correct sorted position.

On EntryNOTEzp_mem_ptr_lo = insertion point index in FSM
On ExitAcorrupted
Xcorrupted
Ycorrupted
85C1 .insert_new_entry←2← 858A BEQ← 859E JMP
LDA fsm_s1_end_of_list_ptr ; Get end-of-list pointer
85C4 CMP #&f6 ; Room for new entry (< &F6)?
85C6 BCC shift_entries_up_start ; Yes: proceed with insert
85C8 JSR generate_disc_error ; Save drive state and raise error
85CB EQUB &99 ; Error &99: Map full
85CC EQUS "Map full", &00
85D5 .shift_entries_up_start←1← 85C6 BCC
LDX fsm_s1_end_of_list_ptr ; Get end-of-list pointer
85D8 .shift_entries_up_loop←1← 85E9 JMP
CPX zp_mem_ptr_lo ; Reached insertion point?
85DA BEQ store_new_entry ; Yes: insert here
85DC DEX ; Shift entries up by 3 bytes
85DD LDA fsm_sector_0,x ; Get FSM address byte to shift
85E0 STA fsm_s0_first_length,x ; Store 3 bytes higher
85E3 LDA fsm_sector_1,x ; Get FSM length byte to shift
85E6 STA fsm_s1_first_length,x ; Store 3 bytes higher
85E9 JMP shift_entries_up_loop ; Continue shifting
85EC .store_new_entry←1← 85DA BEQ
LDY #0 ; Y=0: store new entry at gap
85EE .store_new_entry_loop←1← 85FE BNE
LDA wksp_object_sector,y ; Get object sector byte
85F1 STA fsm_sector_0,x ; Store as FSM entry address
85F4 LDA wksp_object_size,y ; Get released size byte
85F7 STA fsm_sector_1,x ; Store as FSM entry length
85FA INX ; Next byte
85FB INY ; Next source byte
85FC CPY #3 ; All 3 bytes?
85FE BNE store_new_entry_loop ; No, continue
8600 LDA fsm_s1_end_of_list_ptr ; Get end-of-list pointer
8603 ADC #2 ; Add 3 (new entry size)
8605 STA fsm_s1_end_of_list_ptr ; Store updated pointer
8608 .return_7←1← 8617 BEQ
RTS ; Return

Sum all free space in FSM

Walk the FSM entries accumulating the 3-byte length of each free extent into workspace &105D-&105F.

8609 .sum_free_space←2← 8642 JSR← A1B7 JSR
LDX #0 ; X=0: start scanning FSM
860B STX wksp_access_accum ; Clear accumulator low byte
860E STX wksp_access_accum_1 ; Clear accumulator mid byte
8611 STX wksp_free_space_total ; Clear accumulator high byte
8614 .sum_fsm_entries_loop←1← 862F JMP
CPX fsm_s1_end_of_list_ptr ; Past end of FSM entries?
8617 BEQ return_7 ; Yes: return total
8619 LDY #0 ; Y=0: sum this 3-byte entry
861B CLC ; Clear carry for addition
861C PHP ; Save carry for multi-byte add
861D .sum_entry_bytes_loop←1← 862C BNE
PLP ; Restore carry
861E LDA fsm_sector_1,x ; Get FSM length byte
8621 ADC wksp_access_accum,y ; Add to accumulator
8624 STA wksp_access_accum,y ; Store updated accumulator
8627 PHP ; Save carry for next byte
8628 INY ; Next accumulator byte
8629 INX ; Next FSM byte
862A CPY #3 ; All 3 bytes?
862C BNE sum_entry_bytes_loop ; No, continue
862E PLP ; Restore carry
862F JMP sum_fsm_entries_loop ; Loop for next FSM entry

Allocate disc space from free space map

Find the best-fit free entry for the requested size at &103D-&103F. Generates Disc full or Compaction required errors if allocation is not possible.

8632 .allocate_disc_space←3← 8F55 JSR← 9895 JSR← AF0B JSR
LDX #&ff ; X=&FF: no best-fit entry yet
8634 STX zp_mem_ptr_hi ; Store as best-fit index
8636 INX
8637 .scan_for_best_fit←1← 8705 JMP
CPX fsm_s1_end_of_list_ptr ; Past end of FSM entries?
863A BCC compare_entry_size ; No: check this entry
863C LDX zp_mem_ptr_hi ; Get best-fit index
863E CPX #&ff ; Still &FF (no fit found)?
8640 BNE use_best_fit_entry ; Found a fit: use it
8642 JSR sum_free_space ; No fit: sum all free space
8645 LDY #0 ; Y=0: compare total vs requested
8647 LDX #2 ; X=2: compare 3 bytes
8649 SEC ; Set carry for subtraction
864A .compare_total_vs_requested←1← 8652 BPL
LDA wksp_access_accum,y ; Get total free space byte
864D SBC wksp_alloc_size,y ; Subtract requested size byte
8650 INY ; Next byte
8651 DEX ; Next requested byte
8652 BPL compare_total_vs_requested ; All 3 bytes?
8654 BCS compaction_required_error ; Total >= requested: space exists
8656 .disc_full_error←2← 8F3A JMP← AED4 JMP
JSR generate_disc_error ; Not enough: Disc full error
8659 EQUB &C6 ; Error &C6: Disc full
865A EQUS "Disc full", &00
8664 .compaction_required_error←1← 8654 BCS
JSR generate_disc_error ; Compaction needed: error
8667 EQUB &98 ; Error &98: Compaction required
8668 EQUS "Compaction required", &00
867C .use_best_fit_entry←1← 8640 BNE
LDY #2 ; Y=2: copy best-fit entry sector addr
867E .copy_allocated_sector_loop←1← 8686 BPL
DEX ; Back up to entry start
867F LDA fsm_sector_0,x ; Get FSM address byte
8682 STA wksp_alloc_sector,y ; Store as allocated sector
8685 DEY ; Next byte
8686 BPL copy_allocated_sector_loop ; Loop for 3 bytes
8688 INY ; Y=1 (adjusted for carry)
8689 LDX zp_mem_ptr_hi ; Restore best-fit index
868B CLC ; Clear carry for addition
868C PHP ; Save carry
868D .advance_entry_addr_loop←1← 869C BNE
PLP ; Restore carry
868E LDA fsm_s0_pre3,x ; Get entry address byte
8691 ADC wksp_alloc_size,y ; Add requested size to advance addr
8694 STA fsm_s0_pre3,x ; Store updated entry address
8697 PHP ; Save carry
8698 INX ; Next entry byte
8699 INY ; Next requested byte
869A CPY #3 ; All 3 bytes?
869C BNE advance_entry_addr_loop ; No, continue
869E PLP ; Restore carry
869F LDY #0 ; Y=0: subtract requested from length
86A1 LDX zp_mem_ptr_hi ; Get best-fit index
86A3 SEC ; Set carry for subtraction
86A4 PHP ; Save carry
86A5 .subtract_from_length_loop←1← 86B4 BNE
PLP ; Restore carry
86A6 LDA fsm_s0_disc_size_mid,x ; Get entry length byte
86A9 SBC wksp_alloc_size,y ; Subtract requested size
86AC STA fsm_s0_disc_size_mid,x ; Store reduced length
86AF PHP ; Save carry
86B0 INX ; Next entry byte
86B1 INY ; Next requested byte
86B2 CPY #3 ; All 3 bytes?
86B4 BNE subtract_from_length_loop ; No, continue
86B6 PLP ; Restore carry
86B7 RTS ; Return (allocation complete)
86B8 .compare_entry_size←1← 863A BCC
LDY #2 ; Y=2: compare entry length backwards
86BA INX ; Advance X to entry+3
86BB INX ; 2nd byte
86BC INX ; 3rd byte
86BD STX zp_mem_ptr_lo ; Save entry end index
86BF .compare_size_bytes_loop←1← 86CB BPL
DEX ; Back up one byte
86C0 LDA fsm_sector_1,x ; Get entry length byte
86C3 CMP wksp_alloc_size,y ; Compare with requested size
86C6 BCC continue_scanning ; Entry < requested: too small
86C8 BNE check_if_first_fit ; Not equal: entry is larger
86CA DEY ; Next byte (decreasing Y)
86CB BPL compare_size_bytes_loop ; Loop for 3 bytes
86CD LDX zp_mem_ptr_lo ; Exact match: use this entry
86CF LDY #2 ; Y=2: copy entry address
86D1 .copy_exact_match_addr_loop←1← 86D9 BPL
DEX ; Back up
86D2 LDA fsm_sector_0,x ; Get entry address byte
86D5 STA wksp_alloc_sector,y ; Store as allocated sector
86D8 DEY ; Next byte
86D9 BPL copy_exact_match_addr_loop ; Loop for 3 bytes
86DB LDX zp_mem_ptr_lo ; Restore entry index
86DD .remove_exact_entry_loop←1← 86EF BNE
CPX fsm_s1_end_of_list_ptr ; Past end of entries?
86E0 BCS shrink_list_after_exact ; Yes: shrink list
86E2 LDA fsm_sector_0,x ; Shift entries down
86E5 STA fsm_s0_pre3,x ; Store 3 bytes lower (addresses)
86E8 LDA fsm_sector_1,x ; Get length entry to shift
86EB STA fsm_s0_disc_size_mid,x ; Store 3 bytes lower (lengths)
86EE INX ; Next entry
86EF BNE remove_exact_entry_loop ; Loop shifting entries
86F1 .shrink_list_after_exact←1← 86E0 BCS
LDA fsm_s1_end_of_list_ptr ; Get end-of-list pointer
86F4 SBC #3 ; Subtract 3 (removed entry)
86F6 STA fsm_s1_end_of_list_ptr ; Store updated pointer
86F9 RTS ; Return (exact match used)
86FA .check_if_first_fit←1← 86C8 BNE
LDX zp_mem_ptr_hi ; Get current best-fit
86FC INX ; X+1: was &FF (no fit yet)?
86FD BNE continue_scanning ; Non-zero: this entry is new best
86FF LDA zp_mem_ptr_lo ; No previous fit: store this one
8701 STA zp_mem_ptr_hi ; Store as best-fit index
8703 .continue_scanning←2← 86C6 BCC← 86FD BNE
LDX zp_mem_ptr_lo ; Restore entry index
8705 JMP scan_for_best_fit ; Continue scanning

Advance text pointer by one character

Increment the 16-bit text pointer at (&B4) by one, handling page crossing.

8708 .advance_text_ptr←3← 885A JSR← 8872 JSR← 88C1 JSR
INC zp_text_ptr_lo ; Increment pointer low byte
870A BNE return_8 ; No page crossing: return
870C INC zp_text_ptr_hi ; Increment pointer high byte
870E .return_8←1← 870A BNE
RTS ; Return

Parse argument and set up directory search

Skip leading spaces, set up directory search state, and clear the search result workspace. Falls through to check_char_is_terminator.

On ExitAfirst non-space character (Z set if terminator)
X0 if terminator, else preserved
Y0
870F .parse_and_setup_search←2← 884C JSR← 8851 JSR
JSR skip_spaces
8712 JSR set_up_directory_search ; Set up directory for search
8715 LDY #0 ; Y=0: clear search flag
8717 STY wksp_search_flag ; Store in workspace
fall through ↓

Check if character is a filename terminator

Test whether the character at (&B4),Y is a filename terminator. Bit 7 is stripped with AND #&7F before any comparison, so &8D (CR with bit 7 set) is treated identically to &0D. After stripping, any character below space (&20) is a terminator, as are '.' and '"'.

Used for parsing user-typed command-line text, not for scanning on-disc directory entry names directly.

On EntryYindex into text at (&B4)
On ExitAcharacter with bit 7 stripped (Z set if terminator)
X0 if terminator, else preserved
Ypreserved
871A .check_char_is_terminator←18← 872F JSR← 8767 JSR← 8782 JSR← 8787 JSR← 879C JSR← 8869 JSR← 88BA JSR← 88C6 JSR← 88DB JSR← 893B JSR← 8D70 JSR← 8D80 JSR← 8D9F JSR← 8DAB JSR← 8DD6 JSR← A4B9 JSR← A534 JSR← A867 JSR
LDA (zp_text_ptr_lo),y ; Get character, strip bit 7
871C AND #&7f ; Strip bit 7 of character
871E CMP #&2e ; Is it '.'?
8720 BEQ set_terminator_flag ; Yes, terminator
8722 CMP #&22 ; Is it a double-quote?
8724 BEQ set_terminator_flag ; Yes, terminator
8726 CMP #&20 ; Is it >= space?
8728 BCS return_9 ; Yes, not a terminator
872A .set_terminator_flag←2← 8720 BEQ← 8724 BEQ
LDX #0 ; X=0: signal terminator found
872C .return_9←1← 8728 BCS
RTS ; Return (not a terminator)

Check filename length, copy entry name, and compare

Scan up to 10 characters of filename at (&B4),Y. Raises Bad name error if no terminator found within 10 characters. Then copies all 10 bytes of the directory entry name at (zp_entry_ptr) to wksp_object_name, stripping bit 7 from each byte. This removes access attribute bits so the workspace copy contains pure 7-bit ASCII. Padding CRs (&0D) in unused name positions are preserved as-is; they terminate the name during compare_filename (via CMP #&21).

Falls through to compare_filename, whose return flags are passed back to the caller: Z=1 for match, and carry indicates sort order (C=0: pattern < entry name, C=1: pattern >= entry name). See compare_filename for details.

On ExitAcorrupted (Z set if match, C for sort order)
Xcorrupted
Ycorrupted
872D .check_filename_length←2← 87F6 JSR← 896F JSR
LDY #&0a ; Y=&0A: check up to 10 characters
872F .check_name_char_loop←1← 8735 BPL
JSR check_char_is_terminator ; Check next character
8732 BEQ name_length_ok ; Terminator found, ok
8734 DEY ; Decrement character count
8735 BPL check_name_char_loop ; Continue checking
8737 .bad_name_error←6← 8785 BNE← 8849 JMP← 8BD0 JMP← 8DDB JMP← A4E2 JMP← A86C JMP
JSR reload_fsm_and_dir_then_brk
873A EQUB &CC ; Error &CC: Bad name
873B EQUS "Bad name", &00
8744 .name_length_ok←1← 8732 BEQ
LDY #9 ; Y=9: copy 10 bytes of entry name
8746 .copy_entry_name_loop←1← 874E BPL
LDA (zp_entry_ptr_lo),y ; Get name byte from directory entry
8748 AND #&7f ; Strip bit 7 (access bit)
874A STA wksp_object_name,y ; Store in object name workspace
874D DEY ; Next byte in entry name
874E BPL copy_entry_name_loop ; Loop for 10 bytes
8750 INY ; Y=1: start of object name
8751 LDX #0 ; X=0: pattern index
fall through ↓

Compare filename against pattern with wildcards

Compare the object name in workspace against the pattern at (&B4),Y. Supports '#' (match one char) and '*' (match rest) wildcards. Case-insensitive comparison.

The workspace name has already had bit 7 stripped by copy_entry_name_loop, so name characters are pure 7-bit ASCII. End-of-name is detected by CMP #&21: any character below '!' (including CR padding at &0D) signals the name has ended. Called recursively for '*' wildcard backtracking.

Return flags are used by begin_dir_entry_search for the sorted-order early exit: Z=1: match found Z=0, C=0: no match, pattern < entry name (entry sorts after pattern; stop scanning sorted dir) Z=0, C=1: no match, pattern > entry name (entry sorts before pattern; continue to next entry) Wildcard patterns may return C=1 even when the pattern sorts before the entry, so wildcard searches do not benefit from the sorted early-exit optimisation.

On EntryXindex into wksp_object_name
Yindex into pattern at (&B4)
On ExitAcorrupted (Z set if match, C for sort order)
Xcorrupted
Ycorrupted
8753 .compare_filename←2← 877F BNE← 87BA JSR
CPX #&0a ; X >= 10? End of name reached
8755 BCS check_both_exhausted ; Yes, check pattern is also done
8757 LDA wksp_object_name,x ; Get object name character
875A CMP #&21 ; < '!': end of name (CR padding)
875C BCC check_both_exhausted ; Yes, name ended early
875E ORA #&20 ; Convert name char to lowercase
8760 STA wksp_csd_sector_temp ; Store for comparison
8763 CPY #&0a ; Y >= 10? Pattern exhausted
8765 BCS check_pattern_exhausted ; Yes, check if pattern terminated
8767 JSR check_char_is_terminator ; Check if pattern char is terminator
876A BEQ check_hash_wildcard ; Yes, compare lengths
876C CMP #&2a ; Pattern char is '*' wildcard?
876E BEQ begin_star_match ; Yes, match rest of name
8770 CMP #&23 ; Pattern char is '#' wildcard?
8772 BEQ advance_pattern_index ; Yes, match any single char
8774 ORA #&20 ; Convert pattern char to lowercase
8776 CMP wksp_csd_sector_temp ; Compare pattern char with name char
8779 BCC check_hash_wildcard ; C=0: pattern < name (sorted exit)
877B BNE return_10 ; C=1: pattern > name (continue)
877D .advance_pattern_index←1← 8772 BEQ
INX ; Match: advance both pointers
877E INY ; Advance Y (pattern index)
877F BNE compare_filename
8781 .return_10←3← 877B BNE← 879A BEQ← 879F BEQ
RTS ; Return
8782 .check_pattern_exhausted←1← 8765 BCS
JSR check_char_is_terminator
8785 BNE bad_name_error ; Pattern check failed: Bad name
8787 .check_hash_wildcard←3← 876A BEQ← 8779 BCC← 8793 BPL
JSR check_char_is_terminator
878A CMP #&23 ; Trailing '#' in pattern?
878C BEQ check_trailing_star ; Yes: match (name shorter than pattern)
878E CMP #&2a ; Trailing '*' in pattern?
8790 BEQ check_trailing_star ; Yes: match (wildcard eats rest)
8792 DEY ; Back up Y to try shorter match
8793 BPL check_hash_wildcard ; More positions to try
8795 CMP #&ff ; Compare &FF (force NE for no match)
8797 RTS ; Return Z clear (no match)

Check pattern and name both exhausted

After pattern ends, check whether the entry name has also ended. Returns Z set if the match succeeds.

8798 .check_both_exhausted←2← 8755 BCS← 875C BCC
CPY #&0a ; Pattern exhausted: check name too
879A BEQ return_10 ; Both at 10: exact match
879C JSR check_char_is_terminator ; Check if pattern char is terminator
879F BEQ return_10 ; Terminator: name matches
87A1 CMP #&2a ; Trailing '*': match
87A3 BEQ begin_star_match ; Is it '*'? Match rest
87A5 .check_trailing_star←2← 878C BEQ← 8790 BEQ
CMP #0 ; Compare 0 with 0 to set Z flag
87A7 RTS ; Return with Z flag result

Begin wildcard '*' matching

Skip past '*' in pattern and try matching the rest against each successive position in the entry name.

87A8 .begin_star_match←3← 876E BEQ← 87A3 BEQ← 87E3 BEQ
INY ; Skip past '*' in pattern
87A9 .try_star_position_loop←1← 87C4 BNE
LDA wksp_object_name,x ; Get object name char at X
87AC AND #&7f ; Strip bit 7
87AE CMP #&21 ; < '!': end of name (CR padding)
87B0 BCC check_name_ended ; End of name: check pattern trail
87B2 CPX #&0a ; X >= 10: end of name
87B4 BCS check_name_ended ; End of name: check pattern trail
87B6 TXA ; Save X (name position)
87B7 PHA ; Push on stack
87B8 TYA ; Save Y (pattern position)
87B9 PHA ; Push on stack
87BA JSR compare_filename ; Try matching from here (recursive)
87BD BEQ discard_saved_positions ; Z set: match succeeded
87BF PLA ; No match: restore Y
87C0 TAY ; Transfer to Y
87C1 PLA ; Restore X
87C2 TAX ; Transfer to X
87C3 INX ; Advance name position, try again
87C4 BNE try_star_position_loop ; Loop trying next position
87C6 .no_match_cleanup_loop←1← 87E5 BNE
CPX #0 ; Compare X with 0 to set Z flag
87C8 RTS ; Return with Z flag
87C9 .discard_saved_positions←1← 87BD BEQ
PLA ; Match: discard saved positions
87CA PLA ; Discard saved Y
fall through ↓

Return successful wildcard match

Set A=0 and carry to signal a successful match.

87CB .star_match_succeeded←4← 87D1 BCS← 87D7 BCC← 87DB BEQ← 87DF BEQ
LDA #0 ; A=0: set Z flag (match)
87CD SEC ; Set carry
87CE RTS ; Return Z set (match)

Check name ended during '*' match

After name is exhausted, check whether remaining pattern is only terminators. Returns Z set if match succeeds.

87CF .check_name_ended←2← 87B0 BCC← 87B4 BCS
CPY #&0a ; Name ended: check pattern trail
87D1 BCS star_match_succeeded ; Y >= 10: both exhausted, match
87D3 LDA (zp_text_ptr_lo),y ; Get pattern char
87D5 CMP #&21 ; Control char: pattern ended too
87D7 BCC star_match_succeeded ; Pattern ended: match
87D9 CMP #&2e ; Is it '.'?
87DB BEQ star_match_succeeded ; Dot: match (path separator)
87DD CMP #&22 ; Is it '"'?
87DF BEQ star_match_succeeded ; Quote: match (string end)
87E1 CMP #&2a ; Is it '*'?
87E3 BEQ begin_star_match ; Another '*': skip it and retry
87E5 BNE no_match_cleanup_loop ; Other char: no match (always)
fall through ↓

Linear scan of sorted directory for matching entry

Skip leading spaces, point (&B6) to the first directory entry, verify directory integrity, then perform a linear scan through entries comparing each against the filename pattern.

Directory entries are stored in case-insensitive ascending alphabetical order. The scan exploits this invariant: if compare_filename returns with carry clear (pattern sorts before the current entry name), the target cannot exist later in the directory and the search terminates early.

On return, (&B6) points to the matched entry (Z=1) or to the first entry that sorts after the pattern (Z=0). This position is used by the sorted-insertion code at check_name_already_exists to maintain directory order when creating new entries.

On ExitAcorrupted (Z set if match found)
Xcorrupted
Ymatch length if found
87E7 .parse_pathname_entry←1← 88FF JSR
JSR skip_spaces ; Skip leading spaces
87EA JSR print_catalogue_header ; Set (&B6) to first dir entry
87ED JSR verify_dir_integrity ; Verify directory integrity
; Linear scan through sorted directory entries. Entries are
; in ascending alphabetical order. Each 26-byte entry is
; checked in turn. The scan terminates on: match (Z=1),
; sorted early exit when pattern < entry name (C=0), or
; end of directory (first byte = 0). On exit, (&B6)
; points to the matched or insertion-point entry.
87F0 .begin_dir_entry_search←2← 8803 BCC← 8807 BNE
LDY #0 ; Y=0: start parsing pathname
87F2 LDA (zp_entry_ptr_lo),y ; Get first byte of entry
87F4 BEQ end_of_dir_entries ; Zero: end of entries
87F6 JSR check_filename_length ; Check name length and compare
87F9 BEQ return_11 ; Z=1: match found
87FB BCC return_11 ; C=0: pattern < name, stop (sorted)
87FD LDA zp_entry_ptr_lo ; C=1: pattern > name, next entry
87FF ADC #&19 ; Add &19+C(=1) = &1A (26 byte entry)
8801 STA zp_entry_ptr_lo ; Store updated pointer
8803 BCC begin_dir_entry_search ; No page crossing: continue
8805 INC zp_entry_ptr_hi ; Increment page
8807 BNE begin_dir_entry_search ; Continue searching
8809 .end_of_dir_entries←1← 87F4 BEQ
CMP #&0f ; A=0 at end: compare with &0F
880B .return_11←2← 87F9 BEQ← 87FB BCC
RTS ; Return (Z clear = not found)

Disc operation templates for FSM and directory reads

Two overlapping disc operation control block templates that share common fields. The templates are copied to workspace &1014-&101F before issuing disc read commands.

disc_op_tpl_read_fsm (&880C, 10 bytes via l8816+offset): Read 2 sectors from sector 0 into &0E00 (FSM buffer). Used to reload the free space map from disc.

disc_op_tpl_read_dir (&8817, 11 bytes): Read 5 sectors from sector 2 into &1200 (directory buffer). Used to load a directory from disc.

The templates overlap at &8817-&881B, sharing the result byte (&01), host memory marker (&FFFF), and read command (&08). The zero byte at l8816 (&8816) provides padding when copying starts from &1014 instead of &1015.

880C .disc_op_tpl_read_fsm
EQUB &01 ; Result: &01 (default)
880D EQUB &00 ; Memory address low: &00
880E EQUB &0E ; Memory address high: &0E (-> &0E00 FSM buffer)
880F EQUB &FF ; Memory address byte 3: &FF (host memory)
8810 EQUB &FF ; Memory address byte 4: &FF (host memory)
8811 EQUB &08 ; Command: &08 (read sectors)
8812 EQUB &00 ; Sector high: &00
8813 EQUB &00 ; Sector mid: &00
8814 EQUB &00 ; Sector low: &00 (sector 0)
8815 EQUB &02 ; Sector count: &02 (2 sectors for FSM)
8816 .disc_op_tpl_padding←2← A7D6 LDA← A7F7 LDA
EQUB &00 ; Padding: &00 (for 12-byte copy from &1014)
8817 .disc_op_tpl_read_dir←4← 89B1 LDA← 89F4 LDA← 8F8E LDA← 94AA LDA
EQUB &01 ; Result: &01 (default)
8818 EQUB &00 ; Memory address low: &00
8819 EQUB &12 ; Memory address high: &12 (-> &1200 dir buffer)
881A EQUB &FF ; Memory address byte 3: &FF (host memory)
881B EQUB &FF ; Memory address byte 4: &FF (host memory)
881C EQUB &08 ; Command: &08 (read sectors)
881D EQUB &00 ; Sector high: &00
881E EQUB &00 ; Sector mid: &00
881F EQUB &02 ; Sector low: &02 (sector 2 = root dir)
8820 EQUB &05 ; Sector count: &05 (5 sectors per directory)
8821 EQUB &00 ; Control: &00

Parse drive number from ASCII character

Convert ASCII drive character ('0'-'7' or 'A'-'H') to a 3-bit drive ID in bits 5-7 of A. Limits to drives 0-3 if no hard drive present.

On EntryAASCII drive character ('0'-'7' or 'A'-'H')
On ExitAdrive ID (bits 5-7)
Xpreserved
Ypreserved
8822 .parse_drive_from_ascii←2← 886C JSR← A10A JSR
CMP #&30 ; Character >= '0'?
8824 BCC bad_drive_name ; Below '0': bad name
8826 CMP #&38 ; Character >= '8' (not digit)?
8828 BCC push_valid_drive ; Digit 0-7: valid drive
882A ORA #&20 ; Convert to lowercase
882C CMP #&61 ; Character >= 'a'?
882E BCC bad_drive_name ; Below 'a': bad name
8830 CMP #&69 ; Character >= 'i'?
8832 BCS bad_drive_name ; Above 'h': bad name
8834 SBC #0 ; Subtract to get drive number
8836 .push_valid_drive←1← 8828 BCC
PHA ; Save drive digit on stack
8837 LDA zp_adfs_flags ; Check for hard drive
8839 AND #&20 ; Bit 5: hard drive present?
883B BNE restore_drive_digit ; HD present: allow drives 0-7
883D PLA ; No HD: restore drive digit
883E AND #3 ; Mask to drives 0-3 only (floppy)
8840 PHA ; Re-push limited drive number
8841 .restore_drive_digit←1← 883B BNE
PLA ; Restore drive number
8842 AND #7 ; Mask to 3 bits (drives 0-7)
8844 LSR ; Shift into drive ID position
8845 ROR ; Rotate right
8846 ROR ; Rotate right
8847 ROR ; Rotate right (bits 5-7)
8848 RTS ; Return drive ID in A

Raise Bad name error for invalid drive

Jump to bad name error handler for an invalid drive specifier character.

8849 .bad_drive_name←4← 8824 BCC← 882E BCC← 8832 BCS← 884F BEQ
JMP bad_name_error ; Invalid: Bad name error

Parse filename from command line

Parse a filename from (&B4) including drive specifier, root, and parent directory references.

On ExitAcorrupted (Z set if found, (&B6) points to entry)
Xcorrupted
Ycorrupted
884C .parse_filename_from_cmdline←2← 8BB3 JSR← 8FDF JSR
JSR parse_and_setup_search ; Get filename from (&B4)
884F BEQ bad_drive_name ; Empty filename: bad name
8851 .full_pathname_parser←1← 947F JSR
JSR parse_and_setup_search ; Get first path character
8854 BEQ check_drive_initialised ; Empty: use current directory
8856 CMP #&3a ; Is it ':' (drive specifier)?
8858 BNE check_root_specifier ; No colon: check for $ or path
885A JSR advance_text_ptr ; Advance past ':'
885D LDX wksp_saved_drive ; Check if drive already saved
8860 INX ; Saved drive = &FF (not set)?
8861 BNE get_first_path_char ; Already set: keep it
8863 LDA wksp_current_drive ; Save current drive for restore
8866 STA wksp_saved_drive ; Store as saved drive
8869 .get_first_path_char←1← 8861 BNE
JSR check_char_is_terminator ; Get drive character
886C JSR parse_drive_from_ascii ; Parse drive number from ASCII
886F STA wksp_current_drive ; Store as new current drive
8872 .advance_past_colon←1← 88CD BEQ
JSR advance_text_ptr ; Advance past drive number
8875 .check_drive_initialised←1← 8854 BEQ
LDX wksp_current_drive ; Check drive is initialised
8878 INX ; Drive = &FF (uninitialised)?
8879 BNE set_fsm_loading_flag ; Not &FF: drive is valid
887B STX wksp_current_drive ; Set to drive 0 as default
887E .set_fsm_loading_flag←1← 8879 BNE
LDA zp_adfs_flags ; Set FSM-inconsistent flag (bit 4)
8880 ORA #&10 ; Bit 4: FSM being loaded
8882 STA zp_adfs_flags ; Store updated flags
8884 LDX #&0c ; Load FSM from disc (sectors 0-1)
8886 LDY #&88 ; Y=&88: FSM read control block page
8888 JSR exec_disc_command ; Read FSM from disc
888B LDA zp_adfs_flags ; Clear FSM-inconsistent flag
888D AND #&ef ; Mask off bit 4
888F STA zp_adfs_flags ; Store cleared flags
8891 LDA wksp_alt_sector_hi ; Check if alt workspace is set
8894 BPL load_root_directory ; Set: skip CSD copy
8896 LDY #2 ; Y=2: copy CSD sector to workspace
8898 .copy_csd_to_root_loop←1← 889F BPL
LDA wksp_csd_sector_lo,y ; Get CSD sector byte
889B STA wksp_csd_drive_sector,y ; Copy to CSD drive sector
889E DEY ; Next byte
889F BPL copy_csd_to_root_loop ; Loop for 3 bytes
88A1 .load_root_directory←1← 8894 BPL
LDY #&88 ; Load root directory (sector 2)
88A3 LDX #&17 ; X=&17: directory read block offset
88A5 JSR exec_disc_command ; Read directory from disc
88A8 LDA #2 ; Set root sector = 2
88AA STA wksp_csd_sector_lo ; Store root sector low
88AD LDA #0 ; A=0: clear mid and high bytes
88AF STA wksp_csd_sector_mid ; Clear sector mid byte
88B2 STA wksp_csd_sector_hi ; Clear sector high byte
88B5 JSR check_channels_on_drive ; Validate FSM checksums
88B8 LDY #0 ; Y=0: check next path character
88BA JSR check_char_is_terminator ; Get next character
88BD CMP #&2e ; Is it '.' (path separator)?
88BF BNE set_root_dir_entry ; No dot: this is the final component
88C1 JSR advance_text_ptr ; Skip past dot separator
88C4 .check_root_specifier←1← 8858 BNE
LDY #0 ; Y=0: check for $ or path component
88C6 JSR check_char_is_terminator ; Get character
88C9 AND #&fd ; Mask to check for $ (ignore bit 1)
88CB CMP #&24 ; Is it '$' (root directory)?
88CD BEQ advance_past_colon ; Yes: advance and load root
88CF JSR check_drive_and_reload_fsm ; Not root: load current directory
88D2 .check_special_dir_in_path←1← 89CD JMP
JSR check_special_dir_char ; Check for ^ or @ specifiers
88D5 BNE search_current_dir ; Not special: regular path component
88D7 INY ; Advance past ^ or @ character
88D8 STY wksp_copy_read_sector ; Store length marker
88DB JSR check_char_is_terminator ; Get next character
88DE CMP #&2e ; Is it '.' (more path follows)?
88E0 BNE return_12 ; No: this is the final component
88E2 JMP advance_text_past_component ; Jump to subdirectory descent
88E5 .set_root_dir_entry←1← 88BF BNE
LDA #&24 ; No dot after root: set up $ entry
88E7 STA wksp_object_name ; Store '$' as object name
88EA LDA #&0d ; CR padding
88EC STA wksp_object_name_1 ; Store CR after name
88EF LDA #&cc ; Point to dummy dir entry at &94CC
88F1 STA zp_entry_ptr_lo ; Store pointer low
88F3 LDA #&94 ; Pointer high = &94
88F5 STA zp_entry_ptr_hi ; Store pointer high
88F7 LDA #2 ; A=2: root sector number
88F9 STA wksp_search_flag ; Store as found sector
88FC LDA #0 ; A=0: success (Z set)
88FE RTS ; Return
88FF .search_current_dir←1← 88D5 BNE
JSR parse_pathname_entry ; Regular path: search current dir
8902 BEQ check_if_dir_entry ; Found? Proceed to check dir/file
8904 .return_12←1← 88E0 BNE
RTS ; Return (not found)

Save text pointer and determine object type

After a directory entry match, save the remaining text position and determine whether the entry is a file (type 1) or directory (type 2).

8905 .save_text_ptr_after_match←2← 8940 BCC← 8944 BEQ
LDA zp_text_ptr_lo ; Save current text pointer
8907 PHA ; Push low byte
8908 LDA zp_text_ptr_hi ; Get high byte
890A PHA ; Push high byte
890B TYA ; Transfer Y to A (matched length)
890C CLC ; Clear carry for addition
890D ADC zp_text_ptr_lo ; Add matched length to text pointer
890F STA zp_text_ptr_lo ; Store updated text pointer low
8911 LDA #0 ; A=0 for carry propagation
8913 ADC zp_text_ptr_hi ; Add carry to high byte
8915 STA zp_text_ptr_hi ; Store updated text pointer high
8917 JSR skip_spaces ; Skip spaces after path component
891A LDA zp_text_ptr_lo ; Save remaining text pointer
891C STA wksp_cmd_tail ; Store for later use
891F LDA zp_text_ptr_hi ; Get high byte
8921 STA wksp_cmd_tail_hi ; Store high byte
8924 PLA ; Restore original text pointer
8925 STA zp_text_ptr_hi ; Store high byte
8927 PLA ; Restore low byte
8928 STA zp_text_ptr_lo ; Store low byte
892A LDX #1 ; X=1: object type (file)
892C LDY #3 ; Y=3: check access byte
892E LDA (zp_entry_ptr_lo),y ; Get access/attribute byte
8930 BPL scan_for_component_end ; Bit 7 clear: not a directory
8932 INX ; Bit 7 set: X=2 (directory)
8933 .scan_for_component_end←1← 8930 BPL
STX wksp_search_flag ; Store object type
8936 LDA #0 ; A=0: success (Z set)
8938 RTS ; Return
8939 .check_if_dir_entry←1← 8902 BEQ
LDY #0 ; Y=0: scan for end of component
893B .scan_component_chars_loop←1← 894B BNE
JSR check_char_is_terminator ; Check next character
893E CMP #&21 ; Control char? End of component
8940 BCC save_text_ptr_after_match ; Yes: set up result
8942 CMP #&22 ; Double-quote? End of component
8944 BEQ save_text_ptr_after_match ; Yes: set up result
8946 CMP #&2e ; Dot? Path separator
8948 BEQ save_component_length ; Yes: descend into subdirectory
894A INY ; Next character
894B BNE scan_component_chars_loop ; Loop scanning
894D .save_component_length←1← 8948 BEQ
STY wksp_copy_read_sector ; Save component length
8950 .check_access_is_dir_loop←1← 8959 BEQ
LDY #3 ; Y=3: check if entry is directory
8952 LDA (zp_entry_ptr_lo),y ; Get access byte
8954 BMI descend_into_subdir ; Bit 7: is a directory
8956 JSR advance_dir_entry_ptr ; Not dir: advance to next entry
8959 BEQ check_access_is_dir_loop ; Found next entry: retry match
895B .next_entry_not_found←1← 896D BEQ
LDA #&ff ; A=&FF: return not found
895D RTS ; Return

Advance to next matching directory entry

Advance (&B6) by 26 bytes to the next directory entry, then check whether it matches the current search pattern.

On ExitAcorrupted (Z set if match, (&B6) points to entry)
Xcorrupted
Ycorrupted
895E .advance_dir_entry_ptr←8← 8956 JSR← 8972 BNE← 8BBA JSR← 948A JSR← 94F2 JSR← 99BE JSR← 9C4E JSR← A88C JSR
CLC ; Clear carry for addition
895F LDA zp_entry_ptr_lo ; Get entry pointer low
8961 ADC #&1a ; Add &1A (26 bytes per entry)
8963 STA zp_entry_ptr_lo ; Store updated pointer
8965 BCC compare_next_dir_entry ; No page crossing
8967 INC zp_entry_ptr_hi ; Increment page on overflow
8969 .compare_next_dir_entry←1← 8965 BCC
LDY #0 ; Y=0: check first byte
896B LDA (zp_entry_ptr_lo),y ; Get first byte of next entry
896D BEQ next_entry_not_found ; Zero: end of entries (not found)
896F JSR check_filename_length ; Compare against pattern
8972 BNE advance_dir_entry_ptr ; No match: try next entry
8974 RTS ; Match found: return
8975 .descend_into_subdir←1← 8954 BMI
LDY #9 ; Y=9: check last name byte
8977 LDA (zp_entry_ptr_lo),y ; Get name byte 9
8979 BPL advance_text_past_component ; Bit 7 clear: normal descent
897B AND #&7f ; Bit 7 set: clear it (bad rename?)
897D STA (zp_entry_ptr_lo),y ; Store cleaned name byte
897F JSR write_dir_and_validate ; Write directory back to disc
8982 .clean_dir_rename_bit←1← A500 JMP
JSR reload_fsm_and_dir_then_brk
8985 EQUB &B0 ; Error &B0: Bad rename
8986 EQUS "Bad rename", &00
8991 .advance_text_past_component←2← 88E2 JMP← 8979 BPL
LDA wksp_copy_read_sector ; Get matched component length
8994 SEC ; Set carry (add 1 for separator)
8995 ADC zp_text_ptr_lo ; Add to text pointer
8997 STA zp_text_ptr_lo ; Store updated pointer low
8999 BCC check_alt_workspace_set ; No page crossing
899B INC zp_text_ptr_hi ; Increment page
899D .check_alt_workspace_set←1← 8999 BCC
LDA wksp_alt_sector_hi ; Check if alt workspace is set
89A0 CMP #&ff ; &FF: not set
89A2 BNE copy_disc_op_for_subdir ; Set: skip CSD copy
89A4 LDY #2 ; Y=2: copy CSD sector
89A6 .copy_csd_sector_after_descent←1← 89AD BPL
LDA wksp_csd_sector_lo,y ; Get CSD sector byte
89A9 STA wksp_csd_drive_sector,y ; Copy to CSD drive sector
89AC DEY ; Next byte
89AD BPL copy_csd_sector_after_descent ; Loop for 3 bytes
89AF .copy_disc_op_for_subdir←1← 89A2 BNE
LDX #&0a ; X=&0A: copy 11-byte disc op template
89B1 .copy_subdir_template_loop←1← 89B8 BPL
LDA disc_op_tpl_read_dir,x ; Get template byte from ROM
89B4 STA wksp_disc_op_result,x ; Store in disc op workspace
89B7 DEX ; Next byte
89B8 BPL copy_subdir_template_loop ; Loop for 11 bytes
89BA LDX #2 ; X=2: copy 3 sector address bytes
89BC LDY #&16 ; Y=&16: start sector in entry
89BE .copy_subdir_sector_loop←1← 89C8 BPL
LDA (zp_entry_ptr_lo),y ; Get sector byte from entry
89C0 STA wksp_disc_op_sector,x ; Store in disc op sector field
89C3 STA wksp_alt_csd_sector,y ; Also store in CSD info
89C6 INY ; Next entry byte
89C7 DEX ; Next sector byte
89C8 BPL copy_subdir_sector_loop ; Loop for 3 bytes
89CA JSR exec_disc_op_from_wksp ; Execute disc read command
89CD JMP check_special_dir_in_path ; Continue parsing path

Load object type and save workspace

Load object type from workspace and fall through to save_wksp_and_return.

On ExitAobject type (0=not found, 1=file, 2=directory)
Xcorrupted
Ycorrupted
89D0 .get_object_type_result←4← 8C5F JMP← 8CC6 JMP← 923B JMP← B360 JSR
LDA wksp_search_flag ; Get object type result
fall through ↓

Save workspace state and return result

Restore the original drive if changed, reload the FSM, restore alternative workspace if set, and save workspace with checksum. Return value passed via A on stack.

On EntryAresult value to preserve across save
On ExitAresult value from entry (preserved)
Xcorrupted
Ycorrupted
89D3 .save_wksp_and_return←34← 82D5 JSR← 8348 JSR← 8CC0 JMP← 9125 JMP← 914B JSR← 941C JMP← 94F7 JMP← 956D JMP← 9938 JMP← 99C6 JMP← 9A40 JMP← 9C1D JSR← 9C74 JSR← 9FF3 JMP← A3A6 JSR← A45D JMP← A488 JMP← A494 JMP← A4A8 JSR← A51B JSR← A579 JSR← A5E5 JMP← A63E JSR← A669 JSR← A682 JMP← A856 JSR← A891 JMP← A94E JSR← ADF2 JSR← B07A JSR← B2D9 JSR← B357 JSR← B3E7 JSR← B710 JSR
PHA ; Save on stack
89D4 LDA wksp_saved_drive ; Check if drive was changed
89D7 CMP #&ff ; Saved drive = &FF (not changed)?
89D9 BEQ check_alt_wksp_on_return ; Not changed: skip drive restore
89DB STA wksp_current_drive ; Restore original drive
89DE LDA #&ff ; A=&FF: clear saved drive marker
89E0 STA wksp_saved_drive ; Mark saved drive as unused
89E3 LDX #&0c ; X=&0C: FSM control block offset
89E5 LDY #&88 ; Y=&88: FSM control block page
89E7 JSR exec_disc_command ; Reload FSM for original drive
89EA .check_alt_wksp_on_return←1← 89D9 BEQ
LDA wksp_alt_sector_hi ; Check alt workspace pointer
89ED CMP #&ff ; &FF: not set
89EF BEQ save_workspace_checksum ; Not set: skip workspace restore
89F1 TAX ; Transfer to X
89F2 LDY #&0a ; Y=&0A: copy 11-byte template
89F4 .copy_alt_wksp_template_loop←1← 89FB BPL
LDA disc_op_tpl_read_dir,y ; Get template byte
89F7 STA wksp_disc_op_result,y ; Store in workspace
89FA DEY ; Next byte
89FB BPL copy_alt_wksp_template_loop ; Loop for 11 bytes
89FD STX wksp_csd_sector_hi ; Store alt sector high
8A00 STX wksp_disc_op_sector ; Store in disc op sector
8A03 LDA wksp_csd_drive_sector_mid ; Get alt sector mid
8A06 STA wksp_csd_sector_mid ; Store in CSD mid
8A09 STA wksp_disc_op_sector_mid ; Store in disc op mid
8A0C LDA wksp_csd_drive_sector ; Get CSD sector low
8A0F STA wksp_csd_sector_lo ; Store in CSD low
8A12 STA wksp_disc_op_sector_lo ; Store in disc op low
8A15 LDA #&ff ; A=&FF: clear alt workspace
8A17 STA wksp_alt_sector_hi ; Mark as unused
8A1A JSR exec_disc_op_from_wksp ; Read directory from disc
8A1D .save_workspace_checksum←1← 89EF BEQ
LDA zp_adfs_flags ; Save flags to workspace
8A1F STA wksp_flags_save ; Store in flags save area
8A22 JSR get_wksp_addr_ba
8A25 LDY #&fb ; Y=&FB: save 252 bytes of workspace
8A27 .save_wksp_page_loop←1← 8A2D BNE
LDA wksp_csd_name,y ; Get workspace byte
8A2A STA (zp_wksp_ptr_lo),y ; Store in saved workspace
8A2C DEY ; Next byte
8A2D BNE save_wksp_page_loop ; Loop until Y=0
8A2F LDA wksp_csd_name ; Get byte at Y=0 too
8A32 STA (zp_wksp_ptr_lo),y ; Store in saved workspace
8A34 JSR store_wksp_checksum_ba_y ; Update workspace checksum
8A37 LDX zp_osfile_ptr_lo ; Restore X from (&B8)
8A39 LDY zp_osfile_ptr_hi ; Restore Y from (&B9)
8A3B PLA ; Restore object type from stack
8A3C .return_13←1← 8A40 BEQ
RTS ; Return

Execute multi-sector disc command

Set up sector count and execute a disc read or write command. Rounds up partial counts for writes. Generates BRK on error.

On ExitA0 on success (Z set)
Xcorrupted
Ycorrupted
8A3D .multi_sector_disc_command←4← 8C59 JSR← 8F77 JSR← 962C JSR← B7D2 JSR
JSR check_disc_command_type ; Set up sector count and execute
8A40 BEQ return_13 ; Success: return Z set
8A42 JMP generate_error

Check command type and adjust sector count

For write commands with partial transfers, round up the sector count. For reads, skip the adjustment.

8A45 .check_disc_command_type←2← 8A3D JSR← 9D5C JSR
LDA wksp_disc_op_command ; Get disc op command
8A48 CMP #8 ; Command 8 (read)?
8A4A BEQ exec_disc_transfer_batched ; Yes: check sector count
8A4C LDA wksp_disc_op_transfer_len ; Get partial transfer count
8A4F BEQ exec_disc_transfer_batched ; Zero: no partial, skip adjust
8A51 LDA #0 ; Clear partial transfer count
8A53 STA wksp_disc_op_transfer_len ; Store zero
8A56 INC wksp_disc_op_xfer_len_1 ; Increment full sector count
8A59 BNE exec_disc_transfer_batched ; No wrap
8A5B INC wksp_disc_op_xfer_len_2 ; Wrap: increment mid byte
8A5E BNE exec_disc_transfer_batched ; No wrap
8A60 INC wksp_disc_op_xfer_len_3 ; Wrap: increment high byte
fall through ↓

Execute disc transfer in batches

For transfers exceeding 255 sectors, loop with full batches. For the final batch, use the remaining count.

8A63 .exec_disc_transfer_batched←4← 8A4A BEQ← 8A4F BEQ← 8A59 BNE← 8A5E BNE
LDX #&15 ; X=&15: disc op block offset
8A65 LDY #&10 ; Y=&10: disc op block page
8A67 LDA #&ff ; Set sector count to &FF (max)
8A69 STA wksp_disc_op_sector_count ; Store max sector count
8A6C .single_sector_read←2← 8AA8 BCS← 8AB5 BCC
LDA wksp_disc_op_xfer_len_3 ; Check if total > 255 sectors
8A6F ORA wksp_disc_op_xfer_len_2 ; OR with mid byte
8A72 BEQ partial_sector_complete ; Both zero: <= 255, use exact count
8A74 JSR command_exec_xy
8A77 BNE return_14 ; Non-zero: use max (&FF), loop
8A79 LDA #&ff ; Advance transfer address by &FF pages
8A7B CLC ; Clear carry for addition
8A7C ADC wksp_disc_op_mem_addr_1 ; Add &FF to transfer addr mid
8A7F STA wksp_disc_op_mem_addr_1 ; Store updated mid
8A82 BCC calc_partial_start_sector ; No carry
8A84 INC wksp_disc_op_mem_addr_2 ; Carry: increment high
8A87 BNE calc_partial_start_sector ; No wrap
8A89 INC wksp_disc_op_mem_addr_3 ; Wrap: increment highest
8A8C .calc_partial_start_sector←2← 8A82 BCC← 8A87 BNE
LDA #&ff ; Advance disc sector by &FF
8A8E CLC ; Clear carry
8A8F ADC wksp_disc_op_sector_lo ; Add &FF to sector low
8A92 STA wksp_disc_op_sector_lo ; Store updated sector low
8A95 BCC calc_partial_end_sector ; No carry
8A97 INC wksp_disc_op_sector_mid ; Carry: increment sector mid
8A9A BNE calc_partial_end_sector ; No wrap
8A9C INC wksp_disc_op_sector ; Wrap: increment sector high
8A9F .calc_partial_end_sector←2← 8A95 BCC← 8A9A BNE
LDA wksp_disc_op_xfer_len_1 ; Subtract &FF from remaining count
8AA2 SEC ; Set carry for subtraction
8AA3 SBC #&ff ; Subtract &FF
8AA5 STA wksp_disc_op_xfer_len_1 ; Store updated count low
8AA8 BCS single_sector_read ; No borrow: loop for more chunks
8AAA LDA wksp_disc_op_xfer_len_2 ; Borrow: decrement mid byte
8AAD BNE exec_partial_sector_op ; Non-zero: adjust high too
8AAF DEC wksp_disc_op_xfer_len_3 ; Decrement high byte
8AB2 .exec_partial_sector_op←1← 8AAD BNE
DEC wksp_disc_op_xfer_len_2 ; Decrement mid byte
8AB5 BCC single_sector_read ; Continue chunked read
8AB7 .partial_sector_complete←1← 8A72 BEQ
LDA wksp_disc_op_xfer_len_1 ; Get remaining sector count
8ABA BEQ read_256_via_hd ; Zero: check for partial sector
8ABC STA wksp_disc_op_sector_count ; Non-zero: use as final chunk size
8ABF JSR command_exec_xy
8AC2 BNE return_14 ; Non-zero: execute this chunk
8AC4 .read_256_via_hd←1← 8ABA BEQ
LDA wksp_disc_op_transfer_len ; Get partial transfer count
8AC7 BNE load_sector_check_result ; Non-zero: execute partial sector
8AC9 .return_14←3← 8A77 BNE← 8AC2 BNE← 8AFE BEQ
RTS ; All done: return
8ACA .load_sector_check_result←1← 8AC7 BNE
STA wksp_disc_op_sector_count ; Store partial count as sector count
8ACD LDA wksp_disc_op_xfer_len_1 ; Advance transfer address by partial
8AD0 CLC ; Clear carry
8AD1 ADC wksp_disc_op_sector_lo ; Add partial count to sector
8AD4 STA wksp_disc_op_sector_lo ; Store updated sector
8AD7 BCC calc_multi_sector_count ; No carry
8AD9 INC wksp_disc_op_sector_mid ; Carry: increment mid
8ADC BNE calc_multi_sector_count ; No wrap
8ADE INC wksp_disc_op_sector ; Wrap: increment high
8AE1 .calc_multi_sector_count←2← 8AD7 BCC← 8ADC BNE
LDA wksp_disc_op_xfer_len_1 ; Advance transfer address
8AE4 CLC ; Clear carry
8AE5 ADC wksp_disc_op_mem_addr_1 ; Add to transfer addr
8AE8 STA wksp_disc_op_mem_addr_1 ; Store updated addr
8AEB BCC copy_sector_to_transfer ; No carry
8AED INC wksp_disc_op_mem_addr_2 ; Carry: increment high
8AF0 BNE copy_sector_to_transfer ; No wrap
8AF2 INC wksp_disc_op_mem_addr_3 ; Wrap: increment highest
8AF5 .copy_sector_to_transfer←2← 8AEB BCC← 8AF0 BNE
JSR wait_ensuring
8AF8 JSR command_set_retries
8AFB .copy_sector_count_loop←1← 8B02 BPL
JSR set_transfer_length ; Execute disc read with retry
8AFE BEQ return_14 ; Success: return
8B00 DEC zp_retry_count ; Decrement retry counter
8B02 BPL copy_sector_count_loop ; More retries: try again
8B04 .set_transfer_length←1← 8AFB JSR
LDX #&15 ; X=&15: disc op block offset
8B06 LDY #&10 ; Y=&10: disc op block page
8B08 STX zp_ctrl_blk_lo ; Store in (&B0)
8B0A STY zp_ctrl_blk_hi ; Store page in (&B1)
8B0C LDA wksp_current_drive ; Get current drive
8B0F ORA wksp_disc_op_sector ; OR into sector high byte
8B12 STA wksp_disc_op_sector ; Store updated sector+drive
8B15 STA wksp_current_drive_hi ; Store as current drive info
8B18 LDA zp_adfs_flags ; Get ADFS flags
8B1A AND #&20 ; Check bit 5: hard drive present?
8B1C BNE hd_command_partial_sector
fall through ↓

Floppy disc partial sector transfer

Transfer a partial sector (less than 256 bytes) to or from a floppy disc. Used for operations that don't align to sector boundaries.

8B1E .floppy_partial_sector←1← 8B44 BMI
LDA wksp_disc_op_sector ; Get sector address high byte
8B21 ORA wksp_current_drive ; Combine with current drive
8B24 STA wksp_err_sector_hi ; Store in error sector workspace
8B27 LDA wksp_disc_op_sector_mid ; Get sector address mid byte
8B2A STA wksp_err_sector_mid ; Store in error sector workspace
8B2D LDA wksp_disc_op_sector_lo ; Get sector address low byte
8B30 STA wksp_err_sector ; Store in error sector workspace
8B33 JSR calc_buffer_page_from_offset ; Calculate buffer offset
8B36 STA wksp_buf_flag,x ; Store partial transfer count
8B39 TXA ; Channel offset to A for buffer calc
8B3A LSR ; Divide by 4 for buffer page index
8B3B LSR ; (continued)
8B3C ADC #&17 ; Add buffer base page (&17)
8B3E JMP exec_floppy_partial_sector_buf_ind ; Execute floppy partial sector op

Hard drive partial sector transfer

Transfer a partial sector via the SCSI hard drive interface.

8B41 .hd_command_partial_sector←1← 8B1C BNE
LDA wksp_current_drive_hi ; Check zp_flags for hard drive
8B44 BMI floppy_partial_sector ; Bit 5 clear: floppy, use floppy path
8B46 JSR scsi_start_command ; Select SCSI target
8B49 LDA wksp_disc_op_mem_addr ; Get transfer address low from blk
8B4C STA zp_mem_ptr_lo ; Store in (&B2)
8B4E LDA wksp_disc_op_mem_addr_1 ; Get transfer address mid
8B51 STA zp_mem_ptr_hi ; Store in (&B3)
8B53 LDA wksp_disc_op_mem_addr_2 ; Get transfer address high
8B56 CMP #&fe ; Address >= &FE00?
8B58 BCC check_partial_sector_needed ; Below: might need Tube claim
8B5A LDA wksp_disc_op_mem_addr_3 ; Get next address byte
8B5D CMP #&ff ; Is it &FF (host memory)?
8B5F BEQ setup_partial_sector_buffer ; Yes: skip Tube claim
8B61 .check_partial_sector_needed←1← 8B58 BCC
JSR claim_tube
8B64 .setup_partial_sector_buffer←1← 8B5F BEQ
LDA wksp_disc_op_sector_count ; Get partial transfer byte count
8B67 TAX ; Save count in X
8B68 LDA #1 ; Set sector count to 1
8B6A STA wksp_disc_op_sector_count ; Only read one sector
8B6D LDA #8 ; SCSI read command = 8
8B6F STA wksp_disc_op_command ; Store command byte
8B72 LDY #0 ; Y=0: start of 6-byte command
8B74 .copy_partial_sector_loop←1← 8B7D BNE
LDA wksp_disc_op_command,y ; Get SCSI command byte
8B77 JSR scsi_send_byte_a
8B7A INY ; Next command byte
8B7B CPY #6 ; Sent all 6 bytes?
8B7D BNE copy_partial_sector_loop ; No, send next
8B7F BIT zp_adfs_flags ; Tube in use?
8B81 BVC complete_partial_write ; No Tube: skip Tube transfer setup
8B83 TXA ; Save byte count from X to A
8B84 PHA ; Push byte count on stack
8B85 LDX #&27 ; X=&27: Tube workspace offset
8B87 LDY #&10 ; Y=&10: Tube workspace page
8B89 LDA #1 ; A=1: Tube read transfer type
8B8B JSR tube_entry ; Start Tube transfer
8B8E PLA ; Restore byte count
8B8F TAX ; Back to X
8B90 .complete_partial_write←1← 8B81 BVC
LDY #0 ; Y=0: data transfer byte index
8B92 JSR scsi_wait_for_req
8B95 BMI execute_partial_disc_op ; Status phase: transfer complete
8B97 .copy_write_data_loop←1← 8BAE BNE
LDA fred_hard_drive_0 ; Read byte from SCSI data bus
8B9A CPX #0 ; Byte count exhausted?
8B9C BEQ partial_read_from_disc ; Yes: discard remaining bytes
8B9E BIT zp_adfs_flags ; Tube in use?
8BA0 BVC check_write_or_read ; No Tube: store in memory
8BA2 JSR tube_delay2 ; Tube timing delay
8BA5 STA tube_data_register_3 ; Write byte to Tube R3
8BA8 BVS partial_write_to_disc ; Always branch (V always set here)
8BAA .check_write_or_read←1← 8BA0 BVC
STA (zp_mem_ptr_lo),y ; Store byte in memory buffer
8BAC .partial_write_to_disc←1← 8BA8 BVS
DEX ; Decrement remaining byte count
8BAD .partial_read_from_disc←1← 8B9C BEQ
INY ; Next transfer position
8BAE BNE copy_write_data_loop ; Loop for 256 bytes (full sector)
8BB0 .execute_partial_disc_op←1← 8B95 BMI
JMP command_done ; Status phase: get SCSI result

Search for non-directory file

Parse a filename and search the current directory for a matching non-directory entry.

On ExitAcorrupted (Z set if found, (&B6) points to entry)
Xcorrupted
Ycorrupted
8BB3 .search_for_file←5← 8C05 JSR← A3A1 JSR← A3B4 JSR← A40E JSR← A82F JSR
JSR parse_filename_from_cmdline ; Search for file in directory
8BB6 BEQ complete_partial_op ; Found? Check if it's a directory
8BB8 BNE return_15 ; Not found: return Z clear
8BBA .copy_partial_read_loop←1← 8BC3 BMI
JSR advance_dir_entry_ptr ; Skip directory entries
8BBD BNE return_15 ; Not found after dirs: return Z clear
8BBF .complete_partial_op←1← 8BB6 BEQ
LDY #3 ; Y=3: check access byte
8BC1 LDA (zp_entry_ptr_lo),y ; Get access byte from entry
8BC3 BMI copy_partial_read_loop ; Bit 7: is a directory, skip it
8BC5 .check_partial_sectors_done←1← 8BEE BPL
LDA #0 ; A=0: return Z set (found)
8BC7 .return_15←3← 8BB8 BNE← 8BBD BNE← 8BE8 BNE
RTS ; Return

Generate Not found error

Check for special directory characters in path and generate either Bad name or Not found error.

8BC8 .not_found_error←6← 8C08 BNE← 8CF6 JMP← 94EC JMP← 9942 JMP← A514 JMP← A834 JMP
LDY #0 ; Y=0: get first path char
8BCA LDA (zp_text_ptr_lo),y ; Get first character
8BCC CMP #&5e ; Is it '^' (parent)?
8BCE BNE file_is_locked_error ; No, check '@'
8BD0 .check_locked_loop←1← 8BD5 BEQ
JMP bad_name_error ; Bad name error (^ or @ in context)
8BD3 .file_is_locked_error←1← 8BCE BNE
CMP #&40 ; Is it '@' (current dir)?
8BD5 BEQ check_locked_loop ; Yes: Bad name error
8BD7 .bad_parms_error←3← 8297 JMP← 948F JMP← A0C0 JMP
JSR reload_fsm_and_dir_then_brk
8BDA EQUB &D6 ; Error &D6: Not found
8BDB EQUS "Not found", &00
8BE5 .find_file_and_validate←3← 907F JSR← 9101 JSR← A50F JSR
JSR find_first_matching_entry ; Search for file
8BE8 BNE return_15 ; Not found: return
8BEA LDY #4 ; Y=4: check E attribute byte
8BEC LDA (zp_entry_ptr_lo),y ; Get access/E byte
8BEE BPL check_partial_sectors_done ; Bit 7 clear: not E, return found
8BF0 .validate_found_entry←4← 8C0E BPL← A41B JMP← B256 JMP← B2F8 JMP
JSR reload_fsm_and_dir_then_brk
8BF3 EQUB &BD ; Error &BD: Access violation
8BF4 EQUS "Access violation", &00
fall through ↓

OSFILE A=0: check for existing file before save

Entry point for OSFILE save (A=0), reached via RTS-trick dispatch from osfile_handler. Searches the current directory for an existing file with the same name, checking it is not a directory and has the correct access attributes.

On entry: (&B4) points to filename, (&B8) to OSFILE control block On exit: Falls through to osfile_save_handler if file is valid

8C05 .osfile_save_check_existing
JSR search_for_file ; Search for matching non-directory file
8C08 BNE not_found_error ; Not found: report Not found error
8C0A LDY #0 ; Y=0: check first entry name byte
8C0C LDA (zp_entry_ptr_lo),y ; Get first byte of found entry
8C0E BPL validate_found_entry ; Bit 7 clear: no read access, error
8C10 .create_new_dir_entry←1← A41E JSR
LDY #6 ; Y=6: check control block byte 6
8C12 LDA (zp_osfile_ptr_lo),y ; Get byte 6
8C14 BNE allocate_space_for_file ; Non-zero: use entry's load address
8C16 DEY ; Y=5: copy bytes 2-5 from block
8C17 .clear_osfile_block_loop←1← 8C1F BNE
LDA (zp_osfile_ptr_lo),y ; Get control block byte
8C19 STA wksp_disc_op_block,y ; Store in disc op workspace
8C1C DEY ; Next byte
8C1D CPY #1 ; Past byte 1?
8C1F BNE clear_osfile_block_loop ; No, continue copying
8C21 BEQ write_dir_entry ; Done: skip to sector setup
8C23 .allocate_space_for_file←1← 8C14 BNE
LDX #4 ; X=4: copy 4 bytes from dir entry
8C25 LDY #&0d ; Y=&0D: entry offset for load addr
8C27 .copy_alloc_sector_loop←1← 8C2E BNE
LDA (zp_entry_ptr_lo),y ; Get byte from directory entry
8C29 STA wksp_disc_op_result,x ; Store in disc op workspace
8C2C DEY ; Next entry byte
8C2D DEX ; Next workspace byte
8C2E BNE copy_alloc_sector_loop ; Loop for 4 bytes
8C30 .write_dir_entry←1← 8C21 BEQ
LDA #1 ; Disc op result = 1
8C32 STA wksp_disc_op_result ; Store result
8C35 LDA #8 ; SCSI read command = 8
8C37 STA wksp_disc_op_command ; Store command byte
8C3A LDA #0 ; Clear control byte
8C3C STA wksp_disc_op_control ; Store control
8C3F LDY #&16 ; Y=&16: entry offset for start sector
8C41 LDX #3 ; X=3: copy 3+1 sector bytes
8C43 .copy_name_byte_loop←1← 8C4A BNE
LDA (zp_entry_ptr_lo),y ; Get sector byte from entry
8C45 STA wksp_disc_op_command,x ; Store in disc op command block
8C48 INY ; Next entry byte
8C49 DEX ; Next command byte
8C4A BNE copy_name_byte_loop ; Loop for 3 bytes
8C4C LDY #&15 ; Y=&15: entry offset for length
8C4E LDX #4 ; X=4: copy length bytes
8C50 .set_access_bits_loop←1← 8C57 BNE
LDA (zp_entry_ptr_lo),y ; Get length byte from entry
8C52 STA wksp_disc_op_control,x ; Store in control field
8C55 DEY ; Next byte
8C56 DEX ; Next control byte
8C57 BNE set_access_bits_loop ; Loop for 4 bytes
8C59 JSR multi_sector_disc_command ; Calculate sector count from length
8C5C .store_length_and_sector←1← 8F83 JMP
JSR search_dir_for_file ; Validate checksum and flags
8C5F JMP get_object_type_result ; Save workspace and return

Search directory for matching file

Copy catalogue data from the entry at (zp_entry_ptr) to workspace and the OSFILE control block, then extract the R/W/L access attributes from bit 7 of name bytes 0-2 into a standard OSFILE access byte (L0WRL0WR format). Search the current directory for a matching filename.

8C62 .search_dir_for_file←2← 8C5C JSR← A89E JSR
JSR conditional_info_display ; Display info if *OPT1 verbose
8C65 .search_dir_with_wildcards←2← 8CC3 JSR← 9218 JSR
LDY #&15 ; Y=&15: start of entry data in dir
8C67 LDX #&0b ; X=&0B: copy 12 bytes to workspace
8C69 .scan_dir_entries_loop←1← 8C70 BPL
LDA (zp_entry_ptr_lo),y ; Get entry data byte
8C6B STA wksp_disc_op_result,x ; Store in disc op workspace
8C6E DEY ; Next entry byte (decreasing)
8C6F DEX ; Next workspace byte (decreasing)
8C70 BPL scan_dir_entries_loop ; Loop for 12 bytes
8C72 LDY #&0d ; Y=&0D: copy to OSFILE control block
8C74 LDX #&0b ; X=&0B: 12 bytes
8C76 .compare_entry_names_loop←1← 8C7D BPL
LDA wksp_disc_op_result,x ; Get byte from workspace
8C79 STA (zp_osfile_ptr_lo),y ; Store in OSFILE control block
8C7B DEY ; Next control block byte
8C7C DEX ; Next workspace byte
8C7D BPL compare_entry_names_loop ; Loop for 12 bytes
; Extract access attributes from entry name bytes.
; Bit 7 of each name byte stores one access attribute:
; byte 0 bit 7 = R (read)
; byte 1 bit 7 = W (write)
; byte 2 bit 7 = L (locked)
; The loop collects these into 00000LWR, then the
; bit-shuffling below produces the OSFILE access byte
; format L0WRL0WR, duplicating the same R/W/L bits into
; both the owner (bits 3,1,0) and public (bits 7,5,4)
; nibbles. ADFS does not distinguish owner from public.
8C7F LDA #0 ; Clear access accumulator
8C81 STA wksp_csd_sector_temp ; Store zero in workspace
8C84 LDY #2 ; Y=2: extract from bytes 2,1,0
8C86 .extract_entry_access_loop←1← 8C8D BPL
LDA (zp_entry_ptr_lo),y ; Get name byte from entry
8C88 ASL ; Shift bit 7 (attribute) into carry
8C89 ROL wksp_csd_sector_temp ; Rotate carry into accumulator
8C8C DEY ; Next name byte (decreasing Y)
8C8D BPL extract_entry_access_loop ; Loop: Y=2(L), Y=1(W), Y=0(R)
8C8F LDA wksp_csd_sector_temp ; A = 00000LWR, C = 0
8C92 ROR ; A = 000000LW, C = R
8C93 ROR ; A = R000000L, C = W
8C94 ROR ; A = WR000000, C = L
8C95 PHP ; Save C = L on stack
8C96 LSR ; A = 0WR00000, C = 0 (LSR)
8C97 PLP ; Restore C = L from stack
8C98 ROR ; A = L0WR0000, C = 0
8C99 STA wksp_csd_sector_temp ; Store L0WR0000
8C9C LSR ; A = 0000L0WR after 4x LSR
8C9D LSR ; Second shift
8C9E LSR ; Third shift
8C9F LSR ; Fourth shift
8CA0 ORA wksp_csd_sector_temp ; L0WR0000 OR 0000L0WR = L0WRL0WR
8CA3 LDY #&0e ; Y=&0E: OSFILE access byte position
8CA5 STA (zp_osfile_ptr_lo),y ; Store access byte in control block
8CA7 RTS ; Return
8CA8 .osfile_save_handler
LDY #0 ; Y=0: get filename addr from block
8CAA LDA (zp_osfile_ptr_lo),y ; Get filename address low
8CAC STA zp_text_ptr_lo ; Store in (&B4)
8CAE INY
8CAF LDA (zp_osfile_ptr_lo),y ; Get filename address high
8CB1 STA zp_text_ptr_hi ; Store in (&B5)
8CB3 JSR find_first_matching_entry ; Search for file in directory
8CB6 BNE delete_existing_before_save ; Found? Copy catalogue info
8CB8 LDY #4 ; Y=4: check E attribute
8CBA LDA (zp_entry_ptr_lo),y ; Get E attribute byte
8CBC BPL check_existing_for_save ; Bit 7 clear: not E, copy info
8CBE LDA #&ff ; E attribute: return A=&FF
8CC0 JMP save_wksp_and_return ; Save workspace and return

Check for existing file before save

Search directory using wildcards for an existing entry matching the save filename.

8CC3 .check_existing_for_save←2← 8CBC BPL← 90FE JMP
JSR search_dir_with_wildcards ; Copy catalogue info to block
8CC6 .delete_existing_before_save←1← 8CB6 BNE
JMP get_object_type_result ; Save workspace and return

Parse filename from OSFILE block and search

Extract filename from the OSFILE control block, parse the path, and search the current directory.

On ExitAcorrupted (Z set if found, (&B6) points to entry)
Xcorrupted
Ycorrupted
8CC9 .parse_osfile_and_search←3← 8CE2 JSR← 8CE9 JSR← 911E JSR
LDY #0 ; Y=0: get filename from block
8CCB LDA (zp_osfile_ptr_lo),y ; Get filename address low
8CCD STA zp_text_ptr_lo ; Store in (&B4)
8CCF INY
8CD0 LDA (zp_osfile_ptr_lo),y ; Get filename address high
8CD2 STA zp_text_ptr_hi ; Store in (&B5)
8CD4 JSR set_up_gsinit_path ; Parse path and set up directory
8CD7 JSR find_first_matching_entry ; Search for file
8CDA BEQ return_16 ; Found: return Z set
8CDC JSR check_special_dir_char
8CDF BEQ mark_entry_dirty ; Z clear: check for create
8CE1 .return_16←1← 8CDA BEQ
RTS ; Return
8CE2 .build_osfile_control_block←1← A552 JSR
JSR parse_osfile_and_search ; Parse filename and search
8CE5 BEQ check_file_not_open ; Found: proceed to delete
8CE7 BNE check_4byte_addrs
8CE9 .copy_osfile_addrs←1← 8DF3 JSR
JSR parse_osfile_and_search ; Parse filename and search
8CEC BEQ write_entry_metadata ; Found: check if directory
8CEE .check_4byte_addrs←1← 8CE7 BNE
LDY #0 ; Y=0: check remaining path
8CF0 .copy_3byte_addrs_loop←1← 8D02 BNE
LDA (zp_text_ptr_lo),y ; Get next path character
8CF2 CMP #&2e ; Is it '.' (path separator)?
8CF4 BNE copy_4byte_addrs_loop ; Yes: check for ^ or @ error
8CF6 .mark_entry_dirty←1← 8CDF BEQ
JMP not_found_error ; Check for ^ or @ prefix error
8CF9 .copy_4byte_addrs_loop←1← 8CF4 BNE
CMP #&21 ; Is it printable (> '!')?
8CFB BCC update_entry_from_osfile ; No: end of filename
8CFD CMP #&22 ; Is it '"'?
8CFF BEQ update_entry_from_osfile ; Yes: end of filename
8D01 INY ; Next character
8D02 BNE copy_3byte_addrs_loop ; Loop scanning filename
8D04 .update_entry_from_osfile←2← 8CFB BCC← 8CFF BEQ
LDA #&11 ; A=&11: return code for file found
8D06 RTS ; Return
8D07 .write_entry_metadata←1← 8CEC BEQ
LDY #3 ; Y=3: check if it's a directory
8D09 LDA (zp_entry_ptr_lo),y ; Get access byte
8D0B BPL check_file_not_open ; Bit 7 clear: not dir, create file
8D0D JMP already_exists_error2 ; Directory: Already exists error

Check file is not locked or open

Check the entry at (&B6) for the locked attribute and generate a Locked error if set. Then check whether any files on the current drive are open.

8D10 .check_file_not_open←5← 8CE5 BEQ← 8D0B BPL← 9128 JSR← A589 JSR← B306 JSR
LDY #2 ; Y=2: check file access
8D12 LDA (zp_entry_ptr_lo),y ; Get access byte 2 (L attribute)
8D14 BPL check_open
8D16 JSR reload_fsm_and_dir_then_brk
8D19 EQUB &C3 ; Error &C3: Locked
8D1A EQUS "Locked", &00
fall through ↓

Check if file is open

Check whether any files are currently open on a given drive. Used before operations that would be unsafe with open files.

8D21 .check_open←2← 8D14 BPL← B2EF JSR
LDX #9 ; X=9: check all 10 channels
8D23 .check_open_channel_loop←1← 8D6A BPL
LDA wksp_ch_flags,x ; Get channel flags
8D26 BEQ no_open_files_on_drive ; Channel not open? Skip
8D28 LDA wksp_ch_start_sec_h,x ; Get channel's drive number
8D2B AND #&e0 ; Isolate drive bits (top 3)
8D2D CMP wksp_current_drive ; Compare with current drive
8D30 BNE no_open_files_on_drive ; Different drive? Skip
8D32 LDA wksp_ch_dir_sec_ml,x ; Compare sector address byte
8D35 CMP wksp_csd_sector_lo ; With target sector
8D38 BNE no_open_files_on_drive ; No match? Skip
8D3A LDA wksp_ch_dir_sec_mh,x ; Compare sector mid byte
8D3D CMP wksp_csd_sector_mid ; With target sector mid
8D40 BNE no_open_files_on_drive ; No match? Skip
8D42 LDA wksp_ch_dir_sec_h,x ; Compare sector high byte
8D45 CMP wksp_csd_sector_hi ; With target sector high
8D48 BNE no_open_files_on_drive ; No match? Skip
8D4A LDY #&19 ; Y=&19: compare sequence number
8D4C LDA (zp_entry_ptr_lo),y ; Get entry sequence from dir
8D4E CMP wksp_ch_seq_num,x ; Compare with channel's sequence
8D51 BNE no_open_files_on_drive ; Mismatch: not the same file
8D53 .channel_on_same_drive←1← B24A JMP
JSR reload_fsm_and_dir_then_brk
8D56 EQUB &C2 ; Error &C2: Already open
8D57 EQUS "Can't - File open", &00
fall through ↓

No open file conflict found

All channels checked with no conflicts. Continue with the file operation.

8D69 .no_open_files_on_drive←6← 8D26 BEQ← 8D30 BNE← 8D38 BNE← 8D40 BNE← 8D48 BNE← 8D51 BNE
DEX ; Next channel
8D6A BPL check_open_channel_loop ; Loop for all 10 channels
8D6C INX ; X=1: no conflict found
8D6D RTS ; Return (X=1 = no conflict)

Validate path and check for wildcards

Scan the filename at (&B4) checking for invalid characters and wildcards. Generates Bad name or Wild cards errors for invalid patterns.

8D6E .set_up_directory_search←2← 8712 JSR← 8DBD JSR
LDY #0 ; Y=0: scan filename
8D70 JSR check_char_is_terminator
8D73 BNE begin_pathname_scan ; Non-terminator: check for wildcards
8D75 CMP #&2e ; Is it '.'?
8D77 BEQ bad_name_in_path ; Dot: wild cards error
8D79 RTS ; Return (no wildcards)
8D7A .begin_pathname_scan←1← 8D73 BNE
CMP #&3a ; Is it ':'?
8D7C BNE skip_dot_in_path ; No: check path components
8D7E INY ; Skip past ':D' drive specifier
8D7F .scan_name_bytes_loop←1← 8D91 BEQ
INY ; Skip past drive number
8D80 JSR check_char_is_terminator
8D83 BNE bad_name_in_path ; Non-zero: wild cards error
8D85 CMP #&2e ; Is it '.'?
8D87 BNE return_17 ; No dot after drive: return
8D89 INY ; Skip past dot
8D8A JSR check_path_terminator ; Get next character
8D8D .skip_dot_in_path←1← 8D7C BNE
AND #&fd ; Strip to check for '$'
8D8F CMP #&24 ; Is it '$' (root)?
8D91 BEQ scan_name_bytes_loop ; Yes: continue past root specifier
8D93 .scan_name_alpha_loop←1← 8DA9 BNE
JSR check_path_terminator ; Get next path character
8D96 CMP #&5e ; Is it '^' (parent)?
8D98 BEQ check_bad_name_char ; Yes: skip past it
8D9A CMP #&40 ; Is it '@' (current)?
8D9C BNE c8dab ; No: check for wildcards in name
8D9E .check_bad_name_char←1← 8D98 BEQ
INY ; Skip past ^ or @ specifier
8D9F JSR check_char_is_terminator
8DA2 BNE bad_name_in_path ; Non-terminator: wild cards error
8DA4 .check_special_chars_loop←1← 8DAE BEQ
CMP #&2e ; Is it '.'?
8DA6 BNE return_17 ; No dot: return
8DA8 INY ; Skip past dot
8DA9 BNE scan_name_alpha_loop ; Continue scanning
8DAB .c8dab←2← 8D9C BNE← 8DBB BNE
JSR check_char_is_terminator
8DAE BEQ check_special_chars_loop ; Terminator: check for dot
8DB0 LDX #5 ; X=5: check against 6 special chars
8DB2 .valid_name_continue_loop←1← 8DB8 BPL
CMP tbl_forbidden_chars,x ; Compare with special char table
8DB5 BEQ bad_name_in_path ; Match: wild cards error
8DB7 DEX ; Next special char
8DB8 BPL valid_name_continue_loop ; Loop for 6 chars
8DBA INY ; Next filename character
8DBB BNE c8dab ; Continue scanning
8DBD .set_up_gsinit_path←3← 8CD4 JSR← A50C JSR← B2FE JSR
JSR set_up_directory_search ; Save text pointer low
8DC0 .gsinit_scan_loop←1← 8DD3 BNE
LDA (zp_text_ptr_lo),y ; Save text pointer high
8DC2 AND #&7f ; Push on stack
8DC4 CMP #&2a
8DC6 BEQ wild_cards_error
8DC8 CMP #&23
8DCA BEQ wild_cards_error
8DCC CMP #&2e ; Is it '.'?
8DCE BEQ return_17 ; Restore text pointer high
8DD0 DEY ; Decrement index
8DD1 CPY #&ff ; Restore text pointer low
8DD3 BNE gsinit_scan_loop
8DD5 .return_17←4← 8D87 BNE← 8DA6 BNE← 8DCE BEQ← 8DD9 BNE
RTS ; Return

Check next path character is terminator

Read the character at (&B4),Y and generate a Bad name error if it is not a filename terminator.

8DD6 .check_path_terminator←2← 8D8A JSR← 8D93 JSR
JSR check_char_is_terminator ; Get character at (&B4),Y
8DD9 BNE return_17
8DDB .bad_name_in_path←4← 8D77 BEQ← 8D83 BNE← 8DA2 BNE← 8DB5 BEQ
JMP bad_name_error ; Wild cards found: Bad name error

Raise Wild cards error

Reload FSM and directory then raise error &FD: Wild cards.

8DDE .wild_cards_error←2← 8DC6 BEQ← 8DCA BEQ
JSR reload_fsm_and_dir_then_brk
8DE1 EQUB &FD ; Error &FD: Wild cards
8DE2 EQUS "Wild cards", &00
fall through ↓

Forbidden filename characters

Six characters that may not appear in ADFS filenames because they have special meaning in the pathname syntax. The path validator at set_up_directory_search loops through this table, rejecting any filename containing these characters.

8DED .tbl_forbidden_chars←1← 8DB2 CMP
EQUB &7F ; &7F: DEL (control character)
8DEE EQUB &5E ; '^': parent directory specifier
8DEF EQUB &40 ; '@': current directory specifier
8DF0 EQUB &3A ; ':': drive separator
8DF1 EQUB &24 ; '$': root directory specifier
8DF2 EQUB &26 ; '&': hex number prefix

Copy OSFILE addresses and search for empty entry

Copy the load and exec addresses from the OSFILE control block into the disc operation workspace, then search the current directory for an empty entry slot to use for a new file. Called when creating files via OSFILE save, *CDIR, *RENAME, and *COPY.

8DF3 .copy_addrs_and_find_empty_entry←4← 8F4C JSR← 9594 JSR← A649 JSR← A8DC JSR
JSR copy_osfile_addrs ; A=&FF: mark saved drive as unset
8DF6 .search_dir_for_new_entry←1← A556 JSR
BNE no_empty_entry_found ; Ensure directory integrity
8DF8 LDX #2
8DFA LDY #&12
8DFC LDA (zp_entry_ptr_lo),y
8DFE CMP #1
8E00 .scan_entry_bytes_loop←1← 8E09 BPL
INY ; Next entry byte
8E01 LDA #0 ; Get name and compare
8E03 ADC (zp_entry_ptr_lo),y
8E05 STA wksp_entry_size_base,y
8E08 DEX ; Clear carry for addition
8E09 BPL scan_entry_bytes_loop ; Add 26 bytes
8E0B LDY #&18 ; Store updated pointer low
8E0D LDX #2 ; No page crossing
8E0F .find_empty_entry_loop←1← 8E16 BPL
LDA (zp_entry_ptr_lo),y ; Increment page
8E11 STA wksp_object_sector,x ; Continue searching
8E14 DEY ; Back up one entry position
8E15 DEX ; Compare pointer with &16B1
8E16 BPL find_empty_entry_loop
8E18 RTS ; Return (slot found)
8E19 .no_empty_entry_found←1← 8DF6 BNE
LDA dir_last_entry_area ; Below limit: slot found
8E1C BEQ check_name_already_exists ; Exactly at limit: dir full
8E1E JSR reload_fsm_and_dir_then_brk
8E21 EQUB &B3 ; Error &B3: Dir full
8E22 EQUS "Dir full", &00
fall through ↓

Insert new entry at sorted position in directory

Insert a new directory entry at the position indicated by zp_entry_ptr, which was set by parse_pathname_entry to the first entry that sorts after the new name. Shifts all entries from the end of the directory (&16B1) backwards to zp_entry_ptr up by 26 bytes to open a gap, then returns with zp_text_ptr restored to the saved command text position. The caller then fills in the gap with the new entry's data.

This maintains the ascending alphabetical order invariant that the directory search at begin_dir_entry_search depends on for its sorted early-exit optimisation.

8E2B .check_name_already_exists←1← 8E1C BEQ
LDA zp_text_ptr_lo ; Save text pointer low
8E2D STA wksp_tube_transfer_addr_1 ; Store in workspace
8E30 LDA zp_text_ptr_hi ; Save text pointer high
8E32 STA wksp_tube_xfer_addr_2 ; Store in workspace
8E35 LDA #&b1 ; Source = &16B1 (last entry area)
8E37 STA zp_text_ptr_lo ; Store pointer low
8E39 LDA #&16 ; Page &16
8E3B STA zp_text_ptr_hi ; Store pointer high
8E3D LDY #&1a ; Y=&1A: dest offset (one entry up)
8E3F LDX #6 ; X=6: clear 7 bytes of new entry
8E41 LDA #0 ; A=0: zero fill
8E43 .compare_names_loop←1← 8E47 BNE
STA wksp_last_access_drive,x ; Clear workspace byte
8E46 DEX ; Next byte
8E47 BNE compare_names_loop ; Loop for 7 bytes
; Shift entries up by one position (26 bytes) working
; backwards from end of directory towards the insertion
; point at (&B6). Opens a 26-byte gap for the new entry.
8E49 .copy_entry_data_loop←1← 8E61 JMP
LDA (zp_text_ptr_lo,x) ; Get byte from current position
8E4B STA (zp_text_ptr_lo),y ; Store 26 bytes higher (Y=&1A)
8E4D LDA zp_text_ptr_lo ; Reached insertion point (&B6)?
8E4F CMP zp_entry_ptr_lo ; Compare low byte
8E51 BNE write_entry_to_dir ; Not yet: keep shifting
8E53 LDA zp_text_ptr_hi ; Compare high byte
8E55 CMP zp_entry_ptr_hi ; Match: insertion point reached
8E57 BEQ mark_directory_modified ; Gap opened: restore text ptr
8E59 .write_entry_to_dir←1← 8E51 BNE
LDA zp_text_ptr_lo ; Decrement source pointer
8E5B BNE mark_entry_created ; Low byte non-zero
8E5D DEC zp_text_ptr_hi ; Zero: borrow from high byte
8E5F .mark_entry_created←1← 8E5B BNE
DEC zp_text_ptr_lo ; Decrement low byte
8E61 JMP copy_entry_data_loop ; Continue shifting backwards
8E64 .mark_directory_modified←1← 8E57 BEQ
LDA wksp_tube_transfer_addr_1 ; Restore text pointer low
8E67 STA zp_text_ptr_lo ; Store back in (&B4)
8E69 LDA wksp_tube_xfer_addr_2 ; Restore text pointer high
8E6C STA zp_text_ptr_hi ; Store back in (&B5)
8E6E RTS ; Return

Allocate disc space and store in entry

Allocate disc space from the FSM for the requested file size, then store the allocated sector address in the directory entry at (&B6).

8E6F .allocate_disc_space_for_file←3← 8F4F JSR← A64C JSR← A8DF JSR
LDY #9 ; Get OSFILE block pointer low
8E71 .copy_alloc_request_loop←1← 8E88 BPL
LDA (zp_text_ptr_lo),y ; Store in (&B8)
8E73 AND #&7f ; Get OSFILE block pointer high
8E75 CMP #&21 ; Store in (&B9)
8E77 BCC store_allocated_sector ; Y=2: copy 3-byte start sector
8E79 CMP #&22 ; Get start sector byte from entry
8E7B BNE check_exact_alloc
8E7D .store_allocated_sector←1← 8E77 BCC
LDA #&0d ; A=CR: pad entry name
8E7F .check_exact_alloc←1← 8E7B BNE
CPY #2 ; Next byte (decreasing Y)
8E81 BCS reduce_alloc_to_available
8E83 ORA #&80 ; Set bit 7 for D attribute
8E85 .reduce_alloc_to_available←1← 8E81 BCS
STA (zp_entry_ptr_lo),y ; Compare with 1 (round up sectors)
8E87 DEY ; Next byte
8E88 BPL copy_alloc_request_loop ; Loop for all name bytes
8E8A RTS ; Return

Copy OSFILE template into directory entry

Copy filename, attributes, and sector information from the OSFILE workspace into the directory entry at (&B6).

8E8B .copy_entry_from_template←2← 8F52 JSR← A65D JSR
LDY #&11 ; Y=&11: copy filename and attributes
8E8D .copy_name_to_entry_loop←1← 8E93 BPL
LDA (zp_osfile_ptr_lo),y ; Get name byte from workspace
8E8F STA wksp_disc_op_result,y
8E92 DEY ; Next byte
8E93 BPL copy_name_to_entry_loop ; Loop for 10 bytes
8E95 LDY #&12 ; Increment dir sequence number
8E97 SEC ; Set carry for sector calculation
8E98 LDX #3 ; Store updated sequence in header
8E9A .copy_access_byte_loop←1← 8EA4 BPL
LDA wksp_entry_calc_base,y
8E9D SBC wksp_entry_field_base,y
8EA0 STA (zp_entry_ptr_lo),y ; Store sequence in entry
8EA2 INY ; Next byte
8EA3 DEX ; Decrement counter
8EA4 BPL copy_access_byte_loop ; Loop for required bytes
8EA6 LDY #&0a ; Y=&0A: copy access byte
8EA8 .store_entry_lengths_loop←1← 8EB0 BNE
LDA wksp_entry_field_base,y ; Get OSFILE data byte
8EAB STA (zp_entry_ptr_lo),y ; Store in directory entry
8EAD INY ; Next byte
8EAE CPY #&12 ; Past length field (Y=&12)?
8EB0 BNE store_entry_lengths_loop ; No: continue copying
8EB2 LDA zp_entry_ptr_lo ; Save (&B6) for entry shifting
8EB4 PHA ; Push on stack
8EB5 LDA zp_entry_ptr_hi ; Save (&B7)
8EB7 PHA ; Push on stack
8EB8 .store_entry_3byte_sector←1← 8EEA JMP
LDA #5 ; Point to first dir entry (&1205)
8EBA STA zp_entry_ptr_lo ; Store pointer low
8EBC LDA #&12 ; Page &12
8EBE STA zp_entry_ptr_hi ; Store pointer high
8EC0 .store_entry_4byte_sector←2← 8ED6 BCC← 8EDA BCS
LDY #0 ; Y=0: check entry
8EC2 LDA (zp_entry_ptr_lo),y ; Get first byte
8EC4 BEQ copy_osfile_to_entry_loop ; Zero: end of entries, done
8EC6 LDY #&19 ; Y=&19: check sequence number
8EC8 LDA (zp_entry_ptr_lo),y ; Get entry sequence
8ECA CMP dir_master_sequence ; Compare with dir master sequence
8ECD BEQ update_entry_access ; Match: needs incrementing
8ECF CLC ; Clear carry for entry advance
8ED0 LDA zp_entry_ptr_lo ; Get pointer low
8ED2 ADC #&1a ; Add 26 bytes per entry
8ED4 STA zp_entry_ptr_lo ; Store updated pointer
8ED6 BCC store_entry_4byte_sector ; No page crossing: continue
8ED8 INC zp_entry_ptr_hi ; Increment page
8EDA BCS store_entry_4byte_sector
8EDC .update_entry_access←1← 8ECD BEQ
LDA dir_master_sequence ; Get master sequence number
8EDF CLC ; Clear carry for BCD add
8EE0 SED ; Switch to BCD mode
8EE1 ADC #1 ; Increment sequence (BCD)
8EE3 CLD ; Back to binary mode
8EE4 STA dir_master_sequence ; Store updated sequence in footer
8EE7 STA dir_buffer ; Also store in header
8EEA JMP store_entry_3byte_sector ; Retry from beginning of entries
8EED .copy_osfile_to_entry_loop←1← 8EC4 BEQ
PLA ; Restore (&B7) from stack
8EEE STA zp_entry_ptr_hi ; Store back
8EF0 PLA ; Restore (&B6) from stack
8EF1 STA zp_entry_ptr_lo ; Store back
8EF3 LDY #&19 ; Y=&19: store new sequence in entry
8EF5 LDA dir_master_sequence ; Get current master sequence
8EF8 STA (zp_entry_ptr_lo),y ; Store in the new entry
8EFA LDA #1 ; Result = 1 (file created)
8EFC STA wksp_disc_op_result ; Store result code
8EFF LDX #4 ; X=4: copy 4 transfer length bytes
8F01 .copy_load_addr_loop←1← 8F08 BNE
LDA wksp_disc_op_sector_count,x ; Get transfer count byte
8F04 STA wksp_disc_op_result,x ; Copy to disc op result
8F07 DEX ; Next byte
8F08 BNE copy_load_addr_loop ; Loop for 4 bytes
8F0A LDA #&0a ; SCSI write command = &0A
8F0C STA wksp_disc_op_command ; Store command
8F0F LDA #0 ; Clear sector count (use transfer)
8F11 STA wksp_disc_op_sector_count ; Store zero sector count
8F14 LDA #0 ; A=0: clear control byte
8F16 STA wksp_disc_op_control ; Store zero control
8F19 LDY #&12 ; Y=&12: copy 4 length bytes to entry
8F1B .copy_exec_addr_to_entry_loop←1← 8F23 BNE
LDA (zp_entry_ptr_lo),y ; Get length byte from entry
8F1D STA wksp_entry_len_base,y ; Copy to workspace
8F20 INY ; Next byte
8F21 CPY #&16 ; Past length field (Y=&16)?
8F23 BNE copy_exec_addr_to_entry_loop ; No: continue
8F25 LDY #&12 ; Y=&12: calculate sector count
8F27 LDA (zp_entry_ptr_lo),y ; Get length low from entry
8F29 CMP #1 ; Compare with 1 (round up)
8F2B LDX #2 ; X=2: process 3 sector bytes
8F2D .check_if_updating_length←1← 8F36 BPL
LDA #0 ; A=0: zero for carry propagation
8F2F INY ; Next length byte
8F30 ADC (zp_entry_ptr_lo),y ; Add with carry from comparison
8F32 STA wksp_csd_drive_temp,y ; Store in sector workspace
8F35 DEX ; Next byte
8F36 BPL check_if_updating_length ; Loop for 3 bytes
8F38 BCC update_length_and_access ; No overflow: proceed
8F3A JMP disc_full_error ; Overflow: Disc full error
8F3D .update_length_and_access←1← 8F38 BCC
LDY #&16 ; Y=&16: mark entry sector as &FF
8F3F LDA #&ff ; A=&FF: temporary marker
8F41 STA (zp_entry_ptr_lo),y ; Store &FF in sector low
8F43 INY
8F44 STA (zp_entry_ptr_lo),y ; Store &FF in sector mid
8F46 INY
8F47 STA (zp_entry_ptr_lo),y ; Store &FF in sector high
8F49 JMP release_disc_space

Validate file is not locked then create entry

Check file is not locked or open, write the filename into the directory entry, allocate disc space, and copy the file length and sector address.

8F4C .validate_not_locked←3← 8F74 JSR← 8F7D JSR← B35A JSR
JSR copy_addrs_and_find_empty_entry ; Save (&B6) for restore
8F4F JSR allocate_disc_space_for_file ; Save (&B7)
8F52 .write_entry_sector_info←2← 95CA JSR← A8E2 JSR
JSR copy_entry_from_template ; Y=&0D: copy load/exec/length
8F55 JSR allocate_disc_space
8F58 .copy_length_to_entry←1← A660 JSR
LDY #&18 ; Y=&18: get OSFILE data bytes
8F5A LDX #2 ; Get OSFILE block byte
8F5C .copy_3byte_length_loop←1← 8F63 BPL
LDA wksp_alloc_sector,x
8F5F STA (zp_entry_ptr_lo),y
8F61 DEY ; Next entry byte (decreasing)
8F62 DEX ; Next workspace byte
8F63 BPL copy_3byte_length_loop ; Next OSFILE byte (decreasing)
8F65 LDX #2 ; Loop for 12 bytes
8F67 LDY #6 ; Restore (&B7) from stack
8F69 .copy_sector_to_entry_loop←1← 8F71 BPL
LDA wksp_alloc_sector,x
8F6C STA wksp_disc_op_result,y
8F6F INY ; X=2: 3 sector bytes
8F70 DEX ; Decrement counter
8F71 BPL copy_sector_to_entry_loop ; Get new sector byte from workspace
8F73 RTS ; Return
8F74 .osfile_load_handler
JSR validate_not_locked ; Store in directory entry
8F77 JSR multi_sector_disc_command
8F7A JMP search_for_osfile_target ; Next workspace byte (decreasing X)
8F7D .osfile_read_cat_info
JSR validate_not_locked ; Y=4: clear access byte 4
8F80 .search_for_osfile_target←2← 8F7A JMP← 962F JMP
JSR write_dir_and_validate ; Write directory and update
8F83 JMP store_length_and_sector ; Check and write entry

Write directory and FSM back to disc

Verify directory integrity, validate the free space map entries, then write the current directory to disc. Update the disc ID low byte from the System VIA Timer 1 counter (pseudo-random, for disc-change detection), cache both disc ID bytes in per-drive workspace, recalculate FSM checksums, and write both FSM sectors to disc.

8F86 .write_dir_and_validate←17← 897F JSR← 8F80 JSR← 90FB JSR← 9238 JSR← 96AC JSR← 97D4 JMP← 99C3 JSR← A007 JMP← A273 JMP← A5DF JSR← A5F8 JSR← A663 JSR← A6C4 JMP← A933 JSR← AF4F JSR← B35D JSR← B462 JSR
JSR verify_dir_integrity ; Verify directory integrity
8F89 JSR validate_fsm_entries ; Validate FSM entries
8F8C LDX #&0a ; X=&0A: copy 11 template bytes
8F8E .copy_dir_write_template_loop←1← 8F95 BPL
LDA disc_op_tpl_read_dir,x ; Get disc op template byte from ROM
8F91 STA wksp_disc_op_result,x ; Store in disc op workspace
8F94 DEX ; Next template byte
8F95 BPL copy_dir_write_template_loop ; Loop for template bytes
8F97 LDA #&0a ; Patch to write command (&0A)
8F99 STA wksp_disc_op_command
8F9C LDA wksp_csd_sector_lo ; Get CSD sector low
8F9F STA wksp_disc_op_sector_lo
8FA2 LDA wksp_csd_sector_mid
8FA5 STA wksp_disc_op_sector_mid
8FA8 LDA wksp_csd_sector_hi ; Get CSD sector high
8FAB STA wksp_disc_op_sector ; Store in disc op sector high
8FAE JSR exec_disc_op_from_wksp ; Write directory to disc
8FB1 LDA wksp_current_drive ; Get current drive
8FB4 JSR convert_drive_to_slot ; Get drive slot index in X
8FB7 LDA fsm_s1_disc_id_hi ; Cache disc ID high in workspace
8FBA STA wksp_disc_id_hi,x
8FBD LDA system_via_t1c_l ; Read VIA T1 counter as new disc ID low
8FC0 STA wksp_disc_id_lo,x ; Cache disc ID low in workspace
8FC3 STA fsm_s1_disc_id_lo
8FC6 JSR calc_fsm_checksums ; Calculate FSM checksums
8FC9 STX fsm_s0_checksum ; Store sector 0 checksum
8FCC STA fsm_s1_checksum ; Store sector 1 checksum
8FCF LDX #&71 ; Point to write-FSM template
8FD1 LDY #&90 ; Write FSM sectors to disc
8FD3 JSR exec_disc_command
8FD6 LDA zp_adfs_flags
8FD8 AND #&ef ; Clear FSM-inconsistent flag
8FDA STA zp_adfs_flags ; Return
8FDC LDA #0 ; A=0: success
8FDE RTS ; Return

Find first matching directory entry

Parse a filename from the command line and search the current directory for the first entry matching the parsed filename pattern.

On ExitAcorrupted (Z set if found, (&B6) points to entry)
Xcorrupted
Ycorrupted
8FDF .find_first_matching_entry←13← 8BE5 JSR← 8CB3 JSR← 8CD7 JSR← 94E7 JSR← 993D JSR← 9A2D JSR← 9C43 JSR← A586 JSR← A672 JSR← B20E JSR← B2E6 JSR← B301 JSR← B36D JSR
JSR parse_filename_from_cmdline ; Parse filename from command line
8FE2 PHP ; Save flags across FSM validation
8FE3 PHA ; Save A on stack
8FE4 JSR validate_fsm_checksums
8FE7 PLA ; Restore A
8FE8 PLP ; Restore flags from parse result
8FE9 .return_18←3← 8FF8 BEQ← 900C BEQ← 9028 BCC
RTS ; Return

Validate FSM entry structure and checksums

Validate the in-memory free space map by checking entry structure (via validate_fsm_entries) and recalculating both sector checksums (via calc_fsm_checksums). Raises a Bad FS map error if entries are malformed or checksums do not match. Called as a guard before operations that modify the FSM or directory.

8FEA .validate_fsm_checksums←5← 8FE4 JSR← 9FFA JSR← A255 JSR← A754 JSR← A872 JSR
JSR validate_fsm_entries
8FED JSR calc_fsm_checksums ; Recalculate FSM checksums
8FF0 CMP fsm_s1_checksum ; Compare sector 1 checksum
8FF3 BNE bad_fs_map_error ; Mismatch: bad FS map
8FF5 CPX fsm_s0_checksum
8FF8 BEQ return_18
fall through ↓

Raise Bad FS map error

Generate a Bad FS map error (&A9) via generate_disc_error. Called when FSM checksum validation fails.

8FFA .bad_fs_map_error←7← 8FF3 BNE← 9017 BEQ← 901A BEQ← 9021 BNE← 903A BCS← 9045 BNE← 904A BMI
JSR generate_disc_error ; Bad FS map error
8FFD EQUB &A9 ; Error &A9: Bad FS map
8FFE EQUS "Bad FS map", &00
9009 .validate_fsm_entries←2← 8F89 JSR← 8FEA JSR
LDX fsm_s1_end_of_list_ptr ; Get FSM end-of-list pointer
900C BEQ return_18 ; Empty: return OK
900E LDA #0 ; A=0: init check accumulator
9010 .check_fsm_entry_loop←1← 901D BNE
ORA fsm_s0_pre1,x ; OR entry address high byte
9013 ORA fsm_s0_checksum,x ; OR entry length high byte
9016 DEX ; Back up one
9017 BEQ bad_fs_map_error ; At entry 0: bad FS map
9019 DEX ; Back up more
901A BEQ bad_fs_map_error ; At entry 0: bad FS map
901C DEX ; Back up more
901D BNE check_fsm_entry_loop ; Loop for all entries
901F AND #&e0 ; Check drive bits in accumulator
9021 BNE bad_fs_map_error ; Non-zero: bad FS map
9023 LDX fsm_s1_end_of_list_ptr ; Get end pointer again
9026 CPX #6 ; Need at least 2 entries (>= 6)
9028 BCC return_18 ; Not enough: return OK (empty disc)
902A LDX #3 ; X=3: check entry ordering
902C .check_fsm_ordering←1← 9059 BCC
LDY #2 ; Y=2: compare 3-byte addresses
902E CLC ; Clear carry for addition
902F .add_entry_size_loop←1← 9038 BPL
LDA fsm_s0_pre3,x ; Get prev entry address byte
9032 ADC fsm_s0_disc_size_mid,x ; Add prev entry length byte
9035 PHA ; Push result on stack
9036 INX ; Next byte
9037 DEY ; Next comparison byte
9038 BPL add_entry_size_loop ; Loop for 3 bytes
903A BCS bad_fs_map_error ; Carry set: overlap, bad FS map
903C LDY #2 ; Y=2: compare prev+size with next
903E .compare_with_next_entry_loop←1← 9048 BPL
PLA ; Pop result byte
903F DEX ; Back up X
9040 CMP fsm_sector_0,x ; Compare with next entry address
9043 BCC discard_comparison_bytes ; Below: entries are ordered OK
9045 BNE bad_fs_map_error ; Above: bad ordering, bad FS map
9047 DEY ; Next comparison byte
9048 BPL compare_with_next_entry_loop ; Loop for 3 bytes
904A BMI bad_fs_map_error
904C .discard_comparison_bytes←2← 9043 BCC← 904F BPL
PLA ; Discard remaining stack bytes
904D DEX ; Back up X
904E DEY ; Decrement Y too
904F BPL discard_comparison_bytes ; More to discard
9051 PHA ; Push separator
9052 INX ; Advance to next entry pair
9053 INX ; Continue advancing
9054 INX ; Continue advancing
9055 INX ; Continue advancing
9056 CPX fsm_s1_end_of_list_ptr ; Past end of list?
9059 BCC check_fsm_ordering ; No: check next pair
905B RTS ; All entries OK: return

Calculate FSM sector checksums

Compute 8-bit checksums of FSM sectors 0 and 1 by summing all 255 bytes of each sector.

On ExitAFSM sector 1 checksum
XFSM sector 0 checksum
Ycorrupted
905C .calc_fsm_checksums←2← 8FC6 JSR← 8FED JSR
CLC ; Clear carry for checksum
905D LDY #&ff ; Y=&FF: sum 255 bytes
905F TYA
9060 .checksum_s0_loop←1← 9064 BNE
ADC fsm_s0_pre1,y ; Add FSM sector 0 byte
9063 DEY ; Next byte
9064 BNE checksum_s0_loop ; Loop for 255 bytes
9066 TAX ; Save sector 0 checksum in X
9067 DEY ; Y=&FF for sector 1
9068 TYA ; Transfer to A
9069 CLC ; Clear carry
906A .checksum_s1_loop←1← 906E BNE
ADC fsm_s0_checksum,y ; Add FSM sector 1 byte
906D DEY ; Next byte
906E BNE checksum_s1_loop ; Loop for 255 bytes
9070 RTS ; Return (X=chk0, A=chk1)

Write-FSM disc operation template

Disc operation template for writing both FSM sectors (0 and

  1. from the FSM buffer at &0E00 back to disc. Used by write_dir_and_validate via exec_disc_command with X=&71, Y=&90 pointing to this template.
9071 .disc_op_tpl_write_fsm
EQUB &01 ; Result: &01 (default)
9072 EQUB &00 ; Memory address low: &00
9073 EQUB &0E ; Memory address high: &0E (-> &0E00 FSM buffer)
9074 EQUB &FF ; Memory address byte 3: &FF (host memory)
9075 EQUB &FF ; Memory address byte 4: &FF (host memory)
9076 EQUB &0A ; Command: &0A (write sectors)
9077 EQUB &00 ; Sector high: &00
9078 EQUB &00 ; Sector mid: &00
9079 EQUB &00 ; Sector low: &00 (sector 0)
907A EQUB &02 ; Sector count: &02 (2 sectors for FSM)
907B EQUB &00 ; Control: &00

OSFILE write catalogue info handler

Handle OSFILE A=1 (write all catalogue info), A=2 (write load address) and A=3 (write execution address). Finds the file, validates access, then updates the directory entry fields from the OSFILE parameter block.

907C .osfile_write_load_addr
STA wksp_disc_op_xfer_len_3 ; Store function code
907F JSR find_file_and_validate ; Find file, check access
9082 BEQ osfile_write_load_search ; Found?
9084 LDA #0 ; Not found: A=0 return
9086 RTS ; Return
9087 .osfile_write_load_search←1← 9082 BEQ
LDA wksp_disc_op_xfer_len_3 ; Get function code
908A CMP #3 ; A=3 (write exec addr)?
908C BEQ update_entry_after_write ; Yes: skip to exec addr
908E LDY #5 ; Y=5: get load addr from OSFILE blk
9090 LDX #3 ; X=3: copy 4 bytes
9092 .copy_load_to_entry_loop←1← 9099 BPL
LDA (zp_osfile_ptr_lo),y ; Get OSFILE block byte
9094 STA wksp_disc_op_result,x ; Store in workspace
9097 DEY ; Next OSFILE byte
9098 DEX ; Next workspace byte
9099 BPL copy_load_to_entry_loop ; Loop for 4 bytes
909B LDY #&0d ; Y=&0D: store in dir entry load addr
909D LDX #3 ; X=3: copy 4 bytes
909F .copy_exec_to_entry_loop←1← 90A6 BPL
LDA wksp_disc_op_result,x ; Get from workspace
90A2 STA (zp_entry_ptr_lo),y ; Store in directory entry
90A4 DEY ; Next entry byte
90A5 DEX ; Next workspace byte
90A6 BPL copy_exec_to_entry_loop ; Loop for 4 bytes
90A8 LDA wksp_disc_op_xfer_len_3 ; Get function code
90AB CMP #2 ; A=2 (write load addr only)?
90AD BEQ check_dir_access_bit ; Yes: skip exec addr, write dir
90AF .update_entry_after_write←1← 908C BEQ
LDY #9 ; Y=9: get exec addr from OSFILE blk
90B1 LDX #3 ; X=3: copy 4 bytes
90B3 .update_cat_info_loop←1← 90BA BPL
LDA (zp_osfile_ptr_lo),y ; Get OSFILE block byte
90B5 STA wksp_disc_op_result,x ; Store in workspace
90B8 DEY ; Next OSFILE byte
90B9 DEX ; Next workspace byte
90BA BPL update_cat_info_loop ; Loop for 4 bytes
90BC LDY #&11 ; Y=&11: store in dir entry exec addr
90BE LDX #3 ; X=3: copy 4 bytes
90C0 .copy_cat_info_to_entry_loop←1← 90C7 BPL
LDA wksp_disc_op_result,x ; Get from workspace
90C3 STA (zp_entry_ptr_lo),y ; Store in directory entry
90C5 DEY ; Next entry byte
90C6 DEX ; Next workspace byte
90C7 BPL copy_cat_info_to_entry_loop ; Loop for 4 bytes
90C9 LDX wksp_disc_op_xfer_len_3 ; Get function code again
90CC DEX ; X=0 means function was 1 (write all)
90CD BNE check_dir_access_bit ; A=1 (write all): continue to access
fall through ↓

Write access attributes to directory entry name bytes

Apply access bits from the OSFILE parameter block (offset &0E) to bit 7 of directory entry name bytes 0-2. Each name byte's lower 7 bits (the character) are preserved; only bit 7 is replaced.

The OSFILE access byte layout is L0WRL0WR: bit 0: R (owner read) bit 4: R (public read) bit 1: W (owner write) bit 5: W (public write) bit 3: L (owner locked) bit 7: L (public locked) bits 2,6: unused

For files (byte 3 bit 7 clear), the owner bits are used directly: bit 0 (R) to byte 0, bit 1 (W) to byte 1, bit 3 (L) to byte 2 (bit 2 is skipped via the loop re-entry at apply_access_bits_loop).

For directories (byte 3 bit 7 set), two extra LSRs skip the owner R and W bits, so only the L bit (bit 3) is applied to byte 2. Bytes 0 and 1 are left unchanged.

90CF .set_entry_access_from_osfile←1← 9104 BEQ
LDY #&0e ; Y=&0E: get access byte from OSFILE
90D1 LDA (zp_osfile_ptr_lo),y ; Get access byte from block
90D3 STA wksp_csd_sector_temp ; Store in workspace
90D6 LDY #3 ; Y=3: test directory flag
90D8 LDA (zp_entry_ptr_lo),y ; Get byte 3 from entry
90DA BPL access_bit_clear ; Bit 7 clear: file, use owner R,W,L
90DC LSR wksp_csd_sector_temp ; Dir: skip owner R (bit 0)
90DF LSR wksp_csd_sector_temp ; Dir: skip owner W (bit 1)
90E2 .apply_access_bits_loop←1← 90F9 BEQ
LSR wksp_csd_sector_temp ; Skip unused bit (bit 2 for file)
90E5 LDY #2 ; Y=2: start at byte 2 for L bit
90E7 BPL advance_access_bit
90E9 .access_bit_clear←1← 90DA BPL
LDY #0 ; Y=0: start with byte 0
90EB .advance_access_bit←2← 90E7 BPL← 90F7 BCC
LDA (zp_entry_ptr_lo),y ; Get name byte
90ED ASL ; Shift out bit 7 (old attribute)
90EE LSR wksp_csd_sector_temp ; Shift access bit into carry
90F1 ROR ; Shift carry into name bit 7
90F2 STA (zp_entry_ptr_lo),y ; Store updated name byte
90F4 INY ; Next byte
90F5 CPY #2 ; Past byte 2?
90F7 BCC advance_access_bit ; Y < 2: continue (R then W)
90F9 BEQ apply_access_bits_loop ; Y = 2: re-enter for L bit
90FB .check_dir_access_bit←2← 90AD BEQ← 90CD BNE
JSR write_dir_and_validate ; Write directory to disc
90FE JMP check_existing_for_save ; Return via catalogue info copy
9101 .osfile_delete_handler
JSR find_file_and_validate ; OSFILE A=4: write attributes only
9104 BEQ set_entry_access_from_osfile ; Found: write access byte
9106 LDA #0 ; Not found: A=0
9108 RTS ; Return

*REMOVE command handler

Remove a file from the current directory. Unlike *DELETE, *REMOVE does not report an error if the file is locked.

9109 .star_remove←1← A0BB JSR
JSR skip_spaces ; Skip leading spaces in filename
910C LDA zp_text_ptr_lo ; Save filename address in OSFILE blk
910E STA wksp_osfile_block ; Store filename addr in OSFILE block
9111 LDA zp_text_ptr_hi ; Get filename pointer high
9113 STA wksp_osfile_block_1 ; Store in OSFILE block+1
9116 LDA #&40 ; Point (&B8) to OSFILE control block
9118 STA zp_osfile_ptr_lo ; Store control block pointer low
911A LDA #&10 ; Control block page = &10
911C STA zp_osfile_ptr_hi ; Store control block pointer high
911E .search_and_delete_entry
JSR parse_osfile_and_search ; Search directory for the file
9121 BEQ check_and_delete_found ; Found? Proceed to delete
9123 LDA #0 ; Not found: A=0 (no error)
9125 JMP save_wksp_and_return ; Save workspace and return

Validate and delete a directory entry

Check file is not open, verify locked attribute, for directories confirm empty, then proceed with deletion.

9128 .check_and_delete_found←2← 9121 BEQ← 9A32 JSR
JSR check_file_not_open ; Check if file has open channels
912B LDY #3 ; Y=3: check access byte
912D LDA (zp_entry_ptr_lo),y ; Get access/attribute byte
912F BPL proceed_with_delete ; Bit 7 clear: regular file, skip
9131 LDY #3 ; Directory: check if empty
9133 .save_csd_for_dir_check_loop←1← 913A BPL
LDA wksp_csd_drive_sector,y ; Save CSD sector to temp workspace
9136 STA wksp_temp_sector,y ; Save CSD sector to temp workspace
9139 DEY ; Next byte
913A BPL save_csd_for_dir_check_loop ; Loop for 4 bytes
913C LDA #&ff ; Mark workspace as not saved
913E STA wksp_alt_sector_hi ; Mark alternative workspace as unset
9141 STA wksp_saved_drive ; Mark saved drive as unset
9144 JSR parse_path_and_load ; Load the subdirectory to check
9147 LDA dir_first_entry ; Is first entry empty (dir empty)?
914A PHP ; Save result (empty flag)
914B JSR save_wksp_and_return ; Restore CSD and directory
914E LDY #3 ; Y=3: restore 4 bytes of CSD sector
9150 .restore_csd_after_check_loop←1← 9157 BPL
LDA wksp_temp_sector,y ; Restore saved CSD sector
9153 STA wksp_csd_drive_sector,y ; Restore CSD sector byte
9156 DEY ; Next byte
9157 BPL restore_csd_after_check_loop ; Loop for 4 bytes
9159 PLP ; Restore empty flag
915A BEQ proceed_with_delete ; Directory was empty: proceed
915C JSR reload_fsm_and_dir_then_brk
915F EQUB &B4 ; Error &B4: Dir not empty
9160 EQUS "Dir not empty", &00
916E .proceed_with_delete←2← 912F BPL← 915A BEQ
LDY #&12 ; Get file size from directory entry
9170 LDX #2 ; X=2: 3 bytes of length to process
9172 LDA (zp_entry_ptr_lo),y ; Y=&12: length bytes offset
9174 CMP #1 ; Calculate number of sectors
9176 .copy_locked_name_loop←1← 917F BPL
INY ; Next length byte
9177 LDA #0 ; Add carry from previous byte
9179 ADC (zp_entry_ptr_lo),y ; Add entry length byte
917B STA wksp_entry_size_base,y ; Store sector count in workspace
917E DEX ; Next length byte
917F BPL copy_locked_name_loop ; Loop for 3 bytes
9181 LDY #&18 ; Y=&18: get start sector
9183 LDX #2 ; X=2: copy 3 sector address bytes
9185 .check_locked_attr_loop←1← 918C BPL
LDA (zp_entry_ptr_lo),y ; Copy start sector to workspace
9187 STA wksp_object_sector,x ; Store sector address byte
918A DEY ; Next entry byte (decreasing)
918B DEX ; Next workspace byte (decreasing)
918C BPL check_locked_attr_loop ; Loop for 3 bytes
918E LDY #3 ; Y=3: check access byte for dir flag
9190 LDA (zp_entry_ptr_lo),y ; Check access byte for directory
9192 BPL write_dir_and_release ; Not a directory: skip to delete
9194 LDX wksp_saved_drive ; Get saved drive
9197 CPX #&ff ; Saved drive = &FF (not set)?
9199 BEQ file_is_locked ; Not set? Check CSD
919B CPX wksp_current_drive ; Same as current drive?
919E BNE release_entry_space ; No, skip CSD check
91A0 .file_is_locked←1← 9199 BEQ
LDX #2 ; X=2: compare 3 sector bytes
91A2 .copy_entry_name_to_wksp_loop←1← 91AB BPL
LDA wksp_object_sector,x ; Compare sector with CSD sector
91A5 CMP wksp_csd_drive_sector,x ; Compare with CSD sector byte
91A8 BNE release_entry_space ; Mismatch: not the CSD
91AA DEX ; Next byte in CSD comparison
91AB BPL copy_entry_name_to_wksp_loop ; Loop for 3 bytes
91AD JSR reload_fsm_and_dir_then_brk
91B0 EQUB &96 ; Error &96: Cant delete CSD
91B1 EQUS "Can't delete CSD", &00
91C2 .release_entry_space←2← 919E BNE← 91A8 BNE
LDA wksp_current_drive ; Check if it's the library dir
91C5 CMP wksp_lib_sector_hi ; Compare drive with library drive
91C8 BNE update_dir_sequence ; Different: not the library
91CA LDX #2 ; Compare sector with lib sector
91CC .remove_entry_shift_loop←1← 91D5 BPL
LDA wksp_object_sector,x ; Compare sector with lib sector byte
91CF CMP wksp_lib_sector,x ; Compare with library sector byte
91D2 BNE update_dir_sequence ; Mismatch: not the library dir
91D4 DEX ; Next byte in library comparison
91D5 BPL remove_entry_shift_loop ; Loop for 3 bytes
91D7 JSR reload_fsm_and_dir_then_brk
91DA EQUB &97 ; Error &97: Cant delete library
91DB EQUS "Can't delete Library", &00
91F0 .update_dir_sequence←2← 91C8 BNE← 91D2 BNE
LDA wksp_current_drive ; Check if it's the previous dir
91F3 CMP wksp_prev_dir_sector_hi ; Compare drive with prev dir drive
91F6 BNE write_dir_and_release ; Different: skip prev dir reset
91F8 LDX #2 ; Compare sector with prev dir sector
91FA .copy_entry_up_loop←1← 9203 BPL
LDA wksp_object_sector,x ; Get object sector byte
91FD CMP wksp_prev_dir_sector,x ; Compare with prev dir sector
9200 BNE write_dir_and_release ; Different: skip
9202 DEX ; Next byte in prev dir comparison
9203 BPL copy_entry_up_loop ; Loop for 3 bytes
9205 LDA #2 ; Reset previous dir to root (sector 2)
9207 STA wksp_prev_dir_sector ; Reset prev dir to root (sector 2)
920A LDA #0 ; A=0: clear high sector bytes
920C STA wksp_prev_dir_sector_lo ; Clear prev dir mid byte
920F STA wksp_prev_dir_sector_mid ; Clear prev dir high byte
9212 .write_dir_and_release←4← 9192 BPL← 91F6 BNE← 9200 BNE← A67F JSR
LDY #4 ; Remove entry from directory
9214 LDA (zp_entry_ptr_lo),y ; Y=4: check lock bit
9216 BMI check_csd_deleted ; Bit 7 set: directory, skip lock chk
9218 JSR search_dir_with_wildcards ; Check file is not locked
921B .check_csd_deleted←1← 9216 BMI
LDY #&1a ; Y=&1A: offset to next entry
921D LDX #0 ; X=0: for indirect store via (&B6,X)
921F .check_lib_deleted←2← 922D BNE← 9233 BNE
LDA (zp_entry_ptr_lo),y ; Copy next entry over this one
9221 STA (zp_entry_ptr_lo,x) ; Store in current position
9223 INC zp_entry_ptr_lo ; Advance pointer
9225 BNE check_prev_dir_deleted ; No page crossing
9227 INC zp_entry_ptr_hi ; Increment pointer high byte
9229 .check_prev_dir_deleted←1← 9225 BNE
LDA zp_entry_ptr_lo ; Check if past end of entries
922B CMP #&bb ; Low byte = &BB? (dir footer boundary)
922D BNE check_lib_deleted ; Low byte not at boundary, continue
922F LDA zp_entry_ptr_hi ; Check if past end of entries (&16xx)
9231 CMP #&16 ; High byte should be &16
9233 BNE check_lib_deleted ; Not past end: continue copying
9235 JSR release_disc_space ; Release the file's disc space
9238 JSR write_dir_and_validate ; Write modified directory to disc
923B JMP get_object_type_result ; Save workspace and return

OSFILE handler

Handle OSFILE calls for whole-file operations: load, save, read/write catalogue info, delete, create.

923E .osfile_handler
STX zp_osfile_ptr_lo ; Save control block address low
9240 STY zp_osfile_ptr_hi ; Save control block address high
9242 TAY ; Transfer function code to Y
9243 LDX #0 ; Clear current channel
9245 STX wksp_cur_channel ; Clear current channel
9248 ASL ; A = function * 2 (table index)
9249 TAX ; Transfer A*2 to X
924A INX ; X = A*2 + 1 (skip table base)
924B INX ; X = A*2 + 2 (dispatch table offset)
924C BMI return_19 ; Function < 0? Invalid
924E CPX #&12 ; Function >= 8? Invalid
9250 BCS return_19 ; Function >= 8: unsupported
9252 LDA osfile_dispatch_hi,x ; Push dispatch address high byte
9255 PHA ; Push dispatch address high
9256 LDA osfile_dispatch_lo,x ; Push dispatch address low byte
9259 PHA ; Push dispatch address low
925A TYA ; Restore function code to A
925B PHA ; Save function code on stack
925C LDY #0 ; Y=0: read filename pointer from block
925E LDA (zp_osfile_ptr_lo),y ; Filename address low byte
9260 STA zp_text_ptr_lo ; Store in (&B4)
9262 INY
9263 LDA (zp_osfile_ptr_lo),y ; Filename address high byte
9265 STA zp_text_ptr_hi ; Store in (&B5)
9267 PLA ; Restore function code
9268 .return_19←2← 924C BMI← 9250 BCS
RTS ; RTS-dispatch to function handler

OSFILE dispatch table

RTS-trick dispatch table for OSFILE functions 0-7. Low bytes at &9269, high bytes at &926A, interleaved as pairs. Functions: 0=save, 1=write cat info, 2=write load addr, 3=write exec addr, 4=write attrs, 5=read cat info, 6=delete, 7=create.

9269 .osfile_dispatch_lo←1← 9256 LDA
EQUB <(osfile_save_check_existing-1) ; A=0 lo-1: OSFILE save
926A .osfile_dispatch_hi←1← 9252 LDA
EQUB >(osfile_save_check_existing-1) ; A=0 hi-1: OSFILE save
926B EQUW osfile_load_handler-1
926D EQUW osfile_write_load_addr-1
926F EQUW osfile_write_load_addr-1
9271 EQUW osfile_write_load_addr-1
9273 EQUW osfile_delete_handler-1
9275 EQUW osfile_save_handler-1
9277 EQUW search_and_delete_entry-1
9279 EQUW osfile_read_cat_info-1

Set up pointer to *HELP parameter format string

Point (zp_entry_ptr) to a pathname format string in ROM and prepare to print up to 12 characters.

On EntryAindex into tbl_help_param_ptrs
On ExitAcorrupted
Xzero
Ycorrupted
927B .setup_help_param_ptr←2← 9E35 JSR← 9E3B JSR
TAX ; Transfer index to X
927C LDA #&9f ; Set up (&B6) to point to pathname
927E STA zp_entry_ptr_hi ; Set pointer high byte
9280 LDA tbl_help_param_ptrs,x ; Get pathname format byte
9283 STA zp_entry_ptr_lo ; Store as pointer low byte
9285 LDX #&0c ; X=&0C: max 12 characters
fall through ↓

Print padded entry name from (&B6)

Print up to X characters of the entry name at (&B6). Each byte has bit 7 stripped with AND #&7F before use. Any character below space (&20) — typically CR (&0D) in unused name positions — ends the name; remaining columns are padded with spaces to produce fixed-width output.

On EntryXmaximum number of characters to print
On ExitAcorrupted
Xzero
Ycorrupted
9287 .print_padded_name←6← 92E0 JSR← 9337 JSR← 9366 JSR← 938C JSR← 93A3 JSR← 93BD JSR
LDY #0 ; Y=0: start of entry name
9289 .print_name_char_loop←1← 9296 BNE
LDA (zp_entry_ptr_lo),y ; Get character from entry
928B AND #&7f ; Strip bit 7 (access bit)
928D CMP #&20 ; Is it a printable character?
928F BCC pad_with_spaces ; No, pad rest with spaces
9291 JSR print_via_osasci ; Print character via OSASCI
9294 INY ; Next character
9295 DEX ; Decrement column counter
9296 BNE print_name_char_loop ; Loop for remaining columns
9298 RTS ; Return
9299 .pad_with_spaces←2← 928F BCC← 929D BNE
JSR print_space ; Print space padding
929C DEX ; Pad with spaces
929D BNE pad_with_spaces ; Loop for remaining columns
929F RTS ; Return

Print bit-7-terminated inline string

Pop the return address from the stack, print the inline string that follows the JSR instruction. Characters are printed via OSASCI until a byte with bit 7 set is found (the last character, printed with bit 7 stripped). Pushes the address past the string so RTS continues after it.

On ExitAcorrupted
Xpreserved
Ycorrupted
92A0 .print_inline_string←19← 933A JSR← 9345 JSR← 9369 JSR← 9379 JSR← 938F JSR← 93A6 JSR← 93C0 JSR← 99FD JSR← 9B78 JSR← 9DA7 JSR← 9DC9 JSR← 9E12 JSR← A021 JSR← A041 JSR← A04A JSR← A077 JSR← A0A0 JSR← A1D8 JSR← A247 JSR
PLA ; Pop return addr low (inline data)
92A1 STA zp_entry_ptr_lo ; Store as string pointer low
92A3 PLA ; Pop return addr high
92A4 STA zp_entry_ptr_hi ; Store as string pointer high
92A6 LDY #1 ; Y=1: start past JSR return addr
92A8 .print_char_loop←1← 92B0 BNE
LDA (zp_entry_ptr_lo),y ; Get next string character
92AA BMI last_char_reached ; Bit 7 set: last character
92AC JSR print_via_osasci ; Print character via OSASCI
92AF INY ; Next character
92B0 BNE print_char_loop ; Loop for more characters
92B2 .last_char_reached←1← 92AA BMI
AND #&7f ; Strip bit 7 from last char
92B4 JSR print_via_osasci ; Print last character
92B7 TYA ; Y = string length + 1
92B8 CLC ; Clear carry for address calc
92B9 ADC zp_entry_ptr_lo ; Add string length to pointer
92BB TAY ; Transfer low result to Y
92BC LDA #0 ; A=0: add carry only
92BE ADC zp_entry_ptr_hi ; Add carry to high byte
92C0 PHA ; Push updated return addr high
92C1 TYA ; Transfer low to A
92C2 PHA ; Push updated return addr low
92C3 RTS ; Return (past inline string)

Print character preserving registers

Write A via OSASCI while preserving A, X, and (&B6). Used during catalogue printing.

On EntryAcharacter to print via OSASCI
On ExitApreserved
Xpreserved
Ycorrupted
92C4 .print_via_osasci←3← 9291 JSR← 92AC JSR← 92B4 JSR
PHA ; Save character to print
92C5 TXA ; Save X
92C6 PHA ; Save X on stack
92C7 LDA zp_entry_ptr_lo ; Save (&B6) low
92C9 PHA ; Push on stack
92CA LDA zp_entry_ptr_hi ; Save (&B6) high
92CC PHA ; Push on stack
92CD TSX ; Get stack pointer
92CE LDA brk_error_block_4,x ; Get character from stack+4
92D1 JSR osasci
92D4 PLA ; Restore (&B6) high
92D5 STA zp_entry_ptr_hi ; Store back
92D7 PLA ; Restore (&B6) low
92D8 STA zp_entry_ptr_lo ; Store back
92DA PLA ; Restore X
92DB TAX ; Transfer to X
92DC PLA ; Restore character (was printed)
92DD RTS ; Return

Print entry name and access string

Print the 10-character padded filename from (&B6) via print_padded_name, followed by the access attribute string. Scans name bytes 4 down to 0, testing bit 7 of each to determine which attributes are set. Uses Y as the index into both the entry and tbl_access_chars ("RWLDE"), so byte 0 maps to 'R', byte 1 to 'W', etc. X counts set attributes (starting at 3) to pad unset ones with spaces, producing a fixed-width "DLW " or " WR" style field. Followed by the sequence number in parentheses.

92DE .print_entry_name_and_access←2← 93E2 JSR← 9501 JSR
LDX #&0a ; X=&0A: print up to 10 name chars
92E0 JSR print_padded_name ; Print name characters
92E3 JSR print_space ; Print space after name
92E6 LDY #4 ; Y=4: scan bytes 4,3,2,1,0 (EDLWR)
92E8 LDX #3 ; X=3: space-pad counter for columns
92EA .print_entry_char_loop←1← 92F7 BPL
LDA (zp_entry_ptr_lo),y ; Get name byte Y from entry
92EC ROL ; Bit 7 (attribute flag) into carry
92ED BCC print_access_space ; C=0: attribute not set, skip
92EF LDA tbl_access_chars,y ; C=1: get letter from 'RWLDE'[Y]
92F2 JSR oswrch
92F5 DEX ; X-- (tracks set attribute count)
92F6 .print_access_space←1← 92ED BCC
DEY ; Y-- (next entry byte, towards 0)
92F7 BPL print_entry_char_loop ; Loop for 5 bytes (E,D,L,W,R)
92F9 .print_access_chars_loop←1← 92FF JMP
DEX ; X--: pad remaining columns
92FA BMI print_access_done ; All columns done
92FC JSR print_space ; Print space for unset attribute
92FF JMP print_access_chars_loop ; Continue padding loop
9302 .print_access_done←1← 92FA BMI
LDA #&28 ; Print '(' before sequence number
9304 JSR oswrch
9307 LDY #&19 ; Y=&19: offset to sequence number
9309 LDA (zp_entry_ptr_lo),y ; Get sequence number byte
930B JSR print_hex_byte ; Print as 2 hex digits
930E LDA #&29 ; Print ')' after sequence number
9310 JSR oswrch
9313 JMP print_space ; Print space and return

Access attribute character table

Five-character table 'RWLDE' used to look up and display file access attributes. Indexed by attribute bit position.

9316 .tbl_access_chars←2← 92EF LDA← 99A7 CMP
EQUS "RWLDE"

Print a byte as two hex digits

Print the value in A as two hexadecimal ASCII digits via OSWRCH, high nibble first.

On EntryAbyte value to print as hex
931B .print_hex_byte←9← 930B JSR← 9342 JSR← 9376 JSR← 9521 JSR← A071 JSR← A087 JSR← A1C9 JSR← A1CF JSR← A1D5 JSR
PHA ; Save value
931C LSR ; Shift high nibble to low
931D LSR ; Second shift
931E LSR ; Third shift
931F LSR ; Fourth shift
9320 JSR print_hex_nibble ; Print high nibble as hex char
9323 PLA ; Restore value for low nibble
9324 .print_hex_nibble←1← 9320 JSR
JSR hex_digit
9327 JMP oswrch

Verify directory and print catalogue header

Verify directory integrity then print directory title, sequence number, drive number, and name as the header for a catalogue listing.

932A .verify_dir_and_list←2← 93D4 JSR← 9436 JSR
JSR verify_dir_integrity
932D LDA #&d9 ; Point to dir title at &16D9
932F STA zp_entry_ptr_lo ; Store low byte
9331 LDA #&16 ; Page &16
9333 STA zp_entry_ptr_hi ; Store high byte
9335 LDX #&13 ; X=&13: print 19 chars of title
9337 JSR print_padded_name ; Print title characters
933A JSR print_inline_string
933D EQUS " "
933E TAY ; Transfer Y to A for display
933F LDA dir_master_sequence ; Get directory sequence number
9342 JSR print_hex_byte ; Print as 2 hex digits
9345 JSR print_inline_string
9348 EQUS ")", &0D, "Drive"
934F TSX ; Get stack pointer
9350 LDA wksp_current_drive ; Get current drive number
9353 ASL ; Shift drive bits into position
9354 ROL ; Second shift
9355 ROL ; Third shift
9356 ROL ; Fourth shift
9357 ADC #&30 ; Convert to ASCII digit
9359 JSR oswrch
935C LDA #&5f ; Point to CSD path string in ROM
935E STA zp_entry_ptr_lo ; Store pointer low
9360 LDA #&9a ; Page &9A
9362 STA zp_entry_ptr_hi ; Store pointer high
9364 LDX #&0d ; X=&0D: print CSD path
9366 JSR print_padded_name ; Print path characters
9369 JSR print_inline_string
936C EQUS "Option"
9372 EQUB &A0 ; ' ' + bit 7: end of inline string
9373 LDA fsm_s1_boot_option ; Get boot option from FSM
9376 JSR print_hex_byte ; Print boot option as two hex digits
9379 JSR print_inline_string
937C EQUS " "
937D TAY ; Transfer boot option to Y for lookup
937E LDX fsm_s1_boot_option ; Get boot option again for table index
9381 LDA l941f,x ; Look up option name string address
9384 STA zp_entry_ptr_lo ; Set entry ptr to option name string
9386 LDA #&94
9388 STA zp_entry_ptr_hi
938A LDX #4 ; X=4: print 4-char option name
938C JSR print_padded_name ; Print boot option name (Off/Load/Run/Exec)
938F JSR print_inline_string
9392 EQUS ")", &0D, "Dir." ; ")" + CR + "Dir." + space: option close + dir label
9398 EQUB &A0 ; ' ' + bit 7: end of inline string
9399 LDA #0 ; A=&00: CSD name low (wksp_csd_name)
939B STA zp_entry_ptr_lo ; Store pointer low byte
939D LDA #&11 ; A=&11: CSD name high (&1100)
939F STA zp_entry_ptr_hi ; Store pointer high byte
93A1 LDX #&0a ; X=&0A: print 10-char directory name
93A3 JSR print_padded_name ; Print CSD directory name
93A6 JSR print_inline_string
93A9 EQUS " Lib."
93B2 EQUB &A0 ; ' ' + bit 7: end of inline string
93B3 LDA #&0a ; A=&0A: library name low (wksp_lib_name)
93B5 STA zp_entry_ptr_lo ; Store pointer low byte
93B7 LDA #&11 ; A=&11: library name high (&110A)
93B9 STA zp_entry_ptr_hi ; Store pointer high byte
93BB LDX #&0a ; X=&0A: print 10-char library name
93BD JSR print_padded_name ; Print library directory name
93C0 JSR print_inline_string
93C3 EQUS &0D ; CR: end of library name line
93C4 EQUB &8D ; CR + bit 7: blank line after header
fall through ↓

Point (&B6) to first directory entry

Set zp_entry_ptr (&B6) to &1205, the address of the first 26-byte directory entry in the directory buffer. Directory entries are stored in case-insensitive ascending alphabetical order starting at this address. A zero first byte marks the end of the entry list. Maximum 47 entries (47 x 26 = 1222 bytes from &1205 to &16B0).

Despite its name, this subroutine does not print anything. The label reflects its position in the code, immediately following the catalogue header printing code which falls through to it.

93C5 .print_catalogue_header←3← 87EA JSR← 97BE JSR← 98DA JSR
LDA #5 ; Point to first dir entry at &1205
93C7 STA zp_entry_ptr_lo ; Store pointer low = &05
93C9 LDA #&12 ; Page &12
93CB STA zp_entry_ptr_hi ; Store pointer high
93CD RTS ; Return
93CE .print_catalogue_entries
JSR skip_spaces
93D1 JSR parse_dir_argument
93D4 .print_cat_header_and_entries←1← A485 JSR
JSR verify_dir_and_list ; Load and validate directory
93D7 LDA #4 ; Columns per line = 4
93D9 STA wksp_csd_sector_temp ; Store column counter
93DC .print_cat_entry_loop←2← 93FF BCC← 9403 BCS
LDY #0 ; Y=0: check first byte of entry
93DE LDA (zp_entry_ptr_lo),y ; Get first byte
93E0 BEQ print_cat_pair_second ; Zero: end of entries
93E2 JSR print_entry_name_and_access ; Print entry name with access
93E5 DEC wksp_csd_sector_temp ; Decrement column counter
93E8 BNE advance_cat_entry ; Not zero: same line
93EA LDA #4 ; Reset column counter to 4
93EC STA wksp_csd_sector_temp ; Store column counter
93EF JSR osnewl
93F2 JMP print_cat_pair ; Jump to newline print
93F5 .advance_cat_entry←1← 93E8 BNE
JSR print_space ; Print space between entries
93F8 .print_cat_pair←1← 93F2 JMP
CLC ; Clear carry for pointer advance
93F9 LDA zp_entry_ptr_lo ; Get entry pointer low
93FB ADC #&1a ; Add &1A (26 bytes per entry)
93FD STA zp_entry_ptr_lo ; Store updated pointer
93FF BCC print_cat_entry_loop ; No page crossing: continue
9401 INC zp_entry_ptr_hi ; Increment page
9403 BCS print_cat_entry_loop
9405 .print_cat_pair_second←1← 93E0 BEQ
LDA wksp_csd_sector_temp ; Get column counter
9408 CMP #4 ; Full line (4 columns)?
940A BEQ print_cat_done ; Yes: no partial line to finish
940C LDA #osbyte_read_text_cursor_pos ; OSBYTE &86: read cursor position
940E JSR osbyte ; Read input cursor position (Sets X=POS and Y=VPOS)
9411 TXA ; X is the horizontal text position ('POS') / Y is the vertical text position ('VPOS')
9412 BNE print_cat_newline ; X non-zero: cursor not at col 0
9414 LDA #&0b ; At col 0: VDU 11 (cursor up)
9416 JSR oswrch
9419 .print_cat_newline←1← 9412 BNE
JSR osnewl
941C .print_cat_done←2← 940A BEQ← 943D BEQ
JMP save_wksp_and_return ; Save workspace and return
941F .l941f←1← 9381 LDA
EQUS "#'+/Off LoadRun Exec"

*EX command handler

Display a full catalogue of the current or specified directory, showing filename, attributes, load and execution addresses, length, and start sector for each entry.

9433 .star_ex
JSR parse_dir_argument ; Parse directory argument
9436 .load_dir_and_list_entries←1← A491 JSR
JSR verify_dir_and_list ; Load and validate the directory
9439 .print_next_entry_loop←2← 9449 BCC← 944D BCS
LDY #0 ; Y=0: check first byte of entry
943B LDA (zp_entry_ptr_lo),y ; Get first byte of entry name
943D BEQ print_cat_done ; Zero: end of entries, done
943F JSR print_entry_info ; Print this entry's full info
9442 CLC ; Clear carry for addition
9443 LDA zp_entry_ptr_lo ; Advance (&B6) by 26 to next entry
9445 ADC #&1a ; Add &1A (26 bytes per entry)
9447 STA zp_entry_ptr_lo ; Store low byte
9449 BCC print_next_entry_loop ; No page crossing, continue loop
944B INC zp_entry_ptr_hi ; Page crossed: increment high byte
944D BCS print_next_entry_loop ; Continue loop (always branches)
fall through ↓

Check for ^ (parent) or @ (current) directory

Check if the first character of the argument is ^ (parent directory) or @ (current directory). Sets (&B6) to point to the appropriate directory footer area.

On ExitAcorrupted (Z set if ^ or @ matched)
Xcorrupted
Ycorrupted
944F .check_special_dir_char←3← 88D2 JSR← 8CDC JSR← 9492 JSR
LDY #0 ; Y=0: get first argument char
9451 LDA (zp_text_ptr_lo),y ; Get first argument char
9453 AND #&7f ; Strip bit 7
9455 CMP #&5e ; Is it '^' (parent directory)?
9457 BNE check_at_sign ; No, check for '@'
9459 LDA #&c0 ; '^': point to dir parent sector
945B STA zp_entry_ptr_lo ; Set (&B6) low to &C0
945D LDA #&16 ; Set (&B6) high to &16 (dir footer)
945F STA zp_entry_ptr_hi ; Store high byte
9461 BNE set_matched_flag ; Set Z flag (matched)
9463 .check_at_sign←1← 9457 BNE
CMP #&40 ; Is it '@' (current directory)?
9465 BNE return_20 ; No, return Z clear (no match)
9467 LDA #&fe ; '@': point to workspace at &10FE
9469 STA zp_entry_ptr_lo ; Set (&B6) low to &FE
946B LDA #&10 ; Set (&B6) high to &10 (workspace)
946D STA zp_entry_ptr_hi ; Store high byte
946F .set_matched_flag←1← 9461 BNE
TYA ; Transfer Y=0 to A, setting Z flag
9470 .return_20←2← 9465 BNE← 947D BNE
RTS ; Return

Parse optional directory path argument

If a directory argument is given, parse the path and load the target directory. If no argument, use the current directory (checking it's initialised first).

Used by *EX, *CAT, *CDIR, and *DIR.

9471 .parse_dir_argument←2← 93D1 JSR← 9433 JSR
LDY #0 ; Y=0: check for argument
9473 LDA (zp_text_ptr_lo),y ; Get first char of argument
9475 CMP #&21 ; Is it a printable char?
9477 BCS parse_path_and_load ; Yes, parse the path
9479 LDX wksp_current_drive ; No arg: check drive is initialised
947C INX ; Drive = &FF (uninitialised)?
947D BNE return_20 ; Drive OK, return
fall through ↓

Parse path and load target directory

Parse a full pathname and load the target directory into the buffer. Handles drive specifiers, root, parent, and current directory references.

947F .parse_path_and_load←8← 9144 JSR← 9477 BCS← 953F JSR← 98C9 JSR← 991A JSR← A444 JSR← A6AB JSR← A86F JSR
JSR full_pathname_parser ; Parse path and load directory
9482 BNE check_special_dir ; Simple path or '^'/'@'?
9484 .search_for_dir_entry←1← 948D BEQ
LDY #3 ; Y=3: check entry access byte
9486 LDA (zp_entry_ptr_lo),y ; Get access/attribute byte
9488 BMI prepare_dir_read ; Bit 7 set: is a directory, found it
948A JSR advance_dir_entry_ptr ; Not a directory, search deeper
948D BEQ search_for_dir_entry ; Found directory: loop complete
948F .path_not_found←1← 9495 BNE
JMP bad_parms_error ; Not found: raise error
9492 .check_special_dir←1← 9482 BNE
JSR check_special_dir_char ; Check for '^' or '@' specifier
9495 BNE path_not_found ; Continue to next path component
9497 .prepare_dir_read←1← 9488 BMI
LDY wksp_alt_sector_hi ; Set up workspace for directory read
949A INY ; Increment: was &FF, now 0
949B BNE copy_disc_op_template ; Non-zero: skip CSD copy
949D LDY #2 ; Copy CSD sector to workspace
949F .copy_csd_sector_to_wksp←1← 94A6 BPL
LDA wksp_csd_sector_lo,y ; Get CSD sector byte
94A2 STA wksp_csd_drive_sector,y ; Copy to CSD workspace
94A5 DEY ; Next byte
94A6 BPL copy_csd_sector_to_wksp ; Loop for 3 bytes
94A8 .copy_disc_op_template←1← 949B BNE
LDX #&0a ; X=&0A: copy 11-byte disc op block
94AA .copy_template_loop←1← 94B1 BPL
LDA disc_op_tpl_read_dir,x ; Copy template control block
94AD STA wksp_disc_op_result,x ; Copy template byte to workspace
94B0 DEX ; Next byte
94B1 BPL copy_template_loop ; Loop for 11 bytes
94B3 LDX #2 ; X=2: copy 3 sector address bytes
94B5 LDY #&16 ; Y=&16: offset of start sector in entry
94B7 .copy_entry_sector_loop←1← 94C1 BPL
LDA (zp_entry_ptr_lo),y ; Get sector byte from directory entry
94B9 STA wksp_disc_op_sector,x ; Store in disc op control block
94BC STA wksp_alt_csd_sector,y ; Also store in workspace
94BF INY ; Next sector address byte
94C0 DEX ; Decrement counter
94C1 BPL copy_entry_sector_loop ; Loop for 3 bytes
94C3 LDA zp_entry_ptr_hi ; Check if this is an *INFO call
94C5 CMP #&94 ; zp_b7 = &94 means *INFO context
94C7 BEQ return_21 ; Yes, return without reading dir
94C9 JMP exec_disc_op_from_wksp ; Execute disc read to load directory

Dummy directory entry for root directory '$'

A synthetic 26-byte directory entry representing the root directory. Used when '$' is referenced directly to avoid loading the root directory just to read its metadata. The entry has name '$' (padded with CR), access R/L/D (read, locked, directory), load/exec &00000000, length &00000500 (5 sectors), start sector 2.

94CC .dummy_root_dir_entry
EQUB &A4 ; '$' + bit 7 (R access): filename char 0
94CD EQUB &0D ; CR: filename padding char 1
94CE EQUB &8D ; CR + bit 7 (L access): filename char 2
94CF EQUB &8D ; CR + bit 7 (D=directory): filename char 3
94D0 EQUB &0D, &0D, &0D, &0D, &0D, ; CR padding: filename chars &0D ; 4-9
94D6 EQUB &00, &00, &00, &00 ; Load address: &00000000
94DA EQUB &00, &00, &00, &00 ; Exec address: &00000000
94DE EQUB &00 ; Length low: &00
94DF EQUB &05 ; Length byte 1: &05 (5 sectors = &500 bytes)
94E0 EQUB &00, &00 ; Length bytes 2-3: &0000
94E2 EQUB &02 ; Start sector low: &02 (root directory)
94E3 EQUB &00, &00 ; Start sector mid/high: &0000
94E5 EQUB &00 ; Sequence number: &00
94E6 EQUB &00 ; Padding: &00

*INFO command handler

Display catalogue information for a single file, with the same format as *EX but for one file only. Supports wildcards.

94E7 .star_info←1← 99F4 JSR
JSR find_first_matching_entry ; Find first matching file
94EA BEQ print_info_loop ; Found? Print its info
94EC JMP not_found_error ; Not found: report error
94EF .print_info_loop←2← 94EA BEQ← 94F5 BEQ
JSR print_entry_info ; Print this entry's catalogue info
94F2 JSR advance_dir_entry_ptr ; Find next matching file
94F5 BEQ print_info_loop ; More matches? Continue loop
94F7 JMP save_wksp_and_return ; No more matches: save and return

Display file info if *OPT1 verbose

Check *OPT1 verbose flag. If set, print full catalogue info for the current directory entry.

94FA .conditional_info_display←2← 8C62 JSR← 99BB JSR
LDA zp_adfs_flags ; Check *OPT1 setting
94FC AND #4 ; Bit 2 set: verbose mode on
94FE BNE print_entry_info ; Yes, display the info
9500 .return_21←1← 94C7 BEQ
RTS ; Return

Print full catalogue info for one directory entry

Print a directory entry in *INFO/*EX format: filename access/ loadaddr execaddr length sector [D]

Entry at (&B6) is a 26-byte directory entry. Checks the E (execute-only) attribute and suppresses detail if set. Uses 3-byte addresses for small files, 4-byte for large.

9501 .print_entry_info←3← 943F JSR← 94EF JSR← 94FE BNE
JSR print_entry_name_and_access ; Print filename and access string
9504 JSR oswrch ; Print space after access string
9507 LDY #4 ; Y=4: check first access nibble byte
9509 LDA (zp_entry_ptr_lo),y ; Get access/attribute byte
950B BMI print_newline_return ; Bit 7 (E attribute): suppress info
950D DEY ; Y=3: get access byte for format
950E LDA (zp_entry_ptr_lo),y ; Get access byte
9510 ROL ; Shift bit 7 into C (directory flag)
9511 LDX #&0a ; X=&0A: start offset (3-byte addrs)
9513 LDY #&0d ; Y=&0D: end offset for 3-byte format
9515 BCC print_entry_field_loop ; C=0: 3-byte addresses
9517 LDX #&17 ; X=&17: start offset (4-byte addrs)
9519 LDY #&18 ; Y=&18: end offset for 4-byte format
951B .print_entry_field_loop←2← 9515 BCC← 953A BNE
CPX #&16 ; Skip sector field boundary?
951D BEQ check_field_boundary ; Yes, skip the sector field gap
951F LDA (zp_entry_ptr_lo),y ; Get byte from entry
9521 JSR print_hex_byte ; Print as 2 hex digits
9524 .check_field_boundary←1← 951D BEQ
TXA ; Check if at field boundary
9525 AND #3 ; Field boundary every 4 bytes (X&3=1)
9527 CMP #1 ; X mod 4 == 1? Field boundary
9529 BNE next_entry_byte ; Not at boundary, continue
952B JSR print_space ; Print two spaces between fields
952E JSR print_space ; Print second padding space
9531 TXA ; Skip ahead to next field
9532 CLC ; Clear carry for addition
9533 ADC #5 ; Advance Y by 5
9535 TAY ; Transfer new Y offset
9536 .next_entry_byte←1← 9529 BNE
DEY ; Next byte backwards
9537 INX ; Advance field index
9538 CPX #&1a ; Past end of entry (X=&1A)?
953A BNE print_entry_field_loop ; No, continue printing
953C .print_newline_return←1← 950B BMI
JMP osnewl ; Print newline at end of entry

*DIR command handler

Change the currently selected directory. With no argument, selects the root directory of the current drive.

953F .star_dir←1← A176 JSR
JSR parse_path_and_load ; Parse path and load target dir
9542 LDY #9 ; Y=9: copy 10-byte directory name
9544 .copy_dir_name_loop←1← 954B BPL
LDA dir_name,y ; Get name byte from dir buffer
9547 STA wksp_csd_name,y ; Store as CSD name
954A DEY ; Next byte in name copy
954B BPL copy_dir_name_loop ; Loop for all 10 bytes
954D LDA wksp_saved_drive ; Get saved drive number
9550 CMP #&ff ; Is it &FF (not set)?
9552 BNE store_csd_drive ; No, use saved drive
9554 LDA wksp_current_drive ; Use current drive instead
9557 .store_csd_drive←1← 9552 BNE
STA wksp_prev_dir_sector_hi ; Store as new CSD drive
955A LDY #2 ; Y=2: copy 3-byte sector address
955C .copy_csd_sector_loop←1← 9563 BPL
LDA wksp_csd_drive_sector,y ; Get CSD sector address byte
955F STA wksp_prev_dir_sector,y ; Save as previous dir sector (*BACK)
9562 DEY ; Next byte in sector copy
9563 BPL copy_csd_sector_loop ; Loop for 3 bytes
9565 LDA #&ff ; A=&FF: mark as unset
9567 STA wksp_alt_sector_hi ; Clear alternative workspace ptr
956A STA wksp_saved_drive ; Clear saved drive
956D JMP save_wksp_and_return ; Save workspace and return

*CDIR command handler

Create a new directory. Allocates 5 contiguous sectors on disc and initialises the directory structure with the Hugo identifier, title, and parent pointer.

9570 .star_cdir
LDA #&ff ; OSARGS &FF: ensure FS is selected
9572 LDY #0 ; Y=0: for OSARGS
9574 JSR osargs_handler
9577 LDX #&0f ; X=&0F: copy 16-byte template block
9579 .check_dir_exists_loop←1← 9580 BPL
LDA osfile_tpl_cdir,x ; Copy OSFILE template to workspace
957C STA wksp_osfile_load_addr,x ; Copy template to workspace
957F DEX ; Next byte
9580 BPL check_dir_exists_loop ; Loop for 16 bytes
9582 LDA zp_text_ptr_lo ; Store filename pointer in OSFILE blk
9584 STA wksp_osfile_block ; Store filename in OSFILE block
9587 LDA zp_text_ptr_hi ; Get filename high byte
9589 STA wksp_osfile_block_1 ; Store in OSFILE block
958C LDA #&40 ; Point (&B8) to workspace OSFILE blk
958E STA zp_osfile_ptr_lo ; Store block pointer low
9590 LDA #&10 ; Block page = &10
9592 STA zp_osfile_ptr_hi ; Store block pointer high
9594 JSR copy_addrs_and_find_empty_entry ; Search for existing entry
9597 LDY #9 ; Y=9: check if entry has size > 0
9599 LDA wksp_object_size ; Check size bytes for non-zero
959C ORA wksp_object_size_mid ; OR size mid byte
959F ORA wksp_object_size_hi ; OR size high byte
95A2 BEQ cdir_name_validated ; Size is 0: entry slot is free
95A4 .already_exists_error2←2← 8D0D JMP← A5E8 JMP
JSR reload_fsm_and_dir_then_brk
95A7 EQUB &C4 ; Error &C4: Already exists
95A8 EQUS "Already exists", &00
95B7 .cdir_name_validated←2← 95A2 BEQ← 95C8 BPL
LDA (zp_text_ptr_lo),y ; Copy filename to dir entry, max 10
95B9 AND #&7f ; Strip bit 7
95BB CMP #&22 ; Quote terminates name
95BD BEQ check_root_or_special ; Quote: pad with CR
95BF CMP #&21 ; Control char terminates name
95C1 BCS not_root_or_special ; Printable: use as-is
95C3 .check_root_or_special←1← 95BD BEQ
LDA #&0d ; Pad with CR
95C5 .not_root_or_special←1← 95C1 BCS
STA (zp_entry_ptr_lo),y ; Store character in entry
95C7 DEY ; Next name byte (decreasing)
95C8 BPL cdir_name_validated ; Loop for 10 bytes
95CA JSR write_entry_sector_info ; Allocate disc space for new dir
95CD LDY #3 ; Y=3: set directory attribute
95CF .copy_cdir_sector_loop←1← 95D8 BNE
LDA (zp_entry_ptr_lo),y ; Get entry byte
95D1 ORA #&80 ; Set bit 7 (D attribute on all)
95D3 STA (zp_entry_ptr_lo),y ; Store back
95D5 DEY ; Next byte down
95D6 CPY #1 ; Past byte 1? (byte 0 is special)
95D8 BNE copy_cdir_sector_loop ; No: continue setting attributes
95DA DEY ; Y=0: set D attribute on byte 0
95DB LDA (zp_entry_ptr_lo),y ; Get name byte 0
95DD ORA #&80 ; Set bit 7 (D attribute)
95DF STA (zp_entry_ptr_lo),y ; Store back
95E1 LDA #0 ; A=0: zero-fill all 5 dir pages
95E3 TAX
95E4 TAY
95E5 .copy_dir_template_loop←1← 95F5 BNE
STA ra_buffer_2,x ; Zero page 2 (&1800)
95E8 STA ra_buffer_1,x ; Zero page 1 (&1700)
95EB STA ra_buffer_3,x ; Zero page 3 (&1900)
95EE STA ra_buffer_4,x ; Zero page 4 (&1A00)
95F1 STA ra_buffer_5,x ; Zero page 5 (&1B00)
95F4 INX ; Next byte
95F5 BNE copy_dir_template_loop ; Loop for 256 bytes per page
95F7 LDX #4 ; X=4: copy 5 bytes (seq+Hugo)
95F9 .init_dir_identity_loop←1← 9609 BPL
LDA str_hugo,x ; Get Hugo identifier byte from ROM
95FC STA ra_buffer_1,x ; Store in dir header (&1700)
95FF STA dir2_master_sequence,x ; Store in dir footer (&1BFA)
9602 LDA wksp_csd_sector_lo,x ; Get parent dir sector byte
9605 STA dir2_parent_sector,x ; Store in footer parent pointer
9608 DEX ; Next byte
9609 BPL init_dir_identity_loop ; Loop for 5 bytes
960B LDX #0 ; X=0: copy name as title and name
960D .zero_dir_entries_loop←1← 9625 BNE
LDA (zp_text_ptr_lo),y ; Get name character from argument
960F AND #&7f ; Strip bit 7
9611 CMP #&22 ; Is it double-quote?
9613 BEQ write_new_dir_to_disc ; Yes: pad with CR
9615 CMP #&21 ; Is it printable (> '!')?
9617 BCS set_dir_parent_sector ; Yes: use character as-is
9619 .write_new_dir_to_disc←1← 9613 BEQ
LDA #&0d ; Non-printable: use CR padding
961B .set_dir_parent_sector←1← 9617 BCS
STA dir2_title,x ; Store in directory title
961E STA dir2_name,x ; Store in directory name
9621 INY ; Next argument character
9622 INX ; Next position in title/name
9623 CPX #&0a ; Copied all 10 characters?
9625 BNE zero_dir_entries_loop ; No: continue copying
9627 LDA #&0d ; A=CR: terminate title
9629 STA dir2_title,x ; Store CR after last title char
962C JSR multi_sector_disc_command ; Calculate sectors and write dir
962F JMP search_for_osfile_target ; Write directory and update FSM

OSFILE control block template for *CDIR

16-byte template copied to the OSFILE control block at &1042-&1051 when creating a new directory. Sets the data region to &1700-&1BFF (the 5-page random access buffer area used as scratch space to build the new directory before writing to disc). The &FFFF prefix marks host memory (not Tube).

9632 .osfile_tpl_cdir←1← 9579 LDA
EQUB &00, &00, ; Load address: &00000000 (not used) &00, &00
9636 EQUB &00, &00, ; Exec address: &00000000 (not used) &00, &00
963A EQUB &00 ; Data start low: &00
963B EQUB &17 ; Data start high: &17 (-> &1700 ra_buffer_1)
963C EQUB &FF ; Data start byte 3: &FF (host memory)
963D EQUB &FF ; Data start byte 4: &FF (host memory)
963E EQUB &00 ; Data end low: &00
963F EQUB &1C ; Data end high: &1C (-> &1C00, 5 pages)
9640 EQUB &FF ; Data end byte 3: &FF (host memory)
9641 EQUB &FF ; Data end byte 4: &FF (host memory)
9642 .copy_sectors_between_dirs←1← 98A8 JSR
LDA wksp_saved_drive ; Check if saved drive matches
9645 CMP wksp_current_drive ; Compare with current drive
9648 BEQ read_source_sector ; Same: check CSD sector match
964A CMP #&ff ; Saved = &FF (not set)?
964C BNE advance_sector_ptrs ; Different drive: skip CSD check
964E .read_source_sector←1← 9648 BEQ
LDY #2 ; Y=2: compare 3 sector bytes
9650 .copy_sector_data_loop←1← 9659 BPL
LDA wksp_copy_read_sector,y ; Get old sector address byte
9653 CMP wksp_csd_drive_sector,y ; Compare with CSD sector
9656 BNE advance_sector_ptrs ; Mismatch: not CSD
9658 DEY ; Next byte
9659 BPL copy_sector_data_loop ; Loop for 3 bytes
965B LDY #2 ; CSD matches: update to new sector
965D .write_dest_sector←1← 9664 BPL
LDA wksp_copy_src_sector,y ; Get new sector byte
9660 STA wksp_csd_drive_sector,y ; Store as CSD sector
9663 DEY ; Next byte
9664 BPL write_dest_sector ; Loop for 3 bytes
9666 .advance_sector_ptrs←2← 964C BNE← 9656 BNE
LDA wksp_lib_sector_hi ; Check library directory
9669 CMP wksp_current_drive ; Compare lib drive with current
966C BNE advance_source_sector ; Different drive: skip lib
966E LDY #2 ; Y=2: compare 3 sector bytes
9670 .copy_remaining_loop←1← 9679 BPL
LDA wksp_copy_read_sector,y ; Get old sector address byte
9673 CMP wksp_lib_sector,y ; Compare with lib sector
9676 BNE advance_source_sector ; Mismatch: not library
9678 DEY ; Next byte
9679 BPL copy_remaining_loop ; Loop for 3 bytes
967B LDY #2 ; Lib matches: update to new sector
967D .advance_dest_sector←1← 9684 BPL
LDA wksp_copy_src_sector,y ; Get new sector byte
9680 STA wksp_lib_sector,y ; Store as lib sector
9683 DEY ; Next byte
9684 BPL advance_dest_sector ; Loop for 3 bytes
9686 .advance_source_sector←2← 966C BNE← 9676 BNE
LDA wksp_prev_dir_sector_hi ; Check previous directory
9689 CMP wksp_current_drive ; Compare prev dir drive
968C BNE execute_sector_copy ; Different drive: skip
968E LDY #2 ; Y=2: compare 3 sector bytes
9690 .copy_dir_name_to_entry←1← 9699 BPL
LDA wksp_copy_read_sector,y ; Get old sector byte
9693 CMP wksp_prev_dir_sector,y ; Compare with prev dir sector
9696 BNE execute_sector_copy ; Mismatch: not prev dir
9698 DEY ; Next byte
9699 BPL copy_dir_name_to_entry ; Loop for 3 bytes
969B LDY #2 ; Prev dir matches: update
969D .set_entry_dir_attribute←1← 96A4 BPL
LDA wksp_copy_src_sector,y ; Get new sector byte
96A0 STA wksp_prev_dir_sector,y ; Store as prev dir sector
96A3 DEY ; Next byte
96A4 BPL set_entry_dir_attribute ; Loop for 3 bytes
96A6 .execute_sector_copy←4← 968C BNE← 9696 BNE← A92C JSR← AF84 JSR
LDA zp_adfs_flags ; Check bit 3 (copy in progress?)
96A8 AND #8 ; Bit 3: copy operation flag
96AA BNE check_tube_for_copy ; Set: skip directory write
96AC JSR write_dir_and_validate ; Write directory to disc
96AF JSR flush_all_channels ; Flush OSARGS workspace
96B2 .check_tube_for_copy←1← 96AA BNE
LDA wksp_copy_write_sector_2 ; Check if sectors remain to copy
96B5 ORA wksp_copy_write_sector_1 ; OR with mid byte
96B8 ORA wksp_copy_write_sector ; OR with high byte
96BB BNE read_source_to_buffer ; Non-zero: more to copy
96BD RTS ; All done: return
96BE .read_source_to_buffer←1← 96BB BNE
LDA wksp_copy_write_sector_2 ; Get sector count high
96C1 ORA wksp_copy_write_sector_1 ; OR with mid byte
96C4 BNE write_buffer_to_dest ; Non-zero: more than buffer fits
96C6 LDA wksp_copy_write_sector ; Get sector count low
96C9 CMP wksp_compact_length ; Compare with buffer size
96CC BCC advance_copy_sector ; Fits in buffer: use exact count
96CE .write_buffer_to_dest←1← 96C4 BNE
LDA wksp_compact_length ; Too many: use buffer size
96D1 .advance_copy_sector←1← 96CC BCC
STA wksp_disc_op_sector_count ; Store sector count for this chunk
96D4 LDA wksp_compact_start_page ; Set transfer addr to buffer start
96D7 STA wksp_disc_op_mem_addr_1 ; Store transfer addr mid
96DA LDX #0 ; X=0: clear other addr bytes
96DC STX wksp_disc_op_mem_addr ; Clear transfer addr low
96DF DEX
96E0 STX wksp_disc_op_mem_addr_2 ; Clear high bytes
96E3 STX wksp_disc_op_mem_addr_3 ; Clear highest byte
96E6 .copy_sectors_remaining←1← 977A JMP
SEC ; Set carry for subtraction
96E7 LDA wksp_copy_write_sector ; Subtract copied amount from total
96EA SBC wksp_compact_length ; Subtract buffer size
96ED STA wksp_copy_write_sector ; Store reduced count low
96F0 LDA wksp_copy_write_sector_1 ; Get count mid
96F3 SBC #0 ; Subtract borrow
96F5 STA wksp_copy_write_sector_1 ; Store reduced mid
96F8 LDA wksp_copy_write_sector_2 ; Get count high
96FB SBC #0 ; Subtract borrow
96FD STA wksp_copy_write_sector_2 ; Store reduced high
9700 BCS set_transfer_address ; No underflow: proceed
9702 LDA wksp_copy_write_sector ; Underflow: adjust sector count
9705 ADC wksp_compact_length ; Add buffer size back
9708 STA wksp_disc_op_sector_count ; Store as final chunk size
970B .set_transfer_address←1← 9700 BCS
LDA #8 ; Read command = 8
970D STA wksp_disc_op_command ; Store in disc op
9710 LDA wksp_copy_read_sector ; Get source sector low
9713 STA wksp_disc_op_sector_lo ; Store in disc op sector
9716 LDA wksp_copy_read_sector_1 ; Get source sector mid
9719 STA wksp_disc_op_sector_mid ; Store in disc op
971C LDA wksp_copy_read_sector_2 ; Get source sector high + drive
971F STA wksp_disc_op_sector ; Store in disc op
9722 JSR exec_disc_op_from_wksp ; Read from source
9725 LDA #&0a ; Write command = &0A
9727 STA wksp_disc_op_command ; Store in disc op
972A LDA wksp_copy_src_sector ; Get dest sector low
972D STA wksp_disc_op_sector_lo ; Store in disc op sector
9730 LDA wksp_copy_src_sector_1 ; Get dest sector mid
9733 STA wksp_disc_op_sector_mid ; Store in disc op
9736 LDA wksp_copy_src_sector_2 ; Get dest sector high + drive
9739 STA wksp_disc_op_sector ; Store in disc op
973C JSR exec_disc_op_from_wksp ; Write to destination
973F LDA wksp_copy_write_sector ; Check if more sectors to copy
9742 ORA wksp_copy_write_sector_1 ; OR with mid byte
9745 ORA wksp_copy_write_sector_2 ; OR with high byte
9748 BEQ validate_disc_size ; Zero: all copied
974A LDA wksp_disc_op_sector_count ; Check if full buffer was used
974D CMP wksp_compact_length ; Compare with buffer size
9750 BNE validate_disc_size ; Partial: done
9752 CLC ; Advance source sector
9753 LDA wksp_copy_read_sector ; Get source low
9756 ADC wksp_compact_length ; Add buffer pages copied
9759 STA wksp_copy_read_sector ; Store updated source low
975C BCC check_format_parameters ; No carry
975E INC wksp_copy_read_sector_1 ; Carry: inc source mid
9761 BNE check_format_parameters ; No wrap
9763 INC wksp_copy_read_sector_2 ; Wrap: inc source high
9766 .check_format_parameters←2← 975C BCC← 9761 BNE
CLC ; Advance dest sector
9767 LDA wksp_copy_src_sector ; Get dest low
976A ADC wksp_compact_length ; Add buffer pages
976D STA wksp_copy_src_sector ; Store updated dest low
9770 BCC validate_sector_count ; No carry
9772 INC wksp_copy_src_sector_1 ; Carry: inc dest mid
9775 BNE validate_sector_count ; No wrap
9777 INC wksp_copy_src_sector_2 ; Wrap: inc dest high
977A .validate_sector_count←2← 9770 BCC← 9775 BNE
JMP copy_sectors_remaining ; Loop for more chunks
977D .validate_disc_size←2← 9748 BEQ← 9750 BNE
LDA zp_adfs_flags ; Check copy operation flag
977F AND #8 ; Bit 3: copy in progress?
9781 BEQ begin_format_operation ; Not set: reload directory
9783 RTS ; Set: return directly
9784 .begin_format_operation←1← 9781 BEQ
LDA #&12 ; Set transfer addr to &12 page
9786 STA wksp_disc_op_mem_addr_1 ; Store addr mid
9789 LDA #8 ; Read command = 8
978B STA wksp_disc_op_command ; Store command
978E LDA wksp_csd_sector_lo ; Get dir sector low
9791 STA wksp_disc_op_sector_lo ; Store in disc op sector low
9794 LDA wksp_csd_sector_mid ; Get dir sector mid
9797 STA wksp_disc_op_sector_mid ; Store in disc op mid
979A LDA wksp_csd_sector_hi ; Get dir sector high
979D STA wksp_disc_op_sector ; Store in disc op high
97A0 LDA #5 ; Read 5 sectors (full directory)
97A2 STA wksp_disc_op_sector_count ; Store sector count
97A5 JMP exec_disc_command ; Execute disc read

Initialise directory structure for format

Set up source and destination sector addresses for directory initialisation during a disc format operation.

97A8 .format_init_dir←2← 98AB JMP← 98D7 JSR
LDA #0 ; A=0: clear search state
97AA STA wksp_copy_dest_sector ; Clear dest sector low
97AD STA wksp_copy_dest_sector_1 ; Clear dest sector mid
97B0 STA wksp_copy_dest_sector_2 ; Clear dest sector high
97B3 .format_init_fsm←2← 9835 JMP← 9869 JMP
LDA #&ff ; A=&FF: init source sector to &FFFFFF
97B5 STA wksp_copy_read_sector ; Set source sector low
97B8 STA wksp_copy_read_sector_1 ; Set source sector mid
97BB STA wksp_copy_read_sector_2 ; Set source sector high
97BE JSR print_catalogue_header ; Point to first directory entry
97C1 .init_fsm_zeros_loop←2← 9813 BCC← 9817 BCS
LDY #0 ; Y=0: check entry first byte
97C3 LDA (zp_entry_ptr_lo),y ; Get first byte
97C5 BNE init_fsm_total_sectors ; Non-zero: valid entry
97C7 LDA wksp_copy_read_sector ; End of entries: check if any found
97CA AND wksp_copy_read_sector_1 ; AND all source sector bytes
97CD AND wksp_copy_read_sector_2 ; All &FF?
97D0 CMP #&ff ; Compare with &FF
97D2 BNE init_root_dir_name ; Not &FF: found an entry
97D4 JMP write_dir_and_validate ; All &FF: no entries, write dir
97D7 .init_fsm_total_sectors←1← 97C5 BNE
LDY #&16 ; Y=&16: get entry start sector
97D9 LDX #2 ; X=2: compare 3 sector bytes
97DB SEC ; Set carry for subtraction
97DC .init_fsm_sector_loop←1← 97E3 BPL
LDA wksp_osgbpb_end_ptr,y ; Get workspace sector byte
97DF SBC (zp_entry_ptr_lo),y ; Subtract entry sector byte
97E1 INY ; Next byte
97E2 DEX ; Next workspace byte
97E3 BPL init_fsm_sector_loop ; Loop for 3 bytes
97E5 BCS init_root_dir_entries ; Workspace >= entry: skip
97E7 LDY #&16 ; Y=&16: compare with other workspace
97E9 LDX #2 ; X=2: 3 bytes
97EB SEC ; Set carry
97EC .write_fsm_to_disc_loop←1← 97F3 BPL
LDA wksp_copy_osfile_exec,y ; Get other workspace byte
97EF SBC (zp_entry_ptr_lo),y ; Subtract entry sector byte
97F1 INY ; Next byte
97F2 DEX ; Next workspace byte
97F3 BPL write_fsm_to_disc_loop ; Loop for 3 bytes
97F5 BCC init_root_dir_entries ; Other < entry: update best entry
97F7 LDY #&16 ; Y=&16: copy entry sector to best
97F9 LDX #2 ; X=2: 3 bytes
97FB .create_root_dir←1← 9802 BPL
LDA (zp_entry_ptr_lo),y ; Get entry sector byte
97FD STA wksp_copy_osfile_exec,y ; Store as best entry sector
9800 INY ; Next byte
9801 DEX ; Next workspace byte
9802 BPL create_root_dir ; Loop for 3 bytes
9804 LDA zp_entry_ptr_lo ; Save entry pointer
9806 STA zp_text_ptr_lo ; Store as best entry pointer low
9808 LDA zp_entry_ptr_hi ; Get pointer high
980A STA zp_text_ptr_hi ; Store as best entry pointer high
980C .init_root_dir_entries←2← 97E5 BCS← 97F5 BCC
LDA zp_entry_ptr_lo ; Advance to next dir entry
980E CLC ; Clear carry for addition
980F ADC #&1a ; Add 26 bytes per entry
9811 STA zp_entry_ptr_lo ; Store updated pointer
9813 BCC init_fsm_zeros_loop ; No page crossing: continue search
9815 INC zp_entry_ptr_hi ; Increment page
9817 BCS init_fsm_zeros_loop
9819 .init_root_dir_name←1← 97D2 BNE
LDA zp_text_ptr_lo ; Restore best entry pointer
981B STA zp_entry_ptr_lo ; Store in (&B6)
981D LDA zp_text_ptr_hi ; Get high byte
981F STA zp_entry_ptr_hi ; Store in (&B7)
9821 LDY #2 ; Y=2: copy 3 source sector bytes
9823 .fill_root_name_loop←1← 982A BPL
LDA wksp_copy_read_sector,y ; Get source sector byte
9826 STA wksp_copy_dest_sector,y ; Store as dest for allocation
9829 DEY ; Next byte
982A BPL fill_root_name_loop ; Loop for 3 bytes
982C LDX #0 ; X=0: start scanning FSM
982E STX zp_mem_ptr_lo ; Store scan position
9830 .set_root_identity_loop←1← 984A BNE
CPX fsm_s1_end_of_list_ptr ; Past end of FSM?
9833 BCC write_root_dir_to_disc ; No: check this entry
9835 JMP format_init_fsm ; Past end: reinit search
9838 .write_root_dir_to_disc←1← 9833 BCC
INX ; Advance X by 3
9839 INX ; Continue advancing
983A INX ; 3rd byte
983B STX zp_mem_ptr_lo ; Save position
983D LDY #2 ; Y=2: compare sector bytes
983F .write_root_sectors_loop←1← 984F BPL
DEX ; Back up one
9840 LDA fsm_sector_0,x ; Get FSM address byte
9843 CMP wksp_copy_read_sector,y ; Compare with source sector
9846 BCS set_root_as_csd ; FSM >= source: possible match
9848 LDX zp_mem_ptr_lo ; Restore X, try next
984A BNE set_root_identity_loop ; Loop (X != 0)
984C .set_root_as_csd←1← 9846 BCS
BNE copy_root_sector_loop ; Exact match? Check all bytes
984E DEY ; Next byte (decreasing)
984F BPL write_root_sectors_loop ; Loop for 3 bytes
9851 .copy_root_sector_loop←1← 984C BNE
LDX zp_mem_ptr_lo ; Restore entry position
9853 CPX #6 ; Need at least 2 entries (>= 6)
9855 BCC set_format_drive ; Not enough entries: reinit
9857 LDY #0 ; Check if entry is adjacent
9859 CLC ; Clear carry for addition
985A PHP ; Save carry
985B .init_workspace_for_root←1← 9870 BNE
PLP ; Restore carry
985C LDA fsm_s0_pre6,x ; Get previous entry end address
985F ADC fsm_s0_reserved,x ; Add previous entry length
9862 PHP ; Save carry
9863 CMP wksp_copy_read_sector,y ; Compare with source sector
9866 BEQ format_next_track_loop ; Match: entries are adjacent
9868 PLP ; Restore carry, not adjacent
9869 .set_format_drive←1← 9855 BCC
JMP format_init_fsm ; Not adjacent: reinit search
986C .format_next_track_loop←1← 9866 BEQ
INX ; Next byte
986D INY ; Next source byte
986E CPY #3 ; All 3 bytes?
9870 BNE init_workspace_for_root ; No: continue comparing
9872 PLP ; Restore carry
9873 LDX #2 ; X=2: copy sector address
9875 LDY #&12 ; Y=&12: entry length offset
9877 LDA (zp_entry_ptr_lo),y ; Get entry length byte
9879 CMP #1 ; Compare with 1 (min sector)
987B .format_write_sectors_loop←1← 9890 BPL
INY ; Next length byte
987C LDA (zp_entry_ptr_lo),y ; Get next byte
987E ADC #0 ; Add carry from compare
9880 STA wksp_filename_save_hi,y ; Store sector count
9883 STA wksp_csd_drive_temp,y ; Store in alt workspace
9886 STA wksp_entry_size_base,y ; Store in disc op
9889 LDA wksp_copy_read_sector,x ; Get source sector byte
988C STA wksp_object_sector,x ; Store in object sector
988F DEX ; Next byte
9890 BPL format_write_sectors_loop ; Loop for 3 bytes
9892 JSR release_disc_space
9895 JSR allocate_disc_space ; Allocate space from FSM
9898 LDX #2 ; X=2: copy new sector address
989A LDY #&18 ; Y=&18: start sector in entry
989C .verify_formatted_sectors←1← 98A6 BPL
LDA wksp_alloc_sector,x ; Get new sector byte
989F STA (zp_entry_ptr_lo),y ; Store in directory entry
98A1 STA wksp_copy_src_sector,x ; Store as dest sector
98A4 DEY ; Next entry byte (decreasing)
98A5 DEX ; Next workspace byte
98A6 BPL verify_formatted_sectors ; Loop for 3 bytes
98A8 JSR copy_sectors_between_dirs ; Update CSD/lib/prev dir pointers
98AB JMP format_init_dir ; Continue compaction search
98AE .calculate_total_sectors←1← A350 JSR
LDA #0 ; A=0: init recursion stack pointer
98B0 STA zp_name_ptr_lo ; Store in workspace
98B2 STA wksp_osfile_attr_1 ; Clear root sector low
98B5 STA wksp_osfile_attr_2 ; Clear root sector mid
98B8 LDA #2 ; Root sector = 2
98BA STA wksp_osfile_attr ; Store root sector low
98BD LDA #&1b ; Set up path ':0.$' for root
98BF STA zp_name_ptr_hi ; Store in workspace
98C1 LDA #&3c ; Path string address low
98C3 STA zp_text_ptr_lo ; Store in (&B4)
98C5 LDA #&99 ; Path string page &99
98C7 STA zp_text_ptr_hi ; Store in (&B5)
98C9 .prepare_cdir_directory←1← 990C BMI
JSR parse_path_and_load ; Load root directory
98CC LDY #2 ; Y=2: copy parent sector
98CE .init_cdir_entries_loop←1← 98D5 BPL
LDA wksp_osfile_attr,y ; Get sector byte from workspace
98D1 STA dir_parent_sector,y ; Store as dir parent pointer
98D4 DEY ; Next byte
98D5 BPL init_cdir_entries_loop ; Loop for 3 bytes
98D7 JSR format_init_dir ; Init search state for this dir
98DA JSR print_catalogue_header ; Point to first entry
98DD .setup_cdir_dir_entry←2← 9932 BCC← 9936 BCS
LDY #0 ; Y=0: check entry
98DF LDA (zp_entry_ptr_lo),y ; Get first byte
98E1 BEQ set_cdir_parent_sector ; Zero: end of entries in this dir
98E3 LDY #3 ; Y=3: check access byte
98E5 LDA (zp_entry_ptr_lo),y ; Get access byte
98E7 BPL write_cdir_directory ; Bit 7 clear: regular file
98E9 LDA zp_name_ptr_lo ; Directory: check stack depth
98EB CMP #&fe ; Compare with &FE (max depth)
98ED BEQ set_cdir_parent_sector ; At max depth: skip this subdir
98EF LDY #0 ; Push subdir entry address on stack
98F1 LDA zp_entry_ptr_lo ; Get entry pointer low
98F3 STA zp_text_ptr_lo ; Store in (&B4)
98F5 STA (zp_name_ptr_lo),y ; Store on recursion stack
98F7 INC zp_name_ptr_lo ; Advance stack pointer
98F9 LDA zp_entry_ptr_hi ; Get entry pointer high
98FB STA zp_text_ptr_hi ; Store in (&B5)
98FD STA (zp_name_ptr_lo),y ; Store on recursion stack
98FF INC zp_name_ptr_lo ; Advance stack pointer
9901 LDX #2 ; X=2: save parent dir sector
9903 .copy_name_to_cdir_loop←1← 990A BPL
LDA wksp_csd_sector_lo,x ; Get parent sector byte
9906 STA wksp_osfile_attr,x ; Store in workspace
9909 DEX ; Next byte
990A BPL copy_name_to_cdir_loop ; Loop for 3 bytes
990C BMI prepare_cdir_directory
990E .set_cdir_parent_sector←2← 98E1 BEQ← 98ED BEQ
LDA zp_name_ptr_lo ; Check recursion stack
9910 BEQ finalise_cdir ; Stack empty: compaction done
9912 LDA #&3b ; Set up path for parent return
9914 STA zp_text_ptr_lo ; Store path address low
9916 LDA #&99 ; Path page &99
9918 STA zp_text_ptr_hi ; Store path address high
991A JSR parse_path_and_load ; Load parent directory
991D LDY #0 ; Y=0: pop entry address from stack
991F DEC zp_name_ptr_lo ; Decrement stack pointer
9921 LDA (zp_name_ptr_lo),y ; Get entry pointer high
9923 STA zp_entry_ptr_hi ; Restore (&B7)
9925 DEC zp_name_ptr_lo ; Decrement stack pointer
9927 LDA (zp_name_ptr_lo),y ; Get entry pointer low
9929 STA zp_entry_ptr_lo ; Restore (&B6)
992B .write_cdir_directory←1← 98E7 BPL
CLC ; Advance to next entry
992C LDA zp_entry_ptr_lo ; Get entry pointer low
992E ADC #&1a ; Add 26 bytes per entry
9930 STA zp_entry_ptr_lo ; Store updated pointer
9932 BCC setup_cdir_dir_entry ; No page crossing: continue scan
9934 INC zp_entry_ptr_hi ; Increment page
9936 BCS setup_cdir_dir_entry
9938 .finalise_cdir←1← 9910 BEQ
JMP save_wksp_and_return ; Save workspace and return
993B EQUS "^", &0D ; Unused "^" + CR: dead remnant

*ACCESS command handler

Change the access attributes of a file. Attributes are specified as a combination of L (locked), W (write), R (read), D (directory), and E (execute).

993D .star_access
JSR find_first_matching_entry ; Find first matching file
9940 BEQ set_file_attributes ; Found? Set attributes
9942 JMP not_found_error ; Not found: report error

Clear R, W, L attribute bits in entry

Strip bit 7 from the first three name bytes of the directory entry at (zp_entry_ptr).

9945 .clear_rwl_attributes←2← 9951 JSR← 9995 JSR
LDY #2 ; Y=2: clear R,W,L attribute bits
9947 .clear_attr_bits_loop←1← 994E BPL
LDA (zp_entry_ptr_lo),y ; Get name byte
9949 AND #&7f ; Strip bit 7 (clear attribute)
994B STA (zp_entry_ptr_lo),y ; Store back
994D DEY ; Next name byte
994E BPL clear_attr_bits_loop ; Loop for 3 bytes
9950 RTS ; Return (attributes cleared)

Set file attributes from access string

Clear existing R, W, L attributes then parse the access string to set appropriate flags including E and D.

9951 .set_file_attributes←2← 9940 BEQ← 99C1 BEQ
JSR clear_rwl_attributes ; Clear existing R,W,L attributes
9954 LDY #4 ; Y=4: check E attribute byte
9956 LDA (zp_entry_ptr_lo),y ; Get byte 4
9958 BMI save_e_attribute_state ; Bit 7 set: E attribute, skip
995A DEY ; Y=3: get D attribute byte
995B LDA (zp_entry_ptr_lo),y ; Get byte 3
995D AND #&80 ; Keep only bit 7 (D flag)
995F LDY #0 ; Y=0: get first name byte
9961 ORA (zp_entry_ptr_lo),y ; OR D flag into name byte 0
9963 STA (zp_entry_ptr_lo),y ; Store back
9965 .save_e_attribute_state←1← 9958 BMI
STA wksp_csd_sector_temp ; Save for E attribute check
9968 LDY #0 ; Y=0: scan for attribute string
996A .skip_filename_loop←1← 9977 BNE
LDA (zp_text_ptr_lo),y ; Skip filename characters
996C CMP #&20 ; Compare with space
996E BCC display_and_find_next ; Control char: end of command
9970 BEQ skip_spaces_before_attrs ; Space: skip to attributes
9972 CMP #&22 ; Double-quote?
9974 BEQ skip_spaces_before_attrs ; Yes: skip to attributes
9976 INY ; Next filename character
9977 BNE skip_filename_loop ; Loop scanning filename
9979 .skip_spaces_before_attrs←3← 9970 BEQ← 9974 BEQ← 9986 BNE
LDA (zp_text_ptr_lo),y ; Skip spaces between name and attrs
997B CMP #&20 ; Is it a space?
997D BCC display_and_find_next ; Control char: no attributes given
997F BEQ skip_space_or_quote ; Space: keep skipping
9981 CMP #&22 ; Is it a double-quote?
9983 BNE parse_attr_char ; No, start parsing attribute chars
9985 .skip_space_or_quote←1← 997F BEQ
INY ; Skip quote character
9986 BNE skip_spaces_before_attrs ; Continue skipping spaces
9988 .parse_attr_char←2← 9983 BNE← 99B9 BNE
LDA (zp_text_ptr_lo),y ; Parse attribute character
998A AND #&df ; Convert to uppercase
998C BIT wksp_csd_sector_temp ; Check if E attribute already set
998F BMI check_rwl_char ; E set: only L attribute allowed
9991 CMP #&45 ; Is it 'E'?
9993 BNE check_rwl_char ; No, check R/W/L
9995 JSR clear_rwl_attributes ; E: clear R,W,L first
9998 LDY #4 ; Y=4: set bit 7 of byte 4
999A LDA (zp_entry_ptr_lo),y ; Get entry byte at attribute pos
999C ORA #&80 ; Set E attribute
999E STA (zp_entry_ptr_lo),y ; Store with E bit set
99A0 STA wksp_csd_sector_temp ; Save E flag for later checks
99A3 BMI next_attr_char
99A5 .check_rwl_char←2← 998F BMI← 9993 BNE
LDX #2 ; X=2: check against "RWL" table
99A7 .match_rwl_loop←1← 99B2 BPL
CMP tbl_access_chars,x ; Compare with R/W/L character
99AA BEQ set_rwl_attribute_bit ; Match: set this attribute
99AC BIT wksp_csd_sector_temp ; E already set? Only L allowed
99AF BMI check_attr_terminator ; E already set: only L allowed
99B1 DEX ; Try next R/W/L character
99B2 BPL match_rwl_loop ; Loop through R, W, L
99B4 .check_attr_terminator←1← 99AF BMI
CMP #&21 ; Unknown char: check if printable
99B6 BCC display_and_find_next ; Control char: end of attributes
99B8 .next_attr_char←2← 99A3 BMI← 99D5 BNE
INY ; Next attribute character
99B9 BNE parse_attr_char ; Continue parsing
99BB .display_and_find_next←3← 996E BCC← 997D BCC← 99B6 BCC
JSR conditional_info_display ; Display info if *OPT1 verbose
99BE JSR advance_dir_entry_ptr ; Find next matching file
99C1 BEQ set_file_attributes ; More matches? Continue
99C3 JSR write_dir_and_validate ; Write directory back to disc
99C6 JMP save_wksp_and_return ; Save workspace and return
99C9 .set_rwl_attribute_bit←1← 99AA BEQ
TYA ; Set attribute: save text pointer
99CA PHA ; Save Y (text position) on stack
99CB TXA ; X = index into R/W/L (0,1,2)
99CC TAY ; Use as Y index into entry
99CD LDA (zp_entry_ptr_lo),y ; Get name byte at that position
99CF ORA #&80 ; Set bit 7 (attribute flag)
99D1 STA (zp_entry_ptr_lo),y ; Store back
99D3 PLA ; Restore text pointer
99D4 TAY ; Restore Y
99D5 BNE next_attr_char ; Continue parsing attributes
99D7 .print_aborted_error←1← 9A1B BNE
JSR osnewl
99DA JSR reload_fsm_and_dir_then_brk
99DD EQUB &92 ; Error &92: Aborted
99DE EQUS "Aborted", &00
fall through ↓

*DESTROY command handler

Delete multiple files matching a wildcard specification. Prompts for confirmation before deleting.

99E6 .star_destroy
LDA zp_text_ptr_lo ; Save filename pointer low
99E8 PHA ; Push low byte
99E9 LDA zp_text_ptr_hi ; Save filename pointer high
99EB PHA ; Push high byte
99EC LDA #&40 ; Set up workspace for *INFO call
99EE STA zp_osfile_ptr_lo ; Store in control block pointer low
99F0 LDA #&10 ; Control block page = &10
99F2 STA zp_osfile_ptr_hi ; Store in control block pointer high
99F4 JSR star_info ; List matching files via *INFO
99F7 PLA ; Restore filename pointer high
99F8 STA zp_text_ptr_hi ; Store in (&B5)
99FA PLA ; Restore filename pointer low
99FB STA zp_text_ptr_lo ; Store in (&B4)
99FD JSR print_inline_string ; Print "Destroy ? "
9A00 EQUS "Destroy ?"
9A09 EQUB &A0 ; ' ' + bit 7: end of inline string
9A0A LDX #3 ; X=3: expect 4 chars (CR,Y,E,S)
9A0C .confirm_destroy_loop←1← 9A1E BPL
JSR osrdch ; Read character from keyboard
9A0F CMP #&20 ; Is it a printable char?
9A11 BCC check_confirm_response ; No, don't echo control chars
9A13 JSR osasci ; Echo the typed character
9A16 .check_confirm_response←1← 9A11 BCC
AND #&df ; Convert to uppercase
9A18 CMP str_yes,x ; Compare with "YES\r" (reversed)
9A1B BNE print_aborted_error ; Mismatch: abort with Aborted error
9A1D DEX ; Next expected character
9A1E BPL confirm_destroy_loop ; Loop for all 4 chars
9A20 JSR osnewl ; Print newline after YES
9A23 INX ; Clear channel for error messages
9A24 STX wksp_cur_channel ; Store in current channel workspace
9A27 .delete_matching_files_loop←1← 9A3B JMP
LDA zp_text_ptr_lo ; Deletion loop: save filename low
9A29 PHA ; Push low byte
9A2A LDA zp_text_ptr_hi ; Save filename pointer high
9A2C PHA ; Push high byte
9A2D JSR find_first_matching_entry ; Find next matching file
9A30 BNE all_files_deleted ; Not found: all deleted, finish
9A32 JSR check_and_delete_found ; Delete this file
9A35 PLA ; Restore filename pointer high
9A36 STA zp_text_ptr_hi ; Store in (&B5)
9A38 PLA ; Restore filename pointer low
9A39 STA zp_text_ptr_lo ; Store in (&B4)
9A3B JMP delete_matching_files_loop ; Loop to delete next match
9A3E .all_files_deleted←1← 9A30 BNE
PLA ; Discard saved filename from stack
9A3F PLA ; Discard second saved byte
9A40 JMP save_wksp_and_return ; Save workspace and return

Jump through FSCV indirect vector

Jump indirectly through the filing system control vector.

9A43 .jmp_indirect_fscv←1← 9B89 JSR
JMP (fscv) ; Jump through filing system control

Default workspace initialisation template

29-byte template copied to workspace page &1100 during hard break initialisation (service call 2). Bytes beyond &1C are zeroed. Sets both CSD and library to the root directory '$' on drive 0, sector 2.

+00 wksp_csd_name (10 bytes): '$' + 9 spaces +0A wksp_lib_name (10 bytes): '$' + 9 spaces +14 wksp_csd_sector (3 bytes): sector 2 (root directory) +17 wksp_current_drive: drive 0 +18 wksp_lib_sector (3 bytes): sector 2 (root directory) +1B wksp_lib_drive: drive 0 +1C wksp_prev_dir_sector low: sector 2

9A46 .default_workspace_data←1← 9AFF LDA
EQUS "$ " ; '$' + 9 spaces: default CSD name
9A50 .default_lib_name
EQUS "$ " ; '$' + 9 spaces: default library name
9A5A .default_csd_sector
EQUB &02 ; CSD sector low: 2 (root directory)
9A5B EQUB &00 ; CSD sector mid: 0
9A5C EQUB &00 ; CSD sector high: 0
9A5D EQUB &00 ; Current drive: 0
9A5E .default_lib_sector
EQUB &02 ; Library sector low: 2 (root directory)
9A5F EQUB &00 ; Library sector mid: 0
9A60 EQUB &00 ; Library sector high: 0
9A61 EQUB &00 ; Library drive: 0
9A62 .default_prev_dir_sector
EQUB &02 ; Previous dir sector low: 2 (root dir)

Detect hard drive hardware

Check whether a SCSI hard drive is present by attempting to read the SCSI status register.

On ExitAcorrupted (Z set if hard drive present)
Xzero
Ypreserved
9A63 .hd_init_detect←3← 9AD7 JSR← 9B4B JSR← 9BFB JSR
LDA #&5a ; Write &5A to SCSI data register
9A65 JSR scsi_write_read_test ; Check if value survived
9A68 BNE return_22 ; No match: SCSI hardware not present
9A6A LDA #&a5 ; Write complement &A5
9A6C .scsi_write_read_test←1← 9A65 JSR
STA fred_hard_drive_0 ; Write test value to SCSI data port
9A6F LDX #0 ; X=0: clear IRQ enable register
9A71 STX fred_hard_drive_3 ; Disable SCSI interrupts
9A74 CMP fred_hard_drive_0 ; Read back: does value match?
9A77 .return_22←2← 9A68 BNE← 9C9C LDX
RTS ; Return

Boot option OSCLI address table and command strings

Three-byte lookup table of OSCLI string low addresses, indexed by boot option number (1-3). The high byte is always &9A. The auto-boot code reads fsm_s1_boot_option and uses it as an index into this table to select the OSCLI command.

Option 1 (Load): &7B -> "L.$.!BOOT" at &9A7B Option 2 (Run): &7D -> "$.!BOOT" at &9A7D (*RUN) Option 3 (Exec): &85 -> "E.$.!BOOT" at &9A85

Option 2 cleverly points into the middle of the "L.$.!BOOT" string to get just "$.!BOOT", which OSCLI interprets as *RUN $.!BOOT.

9A78 .boot_option_addr_table
EQUB <(str_l_boot) ; Option 1: *LOAD $.!BOOT
9A79 EQUB <(str_run_boot) ; Option 2: *RUN $.!BOOT
9A7A EQUB <(str_e_boot) ; Option 3: *EXEC $.!BOOT
9A7B .str_l_boot
EQUS "L.$.!BOOT", &0D ; "L.$.!BOOT" + CR: load boot file
9A85 .str_e_boot
EQUS "E.$.!BOOT", &0D ; "E.$.!BOOT" + CR: exec boot file

Service call dispatch table

RTS-trick dispatch table for MOS service calls 0-9. Low bytes at &9A8F, high bytes at &9A99, 10 entries.

9A8F .service_dispatch_lo←1← 9AC7 LDA
EQUB <(service_handler_0-1)
9A90 EQUB <(service_handler_1-1)
9A91 EQUB <(service_handler_2-1)
9A92 EQUB <(service_handler_3-1)
9A93 EQUB <(service_handler_4-1)
9A94 EQUB <(svc5_irq-1)
9A95 EQUB <(service_handler_0-1)
9A96 EQUB <(service_handler_0-1)
9A97 EQUB <(service_handler_8-1)
9A98 EQUB <(service_handler_9-1)
9A99 .service_dispatch_hi←1← 9AC3 LDA
EQUB >(service_handler_0-1)
9A9A EQUB >(service_handler_1-1)
9A9B EQUB >(service_handler_2-1)
9A9C EQUB >(service_handler_3-1)
9A9D EQUB >(service_handler_4-1)
9A9E EQUB >(svc5_irq-1)
9A9F EQUB >(service_handler_0-1)
9AA0 EQUB >(service_handler_0-1)
9AA1 EQUB >(service_handler_8-1)
9AA2 EQUB >(service_handler_9-1)

ROM service call handler

Main entry point for MOS service calls. Dispatches to individual handlers based on the service call number in A.

9AA3 .service_call_handler←1← 8003 JMP
.service_handler←1← 8003 JMP
PHA ; Save service call number
9AA4 CMP #1 ; Service 1: absolute workspace claim?
9AA6 BNE check_workspace_claimed ; Not service 1, continue
9AA8 LDA rom_wksp_table,x ; Read our ROM status byte
9AAB AND #&bf ; Clear bit 6 (ADFS workspace claimed)
9AAD STA rom_wksp_table,x ; Store updated status
9AB0 .check_workspace_claimed←1← 9AA6 BNE
LDA rom_wksp_table,x ; Read ROM status byte
9AB3 CMP #&40 ; Bit 6 set (workspace claimed)?
9AB5 BCC dispatch_service_call ; No, continue with dispatch
9AB7 PLA ; Yes, discard call and return
9AB8 .service_handler_0←1← 9AC0 BCS
RTS ; Return (service not claimed)
9AB9 .dispatch_service_call←1← 9AB5 BCC
PLA ; Restore service call number
9ABA CMP #&12 ; Service &12: select filing system?
9ABC BEQ select_adfs_filing_system ; Yes, handle FS selection
9ABE CMP #&0a ; Service >= &0A?
9AC0 BCS service_handler_0 ; Yes, not for us, return
9AC2 TAX ; Transfer to X for table index
9AC3 LDA service_dispatch_hi,x ; Get dispatch address high byte
9AC6 PHA ; Push dispatch high byte
9AC7 LDA service_dispatch_lo,x ; Get dispatch address low byte
9ACA PHA ; Push dispatch low byte
9ACB TXA ; Restore service number to A
9ACC LDX romsel_copy ; Get our ROM number
9ACE RTS ; RTS-dispatch to service handler

Service 1: absolute workspace claim

Initialise ADFS on a ROM filing system init service call. Checks for floppy and hard drive hardware. If either is present, claims the ROM workspace slot and raises PAGE to make room for ADFS workspace.

9ACF .service_handler_1←1← 9AD3 BPL
JSR floppy_check_present ; Check if floppy hardware present
9AD2 INX ; Increment result counter
9AD3 BPL service_handler_1
9AD5 BCC adfs_hardware_found ; No floppy, check hard drive
9AD7 JSR hd_init_detect ; Check if hard drive present
9ADA BEQ adfs_hardware_found ; Not present, skip ADFS init
9ADC LDA #&40 ; Mark ROM as having ADFS workspace
9ADE LDX romsel_copy ; Get our ROM number
9AE0 STA rom_wksp_table,x ; Store flag in ROM status table
9AE3 LDA #1 ; Return A=1: service handled
9AE5 RTS ; Return A=1 (claim 1 page)

Claim workspace for ADFS

Return A=1 to claim one workspace page and set Y=&1C to raise PAGE to &1D00 for ADFS workspace.

9AE6 .adfs_hardware_found←2← 9AD5 BCC← 9ADA BEQ
LDA #1 ; Return A=1: claim 1 page
9AE8 LDX romsel_copy ; Get our ROM number
9AEA CPY #&1c ; Y < &1C (PAGE already high enough)?
9AEC BCS return_23 ; Yes, don't change PAGE
9AEE LDY #&1c ; Y=&1C: ADFS PAGE value high byte
9AF0 .return_23←1← 9AEC BCS
RTS ; Return

Service 2: private workspace claim

Claim private workspace pages. On hard break, initialises the workspace with default values (CSD name, directory sector pointers, checksum). On soft break, preserves existing workspace. Sets up the filing system vectors and checks for Tube presence.

9AF1 .service_handler_2
TYA ; Save workspace page in ROM table
9AF2 STA rom_wksp_table,x ; Store workspace page in ROM table
9AF5 PHA ; Save Y on stack
9AF6 LDA last_break_type ; Check break type
9AF9 BEQ verify_workspace_checksum ; Soft break, skip workspace init
9AFB JSR get_wksp_addr_ba ; Get workspace base address
9AFE TAY ; Transfer Y to A
9AFF .copy_default_workspace_loop←1← 9B0B BNE
LDA default_workspace_data,y ; Get default workspace byte
9B02 CPY #&1d ; Past initialisation data (Y>=&1D)?
9B04 BCC check_workspace_initialised ; No, use default value from table
9B06 LDA #0 ; A=0: zero for unused workspace
9B08 .check_workspace_initialised←1← 9B04 BCC
STA (zp_wksp_ptr_lo),y ; Store byte in workspace
9B0A INY ; Next byte
9B0B BNE copy_default_workspace_loop ; Loop for all 256 workspace bytes
9B0D JSR store_wksp_checksum_ba_y ; Store workspace checksum
9B10 .verify_workspace_checksum←1← 9AF9 BEQ
JSR check_wksp_checksum
9B13 INY ; Y=next byte in workspace
9B14 LDA (zp_wksp_ptr_lo),y ; Read stored workspace byte
9B16 CMP #&ff ; Is it &FF (uninitialised)?
9B18 BNE claim_filing_system ; No, workspace valid from soft break
9B1A ROR zp_adfs_flags ; Clear Tube-present flag (bit 7)
9B1C CLC ; Clear carry for rotate
9B1D ROL zp_adfs_flags ; Restore bit 0, Tube flag cleared
9B1F JSR fsc6_new_filing_system
9B22 .claim_filing_system←1← 9B18 BNE
LDX #buffer_keyboard ; X=0: keyboard buffer number
9B24 LDA #osbyte_flush_buffer ; OSBYTE &15: flush buffer
9B26 JSR osbyte ; Flush specific buffer X
9B29 LDA #osbyte_insert_buffer ; OSBYTE &8A: insert into buffer
9B2B LDY #&ca ; Y=&CA: character to insert
9B2D JSR osbyte ; Insert character Y into buffer X
9B30 PLA ; Restore Y (original service param)
9B31 TAY ; Restore Y
9B32 LDX romsel_copy ; Get our ROM number
9B34 INY ; Increment Y (next workspace page)
9B35 LDA #2 ; A=2: return service 2 handled
9B37 .return_24←1← 9B3A BNE
RTS ; Return
9B38 .select_adfs_filing_system←1← 9ABC BEQ
CPY #8 ; Service &12: select filing system?
9B3A BNE return_24 ; No, return
9B3C TYA ; Y=8: ADFS filing system number
9B3D PHA ; Save on stack twice for later
9B3E PHA ; Push again (2 copies on stack)
9B3F BNE boot_run_option ; Always branch to FS init code
fall through ↓

Service 3: auto-boot

Handle auto-boot on power-on or Ctrl+Break. Scans the keyboard for Shift+Break (floppy boot) or A+Break (hard drive boot). Selects ADFS as the filing system and executes the boot file if configured.

9B41 .service_handler_3
TYA ; Save Y (boot flag)
9B42 PHA ; Push Y on stack
9B43 LDA #osbyte_scan_keyboard_from_16 ; OSBYTE &7A: keyboard scan
9B45 JSR osbyte ; Scan keyboard from key 16
9B48 INX ; Key pressed? (X=-1 means no)
9B49 BNE check_boot_option ; Yes, key pressed - check which
9B4B JSR hd_init_detect ; No key: try hard drive boot
9B4E BEQ boot_shift_pressed ; Hard drive found?
9B50 LDA last_break_type ; Check break type
9B53 BEQ boot_shift_pressed ; Power-on break? Skip to boot
9B55 LDX #&44 ; X=&44: floppy drive 4 default
9B57 .check_boot_option←1← 9B49 BNE
DEX ; Adjust key code
9B58 CPX #&79 ; Shift (key 122-1)?
9B5A BEQ boot_shift_pressed ; Shift+Break: boot from floppy
9B5C CPX #&41 ; A (key 66-1)?
9B5E BEQ boot_shift_pressed ; A+Break: boot from hard drive
9B60 CPX #&43 ; Ctrl+Break?
9B62 BEQ check_boot_key ; Yes, handle Ctrl+Break boot
9B64 PLA ; Unrecognised key: pass on service
9B65 TAY ; Restore Y
9B66 LDX romsel_copy ; Get our ROM number
9B68 LDA #3 ; A=3: service not claimed
9B6A RTS ; Return
9B6B .check_boot_key←1← 9B62 BEQ
PLA ; Ctrl+Break: discard saved Y
9B6C TXA ; Push key code instead
9B6D PHA ; Push key code on stack
9B6E .boot_shift_pressed←4← 9B4E BEQ← 9B53 BEQ← 9B5A BEQ← 9B5E BEQ
CLI ; Enable interrupts for OSBYTE
9B6F TXA ; Transfer key code to A
9B70 PHA ; Push key code for later
9B71 LDY #0
9B73 LDA #osbyte_write_keys_pressed ; OSBYTE &78: clear keys pressed
9B75 JSR osbyte ; Write all keys pressed information
9B78 JSR print_inline_string
9B7B EQUS "Acorn ADFS", &0D
9B86 .sub_c9b86
STA ext_vec_fsc_lo
9B89 JSR jmp_indirect_fscv
9B8C LDA #osbyte_issue_service_request ; OSBYTE &8F: issue service 10
9B8E LDX #&0a ; X=&0A: service 10 (claim workspace)
9B90 LDY #&ff ; Y=&FF
9B92 JSR osbyte ; Issue paged ROM service call, Reason X
9B95 LDA #&10 ; Default retry count = &10
9B97 STA wksp ; Store in workspace base
9B9A LDY #&0d ; Y=&0D: copy 14 bytes of vectors
9B9C .copy_boot_command_loop←1← 9BA3 BPL
LDA tbl_fs_vectors,y ; Get vector table byte from ROM
9B9F STA filev,y ; Store in MOS vector table
9BA2 DEY ; Next byte
9BA3 BPL copy_boot_command_loop ; Loop for 14 bytes
9BA5 LDA #&a8 ; OSBYTE &A8: read ROM pointer table
9BA7 JSR osbyte_y_ff_x_00 ; Read current value
9BAA STX zp_text_ptr_lo ; Store extended vector base low
9BAC STY zp_text_ptr_hi ; Store extended vector base high
9BAE LDY #&2f ; Y=&2F: offset into ext vector table
9BB0 LDX #&14 ; X=&14: 21 bytes of ext vectors
9BB2 .copy_csd_name_loop←1← 9BBF BPL
LDA tbl_extended_vectors,x ; Get ext vector byte from ROM
9BB5 CMP #&ff ; Is it &FF (use our ROM number)?
9BB7 BNE set_default_csd ; No, use value as-is
9BB9 LDA romsel_copy ; Replace &FF with our ROM number
9BBB .set_default_csd←1← 9BB7 BNE
STA (zp_text_ptr_lo),y ; Store in extended vector table
9BBD DEY ; Next vector byte
9BBE DEX ; Next ROM table byte
9BBF BPL copy_csd_name_loop ; Loop for 21 bytes
9BC1 LDA #osbyte_issue_service_request ; OSBYTE &8F: issue service 15
9BC3 LDX #&0f ; X=&0F: service 15 (vectors claimed)
9BC5 LDY #&ff ; Y=&FF
9BC7 JSR osbyte ; Issue paged ROM service call, Reason X
9BCA JSR mark_partial_transfer ; Initialise floppy state
9BCD JSR check_wksp_checksum
9BD0 LDX #0 ; X=0: clear workspace entries
9BD2 STX wksp_buf_flag_1 ; Clear workspace byte &08
9BD5 STX wksp_buf_flag_2 ; Clear workspace byte &0C
9BD8 STX wksp_osword_block ; Clear OSWORD block
9BDB STX wksp_disc_op_block ; Clear workspace byte &14
9BDE INX
9BDF STX wksp_buf_flag ; Set workspace byte &04 to 1
9BE2 LDY #&fb ; Y=&FB: copy 252 bytes from saved ws
9BE4 .restore_boot_workspace_loop←1← 9BEA BNE
LDA (zp_wksp_ptr_lo),y ; Get byte from saved workspace
9BE6 STA wksp_csd_name,y ; Copy to CSD name area
9BE9 DEY ; Next byte
9BEA BNE restore_boot_workspace_loop ; Loop until Y=0
9BEC LDA (zp_wksp_ptr_lo),y ; Copy byte at Y=0 too
9BEE STA wksp_csd_name,y ; Store in CSD name byte 0
9BF1 LDA wksp_flags_save ; Get saved flags from workspace
9BF4 AND #4 ; Keep only *OPT1 bit
9BF6 STA zp_adfs_flags ; Set as current ADFS flags
9BF8 JSR load_dir_for_drive ; Store channel checksum
9BFB JSR hd_init_detect
9BFE BNE boot_load_from_disc ; HD not found: skip HD flag
9C00 LDA zp_adfs_flags ; Get current flags
9C02 ORA #&20 ; Set bit 5: hard drive present
9C04 STA zp_adfs_flags ; Store updated flags
9C06 .boot_load_from_disc←1← 9BFE BNE
DEY ; Y=-1 (will be &FF after DEY)
9C07 TYA ; Transfer to A
9C08 STA (zp_wksp_ptr_lo),y ; Store &FF in workspace (marking done)
9C0A PLA ; Retrieve key code from stack
9C0B CMP #&43 ; Was it Ctrl+Break (key C = &43)?
9C0D BNE boot_set_page ; No, do normal boot sequence
9C0F JSR invalidate_fsm_and_dir
9C12 .boot_set_page←1← 9C0D BNE
LDY #3 ; Y=3: copy CSD sector to workspace
9C14 .copy_workspace_to_save_loop←1← 9C1B BPL
LDA wksp_csd_sector_lo,y ; Get CSD sector byte
9C17 STA wksp_csd_drive_sector,y ; Copy to CSD drive sector
9C1A DEY ; Next byte
9C1B BPL copy_workspace_to_save_loop ; Loop for 4 bytes
9C1D JSR save_wksp_and_return ; Save workspace state
9C20 LDX wksp_current_drive ; Check current drive is valid
9C23 INX ; Drive = &FF (uninitialised)?
9C24 BEQ set_fsm_load_flag ; Yes, skip to Tube detection
9C26 JSR check_disc_changed ; Ensure files are closed
9C29 LDA wksp_lib_sector ; Check if library is at default
9C2C CMP #2 ; Sector = 2 (root)?
9C2E BNE init_channel_complete ; No, library is already set
9C30 LDA wksp_lib_sector_lo ; Check other sector bytes
9C33 ORA wksp_lib_sector_mid ; OR with mid byte
9C36 ORA wksp_lib_sector_hi ; OR with high byte
9C39 BNE init_channel_complete ; Non-zero: lib sector is set
9C3B LDA #&ab ; Set up path ':0.LIB*'
9C3D STA zp_text_ptr_lo ; Store path address low
9C3F LDA #&9c ; Path in this ROM page
9C41 STA zp_text_ptr_hi ; Store path address high
9C43 JSR find_first_matching_entry ; Search for LIB directory
9C46 BNE init_channel_complete ; Not found: leave lib as default
9C48 .copy_drive_info_loop←1← 9C53 BEQ
LDY #3 ; Y=3: check access byte
9C4A LDA (zp_entry_ptr_lo),y ; Get access byte
9C4C BMI set_workspace_drive ; Bit 7: is it a directory?
9C4E JSR advance_dir_entry_ptr ; Not a dir: try next match
9C51 BNE init_channel_complete ; No more matches: leave default
9C53 BEQ copy_drive_info_loop
9C55 .set_workspace_drive←1← 9C4C BMI
LDX #2 ; X=2: copy 3 sector address bytes
9C57 LDY #&18 ; Y=&18: start sector in entry
9C59 .init_channel_flags_loop←1← 9C60 BPL
LDA (zp_entry_ptr_lo),y ; Get sector byte
9C5B STA wksp_lib_sector,x ; Store as library sector
9C5E DEY ; Next entry byte (decreasing Y)
9C5F DEX ; Next workspace byte (decreasing X)
9C60 BPL init_channel_flags_loop ; Loop for 3 bytes
9C62 LDA wksp_current_drive ; Get current drive number
9C65 STA wksp_lib_sector_hi ; Store as library drive
9C68 LDY #9 ; Y=9: copy 10-byte directory name
9C6A .init_per_channel_loop←1← 9C72 BPL
LDA (zp_entry_ptr_lo),y ; Get name byte from entry
9C6C AND #&7f ; Strip bit 7 (access flag)
9C6E STA wksp_lib_name,y ; Store as library name
9C71 DEY ; Next byte
9C72 BPL init_per_channel_loop ; Loop for 10 bytes
9C74 .init_channel_complete←4← 9C2E BNE← 9C39 BNE← 9C46 BNE← 9C51 BNE
JSR save_wksp_and_return ; Save workspace state
9C77 .set_fsm_load_flag←1← 9C24 BEQ
LDA #&ea ; OSBYTE &EA: read Tube presence
9C79 JSR osbyte_y_ff_x_00 ; Read current value
9C7C LDA zp_adfs_flags ; Get current ADFS flags
9C7E AND #&7f ; Clear bit 7 (Tube flag)
9C80 INX ; X+1: was X &FF (Tube present)?
9C81 BNE load_fsm_for_boot ; Non-zero: no Tube
9C83 ORA #&80 ; Tube present: set bit 7
9C85 .load_fsm_for_boot←1← 9C81 BNE
STA zp_adfs_flags ; Store updated flags
9C87 PLA ; Retrieve key/boot code from stack
9C88 PHA ; Push back for later
9C89 BNE set_default_dir_for_boot ; Non-zero: skip auto-boot
9C8B LDX wksp_current_drive ; Check drive is valid
9C8E INX ; Drive = &FF?
9C8F BNE clear_fsm_flag_after_load ; No, check boot option
9C91 STX wksp_drive_number ; X=0: store as drive for mount
9C94 JSR mount_drive_setup ; Mount drive 0
9C97 .clear_fsm_flag_after_load←1← 9C8F BNE
LDY fsm_s1_boot_option ; Get boot option from FSM
9C9A BEQ set_default_dir_for_boot ; Option 0: no auto-boot
9C9C LDX return_22,y ; Get boot command addr from table
9C9F LDY #&9a ; Y=&9A: command string page
9CA1 JSR oscli ; Execute boot command via OSCLI
9CA4 .set_default_dir_for_boot←2← 9C89 BNE← 9C9A BEQ
LDX romsel_copy ; Restore ROM number
9CA6 PLA ; Restore Y
9CA7 TAY ; Transfer to Y
9CA8 LDA #0 ; A=0: service claimed
9CAA RTS ; Return
9CAB EQUS ":0.LIB*", &0D ; ":0.LIB*" + CR: default library path

Filing system vector addresses

Seven 2-byte vector addresses copied to the MOS vector table at &0212-&021F when ADFS is selected. All point into the extended vector jump block at &FFxx, which dispatches through tbl_extended_vectors to reach the actual ADFS handler routines.

FILEV &FF1B OSFILE handler ARGSV &FF1E OSARGS handler BGETV &FF21 OSBGET handler BPUTV &FF24 OSBPUT handler GBPBV &FF27 OSGBPB handler FINDV &FF2A OSFIND handler FSCV &FF2D Filing system control handler

9CB3 .tbl_fs_vectors←1← 9B9C LDA
EQUW &FF1B ; FILEV: &FF1B (OSFILE)
9CB5 EQUW &FF1E ; ARGSV: &FF1E (OSARGS)
9CB7 EQUW &FF21 ; BGETV: &FF21 (OSBGET)
9CB9 EQUW &FF24 ; BPUTV: &FF24 (OSBPUT)
9CBB EQUW &FF27 ; GBPBV: &FF27 (OSGBPB)
9CBD EQUW &FF2A ; FINDV: &FF2A (OSFIND)
9CBF EQUW &FF2D ; FSCV: &FF2D (FSC)

Extended vector table

Seven 3-byte extended vector entries for the filing system API. Each entry is: handler address low, handler address high, ROM number (&FF, patched to actual ROM number when installed). Copied to the MOS extended vector area when ADFS is selected as the current filing system.

FILEV &923E osfile_handler ARGSV &A955 osargs_handler BGETV &AD63 osbget_handler BPUTV &B08F osbput_handler GBPBV &B57F osgbpb_handler FINDV &B1B6 osfind_handler FSCV &9E50 fscv_handler

9CC1 .tbl_extended_vectors←1← 9BB2 LDA
EQUW &923E ; FILEV: osfile_handler (&923E)
9CC3 EQUB &FF ; ROM: &FF (patched at runtime)
9CC4 EQUW &A955 ; ARGSV: osargs_handler (&A955)
9CC6 EQUB &FF ; ROM: &FF
9CC7 EQUW &AD63 ; BGETV: osbget_handler (&AD63)
9CC9 EQUB &FF ; ROM: &FF
9CCA EQUW &B08F ; BPUTV: osbput_handler (&B08F)
9CCC EQUB &FF ; ROM: &FF
9CCD EQUW &B57F ; GBPBV: osgbpb_handler (&B57F)
9CCF EQUB &FF ; ROM: &FF
9CD0 EQUW &B1B6 ; FINDV: osfind_handler (&B1B6)
9CD2 EQUB &FF ; ROM: &FF
9CD3 EQUW &9E50 ; FSCV: fscv_handler (&9E50)
9CD5 EQUB &FF ; ROM: &FF

Filing system name string

The string 'adfs' (reversed for stack-based comparison) used to identify the filing system during service call handling.

9CD6 .str_filing_system_name←2← 9CF7 CMP← 9DF9 CMP
EQUS "sfda"

Service 4: unrecognised star command

Handle unrecognised star commands passed to filing system ROMs. Matches commands against the ADFS command table and dispatches to the appropriate handler.

9CDA .service_handler_4
TYA ; Save Y (text offset)
9CDB PHA ; Save Y for later restore
9CDC LDA #&ff ; Push &FF (no prefix flag)
9CDE PHA ; Push default prefix flag (&FF)
9CDF LDA (os_text_ptr),y ; Get first command character
9CE1 ORA #&20 ; Convert to lowercase
9CE3 CMP #&66 ; Is it 'f' (FADFS prefix)?
9CE5 BNE check_adfs_prefix ; No, check for ADFS prefix
9CE7 PLA ; Replace &FF with 'C' (FSC code)
9CE8 LDA #&43 ; Replace with 'C' (FSC code)
9CEA PHA ; Push FSC code
9CEB INY ; Skip past 'F' prefix
9CEC .check_adfs_prefix←1← 9CE5 BNE
LDX #3 ; X=3: match 4 chars of 'ADFS'
9CEE .match_command_loop←1← 9CFD BPL
LDA (os_text_ptr),y ; Get next command character
9CF0 INY ; Advance text pointer
9CF1 CMP #&2e ; Is it a dot (abbreviation)?
9CF3 BEQ service4_not_matched ; Yes, match succeeded
9CF5 ORA #&20 ; Convert to lowercase for compare
9CF7 CMP str_filing_system_name,x ; Compare with "adfs" (backwards)
9CFA BNE service4_decline ; No match, not for us
9CFC DEX ; Next char in 'ADFS'
9CFD BPL match_command_loop ; Loop for 4 characters
9CFF .service4_not_matched←2← 9CF3 BEQ← 9D04 BEQ
LDA (os_text_ptr),y ; Skip spaces after 'ADFS'
9D01 INY ; Advance past matched space
9D02 CMP #&20 ; Space?
9D04 BEQ service4_not_matched ; Yes, skip more spaces
9D06 BCS service4_decline ; Printable: more text follows, fail
9D08 PLA ; Get prefix flag
9D09 TAX ; Transfer prefix flag to X
9D0A PLA ; Get saved text offset
9D0B TXA ; Transfer back to A
9D0C PHA ; Push for later restore
9D0D PHA ; Push again
9D0E JMP boot_run_option ; Select ADFS and execute command

Decline service 4 and pass on

Clean up stack and return A=4 to pass the unrecognised command to the next ROM in the service chain.

9D11 .service4_decline←2← 9CFA BNE← 9D06 BCS
PLA ; Not for us: clean up stack
9D12 PLA ; Clean up stack (discard flag)
9D13 TAY ; Restore Y
9D14 LDA #4 ; A=4: pass on to next ROM
9D16 LDX romsel_copy ; Get our ROM number
9D18 RTS ; Return (not our command)

Service 8: unrecognised OSWORD

Handle unrecognised OSWORD calls. ADFS claims OSWORD &72 for direct disc access.

9D19 .service_handler_8
TYA ; Save Y (OSWORD number is at &EF)
9D1A PHA ; Save Y on stack
9D1B LDA #0 ; A=0 for OSARGS read filing system
9D1D TAY
; On return, A is the filing system number:
; ┌────┬───────────────────────────┐
; │ A │ Meaning │
; ├────┼───────────────────────────┤
; │ 0 │ no filing system selected │
; │ 1 │ 1200 baud CFS │
; │ 2 │ 300 baud CFS │
; │ 3 │ ROM filing system │
; │ 4 │ Disc filing system │
; │ 5 │ Network filing system │
; │ 6 │ Teletext filing system │
; │ 7 │ IEEE filing system │
; │ 8 │ ADFS │
; │ 9 │ Host filing system │
; │ 10 │ Videodisc filing system │
; └────┴───────────────────────────┘
9D1E JSR osargs ; Get current filing system number Get filing system number (A=0, Y=0)
9D21 CMP #8 ; Is it ADFS (filing system 8)?
9D23 BNE store_result_byte ; No, pass on to next ROM
9D25 LDA zp_osbyte_last_a ; Get OSWORD number from &EF
9D27 CMP #&72 ; Is it OSWORD &72 (disc access)?
9D29 BNE check_transfer_complete ; No, check other OSWORD numbers
9D2B LDA zp_osbyte_last_x ; Get control block address from &F0
9D2D STA zp_wksp_ptr_lo ; Store in (&BA) for access
9D2F LDA zp_osbyte_last_y ; Get control block high byte
9D31 STA zp_wksp_ptr_hi ; Store in (&BB)
9D33 LDY #&0f ; Y=&0F: copy 16 bytes of ctrl block
9D35 .match_osword_block_loop←1← 9D3B BPL
LDA (zp_wksp_ptr_lo),y ; Copy control block to workspace
9D37 STA wksp_disc_op_result,y ; Copy control block to workspace
9D3A DEY ; Next byte
9D3B BPL match_osword_block_loop ; Loop for 16 bytes
9D3D LDA wksp_disc_op_command ; Get disc operation command byte
9D40 AND #&fd ; Mask out direction bit
9D42 CMP #8 ; Command 8 = verify?
9D44 BEQ store_osword_result ; Yes, handle verify specially
9D46 .copy_disc_op_params_loop←1← 9D5A BNE
LDX #&15 ; Set up disc op control block
9D48 LDY #&10 ; Y=&10: workspace control block
9D4A INC wksp_current_drive ; Temporarily set drive to &FF+1=0
9D4D BEQ execute_osword_disc_op ; Was it already 0 (unset)?
9D4F DEC wksp_current_drive ; No, restore original drive
9D52 .execute_osword_disc_op←1← 9D4D BEQ
JSR command_exec_xy ; Execute the disc command
9D55 BPL copy_result_sector_loop ; Success?
9D57 .store_osword_result←1← 9D44 BEQ
LDA wksp_disc_op_sector_count ; Check sector count for verify
9D5A BNE copy_disc_op_params_loop ; More sectors to verify
9D5C JSR check_disc_command_type ; Process verify result
9D5F .copy_result_sector_loop←1← 9D55 BPL
LDY #0 ; Y=0: store result at block+0
9D61 STA (zp_wksp_ptr_lo),y ; Write result back to control block
9D63 .set_result_error_code←4← 9D7F BMI← 9D91 JMP← 9DA5 BMI← ABA2 JMP
LDX romsel_copy ; Restore ROM number
9D65 PLA ; Restore Y
9D66 TAY ; Restore Y
9D67 LDA #0 ; A=0: service call claimed
9D69 RTS ; Return (service claimed)
9D6A .store_result_byte←2← 9D23 BNE← 9D96 BNE
LDX romsel_copy ; Not our filing system
9D6C PLA ; Restore Y
9D6D TAY ; Transfer to Y
9D6E LDA #8 ; A=8: pass on to next ROM
9D70 RTS ; Return (not claimed)
9D71 .check_transfer_complete←1← 9D29 BNE
CMP #&73 ; OSWORD &73 (read last error)?
9D73 BNE adjust_partial_transfer ; No, check next
9D75 LDY #4 ; Y=4: copy 5 bytes of error info
9D77 .copy_transfer_count_loop←1← 9D7D BPL
LDA wksp_err_sector,y ; Copy error sector+code to block
9D7A STA (zp_osbyte_last_x),y ; Store error byte in control block
9D7C DEY ; Next byte
9D7D BPL copy_transfer_count_loop ; Loop for 5 error bytes
9D7F BMI set_result_error_code ; Return as claimed
9D81 .adjust_partial_transfer←1← 9D73 BNE
CMP #&70 ; OSWORD &70 (read dir state)?
9D83 BNE store_adjusted_count ; No, check next
9D85 LDA dir_master_sequence ; Get directory master sequence
9D88 LDY #0 ; Y=0: store at block+0
9D8A STA (zp_osbyte_last_x),y ; Write sequence number to block
9D8C LDA zp_adfs_flags ; Get ADFS flags
9D8E INY
9D8F STA (zp_osbyte_last_x),y ; Write flags to block+1
9D91 JMP set_result_error_code ; Return as claimed
9D94 .store_adjusted_count←1← 9D83 BNE
CMP #&71 ; OSWORD &71 (read free space)?
9D96 BNE store_result_byte ; No, not for us
9D98 JSR calc_total_free_space ; Calculate free space on disc
9D9B LDY #3 ; Y=3: copy 4 bytes of result
9D9D .copy_adjusted_bytes_loop←1← 9DA3 BPL
LDA wksp_disc_op_result,y ; Copy free space to control block
9DA0 STA (zp_osbyte_last_x),y ; Store free space byte
9DA2 DEY ; Next byte
9DA3 BPL copy_adjusted_bytes_loop ; Loop for 4 bytes
9DA5 BMI set_result_error_code ; Return as claimed
fall through ↓

Print *HELP version header line

Print a newline followed by the ROM version string for *HELP output. Uses print_inline_string.

9DA7 .help_print_header←2← 9DC6 JSR← 9E08 JSR
JSR print_inline_string
9DAA EQUS &0D, "Advanced DFS 1.30"
9DBC EQUB &8D ; CR + bit 7: end of version string
9DBD RTS ; Return to caller

Service 9: *HELP

Handle *HELP requests. Prints ADFS version information when *HELP ADFS is entered.

9DBE .service_handler_9
TYA ; Save Y (text pointer offset)
9DBF PHA ; Save text pointer on stack
9DC0 LDA (os_text_ptr),y ; Get first char of *HELP argument
9DC2 CMP #&20 ; Is it a printable char?
9DC4 BCS end_of_command_name ; Yes, try matching 'ADFS'
9DC6 JSR help_print_header ; No argument: print version banner
9DC9 JSR print_inline_string ; Print ' ADFS'
9DCC EQUS " ADFS"
9DD2 .check_help_adfs_keyword
STA la868 ; Store flag for help type
9DD5 LDX romsel_copy ; Get our ROM number
9DD7 LDA #9 ; A=9: return service 9 (not claimed)
9DD9 .return_25←1← 9DDF BCS
RTS ; Return

Print *HELP ADFS command list

Print the ADFS command list for *HELP output, formatting each command name with padding.

9DDA .print_help_command_list←2← 9DE5 JSR← 9DEA JSR
INY ; Check next char of HELP argument
9DDB LDA (os_text_ptr),y ; Get next char from help text
9DDD CMP #&20 ; Is it printable?
9DDF BCS return_25 ; Yes, return (more text follows)
9DE1 PLA ; End of argument: pop return address
9DE2 PLA ; Pop 2 return addresses
9DE3 BCC l9dd3 ; Return to service dispatcher
9DE5 .print_next_command←3← 9DE8 BNE← 9DFC BNE← 9E06 BCS
JSR print_help_command_list ; Skip non-space chars in argument
9DE8 BNE print_next_command ; Loop skipping non-space chars
9DEA .output_command_name_loop←1← 9DED BEQ
JSR print_help_command_list ; Skip space chars after word
9DED BEQ output_command_name_loop ; Loop skipping space chars
9DEF .end_of_command_name←1← 9DC4 BCS
LDX #3 ; X=3: compare 4 chars of 'ADFS'
9DF1 .pad_command_name_loop←1← 9E00 BPL
LDA (os_text_ptr),y ; Get char from argument
9DF3 CMP #&2e ; Is it a dot (abbreviation)?
9DF5 BEQ check_more_commands ; Yes, match succeeded
9DF7 ORA #&20 ; Convert to lowercase for compare
9DF9 CMP str_filing_system_name,x ; Compare with "adfs" backwards
9DFC BNE print_next_command ; No match, skip this word
9DFE INY ; Next char in argument
9DFF DEX ; Next char in 'ADFS'
9E00 BPL pad_command_name_loop ; Loop for 4 chars
9E02 LDA (os_text_ptr),y ; Check char after 'ADFS' match
9E04 CMP #&21 ; More alpha chars? Not exact match
9E06 BCS print_next_command ; Not a match, skip word
9E08 .check_more_commands←1← 9DF5 BEQ
JSR help_print_header ; Print version info
9E0B LDX #0 ; X=0: start of command table
9E0D .print_help_data_commands←1← 9E46 BNE
LDA tbl_commands,x ; Get command table byte
9E10 BMI l9dd3 ; Bit 7 set: end of table
9E12 JSR print_inline_string ; Print " " indent before command name
9E15 EQUS " "
9E16 EQUB &A0 ; ' ' + bit 7: end of inline string
9E17 LDY #9 ; Y=9: max 10 chars per command name
9E19 .print_data_cmd_name_loop←1← 9E23 BPL
LDA tbl_commands,x ; Get char from command table
9E1C BMI end_of_data_command ; Bit 7 set: end of command name
9E1E JSR osasci ; Print command name character
9E21 INX ; Next table byte
9E22 DEY ; Decrement char counter
9E23 BPL print_data_cmd_name_loop ; Loop for up to 10 chars
9E25 .end_of_data_command←2← 9E1C BMI← 9E29 BPL
JSR print_space ; Print space for padding
9E28 DEY ; Decrement padding counter
9E29 BPL end_of_data_command ; Loop until 10 columns filled
9E2B TXA ; Save table index
9E2C PHA ; Save table index on stack
9E2D LDA l9ee5,x ; Get address byte from table+2
9E30 PHA ; Save for low nibble
9E31 LSR ; Shift high nibble down
9E32 LSR ; Shift high nibble to low
9E33 LSR ; 4 right shifts total
9E34 LSR ; 4th shift
9E35 JSR setup_help_param_ptr ; Print as hex digit
9E38 PLA ; Restore address byte
9E39 AND #&0f ; Isolate low nibble
9E3B JSR setup_help_param_ptr ; Print as hex digit
9E3E JSR osnewl ; Print newline
9E41 PLA ; Restore table index
9E42 TAX ; Restore table index to X
9E43 INX ; Skip past 3-byte entry data
9E44 INX ; Skip past 1st dispatch byte
9E45 INX ; Skip past 2nd dispatch byte
9E46 BNE print_help_data_commands ; Loop for all commands
fall through ↓

*HELP parameter format string pointer table

Eight low-byte pointers into the &9Fxx page, indexing the parameter format strings displayed after each command name in the *HELP ADFS output. Each command's third table byte packs two nibble indices: the high nibble selects the first parameter string, the low nibble selects the second. For example, *ACCESS has byte &16 meaning index 1 then index 6, producing "ACCESS (L)(W)(R)(E)" in the listing.

0: (none) 4: () 1: 5: 2: 6: (L)(W)(R)(E) 3: <Ob Spec> 7: </p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9E48">9E48</a></td> <td><span class="label">.tbl_help_param_ptrs<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9280">← 9280 LDA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUB</span> <span data-tip="215 &D7 %11010111"><(help_param_none)</span> <span class="comment">; (no parameter)</span></td> </tr> <tr id="addr-9E49"> <td class="addr"><a href="#addr-9E49">9E49</a></td> <td> <span class="directive">EQUB</span> <span data-tip="141 &8D %10001101"><(help_param_list_spec)</span> <span class="comment">; "<List Spec>"</span></td> </tr> <tr id="addr-9E4A"> <td class="addr"><a href="#addr-9E4A">9E4A</a></td> <td> <span class="directive">EQUB</span> <span data-tip="153 &99 %10011001"><(help_param_ob_spec)</span> <span class="comment">; "<Ob Spec>"</span></td> </tr> <tr id="addr-9E4B"> <td class="addr"><a href="#addr-9E4B">9E4B</a></td> <td> <span class="directive">EQUB</span> <span data-tip="163 &A3 %10100011"><(help_param_wild_ob_spec)</span> <span class="comment">; "<<em>Ob Spec</em>>"</span></td> </tr> <tr id="addr-9E4C"> <td class="addr"><a href="#addr-9E4C">9E4C</a></td> <td> <span class="directive">EQUB</span> <span data-tip="175 &AF %10101111"><(help_param_drive)</span> <span class="comment">; "(<Drive>)"</span></td> </tr> <tr id="addr-9E4D"> <td class="addr"><a href="#addr-9E4D">9E4D</a></td> <td> <span class="directive">EQUB</span> <span data-tip="185 &B9 %10111001"><(help_param_sp_lp)</span> <span class="comment">; "<SP> <LP>"</span></td> </tr> <tr id="addr-9E4E"> <td class="addr"><a href="#addr-9E4E">9E4E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="195 &C3 %11000011"><(help_param_access)</span> <span class="comment">; "(L)(W)(R)(E)"</span></td> </tr> <tr id="addr-9E4F"> <td class="addr"><a href="#addr-9E4F">9E4F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="208 &D0 %11010000"><(help_param_title)</span> <span class="comment">; "<Title>"</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9E50"> <td colspan="2"><div class="sub-header"> <h3>Filing system control vector handler</h3> <div class="sub-desc"><p>Handle filing system control calls via FSCV. Dispatches star commands, *RUN, *CAT, etc.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9E50">9E50</a></td> <td><span class="label">.fscv_handler</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save text pointer in (&B4)</span></td> </tr> <tr id="addr-9E52"> <td class="addr"><a href="#addr-9E52">9E52</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store text pointer high</span></td> </tr> <tr id="addr-9E54"> <td class="addr"><a href="#addr-9E54">9E54</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer FSC code to X</span></td> </tr> <tr id="addr-9E55"> <td class="addr"><a href="#addr-9E55">9E55</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-9E6C" data-tip="&9E6C">return_26</a></span> <span class="comment">; FSC >= &80? Not for us</span></td> </tr> <tr id="addr-9E57"> <td class="addr"><a href="#addr-9E57">9E57</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; FSC >= 9? Not for us</span></td> </tr> <tr id="addr-9E59"> <td class="addr"><a href="#addr-9E59">9E59</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-9E6C" data-tip="&9E6C">return_26</a></span> <span class="comment">; FSC >= 9: not for us</span></td> </tr> <tr id="addr-9E5B"> <td class="addr"><a href="#addr-9E5B">9E5B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear current channel</span></td> </tr> <tr id="addr-9E5D"> <td class="addr"><a href="#addr-9E5D">9E5D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D5">wksp_cur_channel</span></span> <span class="comment">; Clear current channel</span></td> </tr> <tr id="addr-9E60"> <td class="addr"><a href="#addr-9E60">9E60</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-9E76" data-tip="&9E76">fscv_dispatch_hi</a>,x</span> <span class="comment">; Get dispatch address high byte</span></td> </tr> <tr id="addr-9E63"> <td class="addr"><a href="#addr-9E63">9E63</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push dispatch high byte</span></td> </tr> <tr id="addr-9E64"> <td class="addr"><a href="#addr-9E64">9E64</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-9E6D" data-tip="&9E6D – FSCV dispatch table">fscv_dispatch_lo</a>,x</span> <span class="comment">; Get dispatch address low byte</span></td> </tr> <tr id="addr-9E67"> <td class="addr"><a href="#addr-9E67">9E67</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push dispatch low byte</span></td> </tr> <tr id="addr-9E68"> <td class="addr"><a href="#addr-9E68">9E68</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Restore X (text pointer low)</span></td> </tr> <tr id="addr-9E6A"> <td class="addr"><a href="#addr-9E6A">9E6A</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Restore Y (text pointer high)</span></td> </tr> <tr id="addr-9E6C"> <td class="addr"><a href="#addr-9E6C">9E6C</a></td> <td><span class="label">.return_26<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-9E55">← 9E55 BMI</a><a href="#addr-9E59">← 9E59 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; RTS-dispatch to handler</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9E6D"> <td colspan="2"><div class="sub-header"> <h3>FSCV dispatch table</h3> <div class="sub-desc"><p>RTS-trick dispatch table for filing system control calls 0-8. Low bytes at &9E6D, high bytes at &9E76, 9 entries. FSC 0=<em>OPT, 1=check EOF, 2=</em>/, 3=*command, 4=*RUN, 5=*CAT, 6=new FS, 7=handle range, 8=*command (OS 1.20).</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9E6D">9E6D</a></td> <td><span class="label">.fscv_dispatch_lo<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9E64">← 9E64 LDA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUB</span> <span data-tip="220 &DC %11011100"><(fsc0_star_opt-1)</span></td> </tr> <tr id="addr-9E6E"> <td class="addr"><a href="#addr-9E6E">9E6E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="57 &39 %00111001 '9'"><(check_eof_for_handle-1)</span></td> </tr> <tr id="addr-9E6F"> <td class="addr"><a href="#addr-9E6F">9E6F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="152 &98 %10011000"><(star_run-1)</span></td> </tr> <tr id="addr-9E70"> <td class="addr"><a href="#addr-9E70">9E70</a></td> <td> <span class="directive">EQUB</span> <span data-tip="126 &7E %01111110 '~'"><(star_cmd-1)</span></td> </tr> <tr id="addr-9E71"> <td class="addr"><a href="#addr-9E71">9E71</a></td> <td> <span class="directive">EQUB</span> <span data-tip="152 &98 %10011000"><(star_run-1)</span></td> </tr> <tr id="addr-9E72"> <td class="addr"><a href="#addr-9E72">9E72</a></td> <td> <span class="directive">EQUB</span> <span data-tip="205 &CD %11001101"><(print_catalogue_entries-1)</span></td> </tr> <tr id="addr-9E73"> <td class="addr"><a href="#addr-9E73">9E73</a></td> <td> <span class="directive">EQUB</span> <span data-tip="59 &3B %00111011 ';'"><(fsc6_new_filing_system-1)</span></td> </tr> <tr id="addr-9E74"> <td class="addr"><a href="#addr-9E74">9E74</a></td> <td> <span class="directive">EQUB</span> <span data-tip="215 &D7 %11010111"><(fsc7_read_handle_range-1)</span></td> </tr> <tr id="addr-9E75"> <td class="addr"><a href="#addr-9E75">9E75</a></td> <td> <span class="directive">EQUB</span> <span data-tip="147 &93 %10010011"><(check_compaction_recommended-1)</span></td> </tr> <tr id="addr-9E76"> <td class="addr"><a href="#addr-9E76">9E76</a></td> <td><span class="label">.fscv_dispatch_hi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9E60">← 9E60 LDA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUB</span> <span data-tip="159 &9F %10011111">>(fsc0_star_opt-1)</span></td> </tr> <tr id="addr-9E77"> <td class="addr"><a href="#addr-9E77">9E77</a></td> <td> <span class="directive">EQUB</span> <span data-tip="173 &AD %10101101">>(check_eof_for_handle-1)</span></td> </tr> <tr id="addr-9E78"> <td class="addr"><a href="#addr-9E78">9E78</a></td> <td> <span class="directive">EQUB</span> <span data-tip="163 &A3 %10100011">>(star_run-1)</span></td> </tr> <tr id="addr-9E79"> <td class="addr"><a href="#addr-9E79">9E79</a></td> <td> <span class="directive">EQUB</span> <span data-tip="158 &9E %10011110">>(star_cmd-1)</span></td> </tr> <tr id="addr-9E7A"> <td class="addr"><a href="#addr-9E7A">9E7A</a></td> <td> <span class="directive">EQUB</span> <span data-tip="163 &A3 %10100011">>(star_run-1)</span></td> </tr> <tr id="addr-9E7B"> <td class="addr"><a href="#addr-9E7B">9E7B</a></td> <td> <span class="directive">EQUB</span> <span data-tip="147 &93 %10010011">>(print_catalogue_entries-1)</span></td> </tr> <tr id="addr-9E7C"> <td class="addr"><a href="#addr-9E7C">9E7C</a></td> <td> <span class="directive">EQUB</span> <span data-tip="169 &A9 %10101001">>(fsc6_new_filing_system-1)</span></td> </tr> <tr id="addr-9E7D"> <td class="addr"><a href="#addr-9E7D">9E7D</a></td> <td> <span class="directive">EQUB</span> <span data-tip="159 &9F %10011111">>(fsc7_read_handle_range-1)</span></td> </tr> <tr id="addr-9E7E"> <td class="addr"><a href="#addr-9E7E">9E7E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">>(check_compaction_recommended-1)</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9E7F"> <td colspan="2"><div class="sub-header"> <h3>Parse and dispatch star command</h3> <div class="sub-desc"><p>Match the command string at (&B4) against the command table at tbl_commands. The table encodes command names with their dispatch addresses. Supports abbreviation with dot.</p> <p>Uses RTS-trick dispatch to the matched command handler.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9E7F">9E7F</a></td> <td><span class="label">.star_cmd</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8305" data-tip="&8305 – Wait while files are being ensured">wait_ensuring</a></span> <span class="comment">; Wait if files being ensured</span></td> </tr> <tr id="addr-9E82"> <td class="addr"><a href="#addr-9E82">9E82</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="162 &A2 %10100010">#&a2</span></span> <span class="comment">; Set up workspace pointer</span></td> </tr> <tr id="addr-9E84"> <td class="addr"><a href="#addr-9E84">9E84</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store workspace pointer low</span></td> </tr> <tr id="addr-9E86"> <td class="addr"><a href="#addr-9E86">9E86</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Workspace page = &10</span></td> </tr> <tr id="addr-9E88"> <td class="addr"><a href="#addr-9E88">9E88</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store workspace pointer high</span></td> </tr> <tr id="addr-9E8A"> <td class="addr"><a href="#addr-9E8A">9E8A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4CF" data-tip="&A4CF – Skip leading spaces in command argument">skip_spaces</a></span> <span class="comment">; Skip leading spaces in command</span></td> </tr> <tr id="addr-9E8D"> <td class="addr"><a href="#addr-9E8D">9E8D</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="253 &FD %11111101">#&fd</span></span> <span class="comment">; X=&FD: start before first table entry</span></td> </tr> <tr id="addr-9E8F"> <td class="addr"><a href="#addr-9E8F">9E8F</a></td> <td><span class="label">.next_command_entry<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-9EAF">← 9EAF BNE</a><a href="#addr-9EC1">← 9EC1 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INX</span> <span class="comment">; Advance X past previous entry's data</span></td> </tr> <tr id="addr-9E90"> <td class="addr"><a href="#addr-9E90">9E90</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Next table entry</span></td> </tr> <tr id="addr-9E91"> <td class="addr"><a href="#addr-9E91">9E91</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: start before first char</span></td> </tr> <tr id="addr-9E93"> <td class="addr"><a href="#addr-9E93">9E93</a></td> <td><span class="label">.match_command_char<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-9E9C">← 9E9C BEQ</a><a href="#addr-9EA2">← 9EA2 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INX</span> <span class="comment">; Next table byte and command char</span></td> </tr> <tr id="addr-9E94"> <td class="addr"><a href="#addr-9E94">9E94</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next argument character</span></td> </tr> <tr id="addr-9E95"> <td class="addr"><a href="#addr-9E95">9E95</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-9EE3" data-tip="&9EE3 – Star command name and dispatch table">tbl_commands</a>,x</span> <span class="comment">; Get byte from command table</span></td> </tr> <tr id="addr-9E98"> <td class="addr"><a href="#addr-9E98">9E98</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-9EB4" data-tip="&9EB4">end_of_table_name</a></span> <span class="comment">; Bit 7 set: end of command name</span></td> </tr> <tr id="addr-9E9A"> <td class="addr"><a href="#addr-9E9A">9E9A</a></td> <td> <span class="opcode">CMP</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Compare with input character</span></td> </tr> <tr id="addr-9E9C"> <td class="addr"><a href="#addr-9E9C">9E9C</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-9E93" data-tip="&9E93">match_command_char</a></span> <span class="comment">; Match, continue</span></td> </tr> <tr id="addr-9E9E"> <td class="addr"><a href="#addr-9E9E">9E9E</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Try case-insensitive (OR &20)</span></td> </tr> <tr id="addr-9EA0"> <td class="addr"><a href="#addr-9EA0">9EA0</a></td> <td> <span class="opcode">CMP</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Compare again</span></td> </tr> <tr id="addr-9EA2"> <td class="addr"><a href="#addr-9EA2">9EA2</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-9E93" data-tip="&9E93">match_command_char</a></span> <span class="comment">; Match, continue</span></td> </tr> <tr id="addr-9EA4"> <td class="addr"><a href="#addr-9EA4">9EA4</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Back up table pointer</span></td> </tr> <tr id="addr-9EA5"> <td class="addr"><a href="#addr-9EA5">9EA5</a></td> <td><span class="label">.skip_to_end_of_name<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9EA9">← 9EA9 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INX</span> <span class="comment">; Skip to next table entry</span></td> </tr> <tr id="addr-9EA6"> <td class="addr"><a href="#addr-9EA6">9EA6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-9EE3" data-tip="&9EE3 – Star command name and dispatch table">tbl_commands</a>,x</span> <span class="comment">; Read table byte</span></td> </tr> <tr id="addr-9EA9"> <td class="addr"><a href="#addr-9EA9">9EA9</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-9EA5" data-tip="&9EA5">skip_to_end_of_name</a></span> <span class="comment">; Loop until bit 7 set (end marker)</span></td> </tr> <tr id="addr-9EAB"> <td class="addr"><a href="#addr-9EAB">9EAB</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Check if input has abbreviation dot</span></td> </tr> <tr id="addr-9EAD"> <td class="addr"><a href="#addr-9EAD">9EAD</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="46 &2E %00101110 '.'">#&2e</span></span> <span class="comment">; Is it a dot?</span></td> </tr> <tr id="addr-9EAF"> <td class="addr"><a href="#addr-9EAF">9EAF</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-9E8F" data-tip="&9E8F">next_command_entry</a></span> <span class="comment">; No, try next command</span></td> </tr> <tr id="addr-9EB1"> <td class="addr"><a href="#addr-9EB1">9EB1</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Skip past the dot</span></td> </tr> <tr id="addr-9EB2"> <td class="addr"><a href="#addr-9EB2">9EB2</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-9EC3" data-tip="&9EC3">advance_past_command</a></span> <span class="comment">; Always branch (Y != 0)</span></td> </tr> <tr id="addr-9EB4"> <td class="addr"><a href="#addr-9EB4">9EB4</a></td> <td><span class="label">.end_of_table_name<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9E98">← 9E98 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Y=0: no chars matched at all?</span></td> </tr> <tr id="addr-9EB5"> <td class="addr"><a href="#addr-9EB5">9EB5</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-9EDA" data-tip="&9EDA">dispatch_command</a></span> <span class="comment">; Yes, unknown command</span></td> </tr> <tr id="addr-9EB7"> <td class="addr"><a href="#addr-9EB7">9EB7</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Check if next input char is alpha</span></td> </tr> <tr id="addr-9EB9"> <td class="addr"><a href="#addr-9EB9">9EB9</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="95 &5F %01011111 '_'">#&5f</span></span> <span class="comment">; Mask to uppercase</span></td> </tr> <tr id="addr-9EBB"> <td class="addr"><a href="#addr-9EBB">9EBB</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="65 &41 %01000001 'A'">#&41</span></span> <span class="comment">; Below 'A'? Not alpha, command done</span></td> </tr> <tr id="addr-9EBD"> <td class="addr"><a href="#addr-9EBD">9EBD</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-9EC3" data-tip="&9EC3">advance_past_command</a></span> <span class="comment">; Not alpha: command name complete</span></td> </tr> <tr id="addr-9EBF"> <td class="addr"><a href="#addr-9EBF">9EBF</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="91 &5B %01011011 '['">#&5b</span></span> <span class="comment">; Above 'Z'? Not alpha, command done</span></td> </tr> <tr id="addr-9EC1"> <td class="addr"><a href="#addr-9EC1">9EC1</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-9E8F" data-tip="&9E8F">next_command_entry</a></span> <span class="comment">; Alpha: partial match, try next cmd</span></td> </tr> <tr id="addr-9EC3"> <td class="addr"><a href="#addr-9EC3">9EC3</a></td> <td><span class="label">.advance_past_command<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-9EB2">← 9EB2 BNE</a><a href="#addr-9EBD">← 9EBD BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Advance text pointer past matched chars</span></td> </tr> <tr id="addr-9EC4"> <td class="addr"><a href="#addr-9EC4">9EC4</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for pointer advance</span></td> </tr> <tr id="addr-9EC5"> <td class="addr"><a href="#addr-9EC5">9EC5</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Add matched length to pointer</span></td> </tr> <tr id="addr-9EC7"> <td class="addr"><a href="#addr-9EC7">9EC7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store updated pointer low</span></td> </tr> <tr id="addr-9EC9"> <td class="addr"><a href="#addr-9EC9">9EC9</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-9ECD" data-tip="&9ECD">skip_spaces_before_args</a></span> <span class="comment">; No page crossing</span></td> </tr> <tr id="addr-9ECB"> <td class="addr"><a href="#addr-9ECB">9ECB</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Increment pointer high page</span></td> </tr> <tr id="addr-9ECD"> <td class="addr"><a href="#addr-9ECD">9ECD</a></td> <td><span class="label">.skip_spaces_before_args<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9EC9">← 9EC9 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4CF" data-tip="&A4CF – Skip leading spaces in command argument">skip_spaces</a></span> <span class="comment">; Skip spaces after command</span></td> </tr> <tr id="addr-9ED0"> <td class="addr"><a href="#addr-9ED0">9ED0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save text pointer for command handler</span></td> </tr> <tr id="addr-9ED2"> <td class="addr"><a href="#addr-9ED2">9ED2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D6">wksp_cmd_tail</span></span> <span class="comment">; Save text pointer low for handler</span></td> </tr> <tr id="addr-9ED5"> <td class="addr"><a href="#addr-9ED5">9ED5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Get text pointer high</span></td> </tr> <tr id="addr-9ED7"> <td class="addr"><a href="#addr-9ED7">9ED7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D7">wksp_cmd_tail_hi</span></span> <span class="comment">; Save for handler</span></td> </tr> <tr id="addr-9EDA"> <td class="addr"><a href="#addr-9EDA">9EDA</a></td> <td><span class="label">.dispatch_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9EB5">← 9EB5 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-9EE3" data-tip="&9EE3 – Star command name and dispatch table">tbl_commands</a>,x</span> <span class="comment">; Get dispatch address from table</span></td> </tr> <tr id="addr-9EDD"> <td class="addr"><a href="#addr-9EDD">9EDD</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push high byte</span></td> </tr> <tr id="addr-9EDE"> <td class="addr"><a href="#addr-9EDE">9EDE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&9EE4">l9ee4</span>,x</span> <span class="comment">; Get dispatch low from table+1</span></td> </tr> <tr id="addr-9EE1"> <td class="addr"><a href="#addr-9EE1">9EE1</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push low byte</span></td> </tr> <tr id="addr-9EE2"> <td class="addr"><a href="#addr-9EE2">9EE2</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; RTS-dispatch to command handler</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9EE3"> <td colspan="2"><div class="sub-header"> <h3>Star command name and dispatch table</h3> <div class="sub-desc"><p>Table of ADFS star command names with dispatch addresses. Each entry is: command name bytes (bit 7 set on last), dispatch address high byte, dispatch address low byte, parameter count nibbles.</p> <p>Dispatch uses the RTS trick: the high and low bytes are pushed onto the stack, then RTS pops and adds 1 to form the target address. The stored address is therefore the handler address minus one.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9EE3">9EE3</a></td> <td><span class="label">.tbl_commands<span class="ref-badge">←5</span><span class="ref-popup"><a href="#addr-9E0D">← 9E0D LDA</a><a href="#addr-9E19">← 9E19 LDA</a><a href="#addr-9E95">← 9E95 LDA</a><a href="#addr-9EA6">← 9EA6 LDA</a><a href="#addr-9EDA">← 9EDA LDA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"ACCESS"</span> <span class="comment">; "ACCESS" command name</span></td> </tr> <tr id="addr-9EE9"> <td class="addr"><a href="#addr-9EE9">9EE9</a></td> <td> <span class="directive">EQUB</span> <span data-tip="153 &99 %10011001">>(star_access-1)</span> <span class="comment">; Dispatch hi-1 -> star_access</span></td> </tr> <tr id="addr-9EEA"> <td class="addr"><a href="#addr-9EEA">9EEA</a></td> <td> <span class="directive">EQUB</span> <span data-tip="60 &3C %00111100 '<'"><(star_access-1)</span> <span class="comment">; Dispatch lo-1 -> star_access</span></td> </tr> <tr id="addr-9EEB"> <td class="addr"><a href="#addr-9EEB">9EEB</a></td> <td> <span class="directive">EQUB</span> <span data-tip="22 &16 %00010110 SYN">&16</span> <span class="comment">; Params &16: <List Spec> (L)(W)(R)(E)</span></td> </tr> <tr id="addr-9EEC"> <td class="addr"><a href="#addr-9EEC">9EEC</a></td> <td> <span class="directive">EQUS</span> <span class="string">"BACK"</span> <span class="comment">; "BACK" command name</span></td> </tr> <tr id="addr-9EF0"> <td class="addr"><a href="#addr-9EF0">9EF0</a></td> <td> <span class="directive">EQUB</span> <span data-tip="164 &A4 %10100100">>(star_back-1)</span> <span class="comment">; Dispatch hi-1 -> star_back</span></td> </tr> <tr id="addr-9EF1"> <td class="addr"><a href="#addr-9EF1">9EF1</a></td> <td> <span class="directive">EQUB</span> <span data-tip="150 &96 %10010110"><(star_back-1)</span> <span class="comment">; Dispatch lo-1 -> star_back</span></td> </tr> <tr id="addr-9EF2"> <td class="addr"><a href="#addr-9EF2">9EF2</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9EF3"> <td class="addr"><a href="#addr-9EF3">9EF3</a></td> <td> <span class="directive">EQUS</span> <span class="string">"BYE"</span> <span class="comment">; "BYE" command name</span></td> </tr> <tr id="addr-9EF6"> <td class="addr"><a href="#addr-9EF6">9EF6</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">>(star_bye-1)</span> <span class="comment">; Dispatch hi-1 -> star_bye</span></td> </tr> <tr id="addr-9EF7"> <td class="addr"><a href="#addr-9EF7">9EF7</a></td> <td> <span class="directive">EQUB</span> <span data-tip="194 &C2 %11000010"><(star_bye-1)</span> <span class="comment">; Dispatch lo-1 -> star_bye</span></td> </tr> <tr id="addr-9EF8"> <td class="addr"><a href="#addr-9EF8">9EF8</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9EF9"> <td class="addr"><a href="#addr-9EF9">9EF9</a></td> <td> <span class="directive">EQUS</span> <span class="string">"CDIR"</span> <span class="comment">; "CDIR" command name</span></td> </tr> <tr id="addr-9EFD"> <td class="addr"><a href="#addr-9EFD">9EFD</a></td> <td> <span class="directive">EQUB</span> <span data-tip="149 &95 %10010101">>(star_cdir-1)</span> <span class="comment">; Dispatch hi-1 -> star_cdir</span></td> </tr> <tr id="addr-9EFE"> <td class="addr"><a href="#addr-9EFE">9EFE</a></td> <td> <span class="directive">EQUB</span> <span data-tip="111 &6F %01101111 'o'"><(star_cdir-1)</span> <span class="comment">; Dispatch lo-1 -> star_cdir</span></td> </tr> <tr id="addr-9EFF"> <td class="addr"><a href="#addr-9EFF">9EFF</a></td> <td> <span class="directive">EQUB</span> <span data-tip="32 &20 %00100000 SP">&20</span> <span class="comment">; Params &20: <Ob Spec></span></td> </tr> <tr id="addr-9F00"> <td class="addr"><a href="#addr-9F00">9F00</a></td> <td> <span class="directive">EQUS</span> <span class="string">"CLOSE"</span> <span class="comment">; "CLOSE" command name</span></td> </tr> <tr id="addr-9F05"> <td class="addr"><a href="#addr-9F05">9F05</a></td> <td> <span class="directive">EQUB</span> <span data-tip="177 &B1 %10110001">>(star_close-1)</span> <span class="comment">; Dispatch hi-1 -> star_close</span></td> </tr> <tr id="addr-9F06"> <td class="addr"><a href="#addr-9F06">9F06</a></td> <td> <span class="directive">EQUB</span> <span data-tip="178 &B2 %10110010"><(star_close-1)</span> <span class="comment">; Dispatch lo-1 -> star_close</span></td> </tr> <tr id="addr-9F07"> <td class="addr"><a href="#addr-9F07">9F07</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9F08"> <td class="addr"><a href="#addr-9F08">9F08</a></td> <td> <span class="directive">EQUS</span> <span class="string">"COMPACT"</span> <span class="comment">; "COMPACT" command name</span></td> </tr> <tr id="addr-9F0F"> <td class="addr"><a href="#addr-9F0F">9F0F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="162 &A2 %10100010">>(star_compact-1)</span> <span class="comment">; Dispatch hi-1 -> star_compact</span></td> </tr> <tr id="addr-9F10"> <td class="addr"><a href="#addr-9F10">9F10</a></td> <td> <span class="directive">EQUB</span> <span data-tip="117 &75 %01110101 'u'"><(star_compact-1)</span> <span class="comment">; Dispatch lo-1 -> star_compact</span></td> </tr> <tr id="addr-9F11"> <td class="addr"><a href="#addr-9F11">9F11</a></td> <td> <span class="directive">EQUB</span> <span data-tip="80 &50 %01010000 'P'">&50</span> <span class="comment">; Params &50: <SP> <LP></span></td> </tr> <tr id="addr-9F12"> <td class="addr"><a href="#addr-9F12">9F12</a></td> <td> <span class="directive">EQUS</span> <span class="string">"COPY"</span> <span class="comment">; "COPY" command name</span></td> </tr> <tr id="addr-9F16"> <td class="addr"><a href="#addr-9F16">9F16</a></td> <td> <span class="directive">EQUB</span> <span data-tip="168 &A8 %10101000">>(star_copy-1)</span> <span class="comment">; Dispatch hi-1 -> star_copy</span></td> </tr> <tr id="addr-9F17"> <td class="addr"><a href="#addr-9F17">9F17</a></td> <td> <span class="directive">EQUB</span> <span data-tip="28 &1C %00011100 FS"><(star_copy-1)</span> <span class="comment">; Dispatch lo-1 -> star_copy</span></td> </tr> <tr id="addr-9F18"> <td class="addr"><a href="#addr-9F18">9F18</a></td> <td> <span class="directive">EQUB</span> <span data-tip="19 &13 %00010011 DC3">&13</span> <span class="comment">; Params &13: <List Spec> <<em>Ob Spec</em>></span></td> </tr> <tr id="addr-9F19"> <td class="addr"><a href="#addr-9F19">9F19</a></td> <td> <span class="directive">EQUS</span> <span class="string">"DELETE"</span> <span class="comment">; "DELETE" command name</span></td> </tr> <tr id="addr-9F1F"> <td class="addr"><a href="#addr-9F1F">9F1F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">>(star_delete-1)</span> <span class="comment">; Dispatch hi-1 -> star_delete</span></td> </tr> <tr id="addr-9F20"> <td class="addr"><a href="#addr-9F20">9F20</a></td> <td> <span class="directive">EQUB</span> <span data-tip="186 &BA %10111010"><(star_delete-1)</span> <span class="comment">; Dispatch lo-1 -> star_delete</span></td> </tr> <tr id="addr-9F21"> <td class="addr"><a href="#addr-9F21">9F21</a></td> <td> <span class="directive">EQUB</span> <span data-tip="32 &20 %00100000 SP">&20</span> <span class="comment">; Params &20: <Ob Spec></span></td> </tr> <tr id="addr-9F22"> <td class="addr"><a href="#addr-9F22">9F22</a></td> <td> <span class="directive">EQUS</span> <span class="string">"DESTROY"</span> <span class="comment">; "DESTROY" command name</span></td> </tr> <tr id="addr-9F29"> <td class="addr"><a href="#addr-9F29">9F29</a></td> <td> <span class="directive">EQUB</span> <span data-tip="153 &99 %10011001">>(star_destroy-1)</span> <span class="comment">; Dispatch hi-1 -> star_destroy</span></td> </tr> <tr id="addr-9F2A"> <td class="addr"><a href="#addr-9F2A">9F2A</a></td> <td> <span class="directive">EQUB</span> <span data-tip="229 &E5 %11100101"><(star_destroy-1)</span> <span class="comment">; Dispatch lo-1 -> star_destroy</span></td> </tr> <tr id="addr-9F2B"> <td class="addr"><a href="#addr-9F2B">9F2B</a></td> <td> <span class="directive">EQUB</span> <span data-tip="16 &10 %00010000 DLE">&10</span> <span class="comment">; Params &10: <List Spec></span></td> </tr> <tr id="addr-9F2C"> <td class="addr"><a href="#addr-9F2C">9F2C</a></td> <td> <span class="directive">EQUS</span> <span class="string">"DIR"</span> <span class="comment">; "DIR" command name</span></td> </tr> <tr id="addr-9F2F"> <td class="addr"><a href="#addr-9F2F">9F2F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="149 &95 %10010101">>(star_dir-1)</span> <span class="comment">; Dispatch hi-1 -> star_dir</span></td> </tr> <tr id="addr-9F30"> <td class="addr"><a href="#addr-9F30">9F30</a></td> <td> <span class="directive">EQUB</span> <span data-tip="62 &3E %00111110 '>'"><(star_dir-1)</span> <span class="comment">; Dispatch lo-1 -> star_dir</span></td> </tr> <tr id="addr-9F31"> <td class="addr"><a href="#addr-9F31">9F31</a></td> <td> <span class="directive">EQUB</span> <span data-tip="32 &20 %00100000 SP">&20</span> <span class="comment">; Params &20: <Ob Spec></span></td> </tr> <tr id="addr-9F32"> <td class="addr"><a href="#addr-9F32">9F32</a></td> <td> <span class="directive">EQUS</span> <span class="string">"DISMOUNT"</span> <span class="comment">; "DISMOUNT" command name</span></td> </tr> <tr id="addr-9F3A"> <td class="addr"><a href="#addr-9F3A">9F3A</a></td> <td> <span class="directive">EQUB</span> <span data-tip="161 &A1 %10100001">>(star_dismount-1)</span> <span class="comment">; Dispatch hi-1 -> star_dismount</span></td> </tr> <tr id="addr-9F3B"> <td class="addr"><a href="#addr-9F3B">9F3B</a></td> <td> <span class="directive">EQUB</span> <span data-tip="16 &10 %00010000 DLE"><(star_dismount-1)</span> <span class="comment">; Dispatch lo-1 -> star_dismount</span></td> </tr> <tr id="addr-9F3C"> <td class="addr"><a href="#addr-9F3C">9F3C</a></td> <td> <span class="directive">EQUB</span> <span data-tip="64 &40 %01000000 '@'">&40</span> <span class="comment">; Params &40: (<Drive>)</span></td> </tr> <tr id="addr-9F3D"> <td class="addr"><a href="#addr-9F3D">9F3D</a></td> <td> <span class="directive">EQUS</span> <span class="string">"EX"</span> <span class="comment">; "EX" command name</span></td> </tr> <tr id="addr-9F3F"> <td class="addr"><a href="#addr-9F3F">9F3F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="148 &94 %10010100">>(star_ex-1)</span> <span class="comment">; Dispatch hi-1 -> star_ex</span></td> </tr> <tr id="addr-9F40"> <td class="addr"><a href="#addr-9F40">9F40</a></td> <td> <span class="directive">EQUB</span> <span data-tip="50 &32 %00110010 '2'"><(star_ex-1)</span> <span class="comment">; Dispatch lo-1 -> star_ex</span></td> </tr> <tr id="addr-9F41"> <td class="addr"><a href="#addr-9F41">9F41</a></td> <td> <span class="directive">EQUB</span> <span data-tip="48 &30 %00110000 '0'">&30</span> <span class="comment">; Params &30: <<em>Ob Spec</em>></span></td> </tr> <tr id="addr-9F42"> <td class="addr"><a href="#addr-9F42">9F42</a></td> <td> <span class="directive">EQUS</span> <span class="string">"FREE"</span> <span class="comment">; "FREE" command name</span></td> </tr> <tr id="addr-9F46"> <td class="addr"><a href="#addr-9F46">9F46</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">>(star_free-1)</span> <span class="comment">; Dispatch hi-1 -> star_free</span></td> </tr> <tr id="addr-9F47"> <td class="addr"><a href="#addr-9F47">9F47</a></td> <td> <span class="directive">EQUB</span> <span data-tip="26 &1A %00011010 SUB"><(star_free-1)</span> <span class="comment">; Dispatch lo-1 -> star_free</span></td> </tr> <tr id="addr-9F48"> <td class="addr"><a href="#addr-9F48">9F48</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9F49"> <td class="addr"><a href="#addr-9F49">9F49</a></td> <td> <span class="directive">EQUS</span> <span class="string">"INFO"</span> <span class="comment">; "INFO" command name</span></td> </tr> <tr id="addr-9F4D"> <td class="addr"><a href="#addr-9F4D">9F4D</a></td> <td> <span class="directive">EQUB</span> <span data-tip="148 &94 %10010100">>(star_info-1)</span> <span class="comment">; Dispatch hi-1 -> star_info</span></td> </tr> <tr id="addr-9F4E"> <td class="addr"><a href="#addr-9F4E">9F4E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="230 &E6 %11100110"><(star_info-1)</span> <span class="comment">; Dispatch lo-1 -> star_info</span></td> </tr> <tr id="addr-9F4F"> <td class="addr"><a href="#addr-9F4F">9F4F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="16 &10 %00010000 DLE">&10</span> <span class="comment">; Params &10: <List Spec></span></td> </tr> <tr id="addr-9F50"> <td class="addr"><a href="#addr-9F50">9F50</a></td> <td> <span class="directive">EQUS</span> <span class="string">"LCAT"</span> <span class="comment">; "LCAT" command name</span></td> </tr> <tr id="addr-9F54"> <td class="addr"><a href="#addr-9F54">9F54</a></td> <td> <span class="directive">EQUB</span> <span data-tip="164 &A4 %10100100">>(star_lcat-1)</span> <span class="comment">; Dispatch hi-1 -> star_lcat</span></td> </tr> <tr id="addr-9F55"> <td class="addr"><a href="#addr-9F55">9F55</a></td> <td> <span class="directive">EQUB</span> <span data-tip="126 &7E %01111110 '~'"><(star_lcat-1)</span> <span class="comment">; Dispatch lo-1 -> star_lcat</span></td> </tr> <tr id="addr-9F56"> <td class="addr"><a href="#addr-9F56">9F56</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9F57"> <td class="addr"><a href="#addr-9F57">9F57</a></td> <td> <span class="directive">EQUS</span> <span class="string">"LEX"</span> <span class="comment">; "LEX" command name</span></td> </tr> <tr id="addr-9F5A"> <td class="addr"><a href="#addr-9F5A">9F5A</a></td> <td> <span class="directive">EQUB</span> <span data-tip="164 &A4 %10100100">>(star_lex-1)</span> <span class="comment">; Dispatch hi-1 -> star_lex</span></td> </tr> <tr id="addr-9F5B"> <td class="addr"><a href="#addr-9F5B">9F5B</a></td> <td> <span class="directive">EQUB</span> <span data-tip="138 &8A %10001010"><(star_lex-1)</span> <span class="comment">; Dispatch lo-1 -> star_lex</span></td> </tr> <tr id="addr-9F5C"> <td class="addr"><a href="#addr-9F5C">9F5C</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9F5D"> <td class="addr"><a href="#addr-9F5D">9F5D</a></td> <td> <span class="directive">EQUS</span> <span class="string">"LIB"</span> <span class="comment">; "LIB" command name</span></td> </tr> <tr id="addr-9F60"> <td class="addr"><a href="#addr-9F60">9F60</a></td> <td> <span class="directive">EQUB</span> <span data-tip="164 &A4 %10100100">>(star_lib-1)</span> <span class="comment">; Dispatch hi-1 -> star_lib</span></td> </tr> <tr id="addr-9F61"> <td class="addr"><a href="#addr-9F61">9F61</a></td> <td> <span class="directive">EQUB</span> <span data-tip="67 &43 %01000011 'C'"><(star_lib-1)</span> <span class="comment">; Dispatch lo-1 -> star_lib</span></td> </tr> <tr id="addr-9F62"> <td class="addr"><a href="#addr-9F62">9F62</a></td> <td> <span class="directive">EQUB</span> <span data-tip="48 &30 %00110000 '0'">&30</span> <span class="comment">; Params &30: <<em>Ob Spec</em>></span></td> </tr> <tr id="addr-9F63"> <td class="addr"><a href="#addr-9F63">9F63</a></td> <td> <span class="directive">EQUS</span> <span class="string">"MAP"</span> <span class="comment">; "MAP" command name</span></td> </tr> <tr id="addr-9F66"> <td class="addr"><a href="#addr-9F66">9F66</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">>(star_map-1)</span> <span class="comment">; Dispatch hi-1 -> star_map</span></td> </tr> <tr id="addr-9F67"> <td class="addr"><a href="#addr-9F67">9F67</a></td> <td> <span class="directive">EQUB</span> <span data-tip="73 &49 %01001001 'I'"><(star_map-1)</span> <span class="comment">; Dispatch lo-1 -> star_map</span></td> </tr> <tr id="addr-9F68"> <td class="addr"><a href="#addr-9F68">9F68</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Params &00: (none)</span></td> </tr> <tr id="addr-9F69"> <td class="addr"><a href="#addr-9F69">9F69</a></td> <td> <span class="directive">EQUS</span> <span class="string">"MOUNT"</span> <span class="comment">; "MOUNT" command name</span></td> </tr> <tr id="addr-9F6E"> <td class="addr"><a href="#addr-9F6E">9F6E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="161 &A1 %10100001">>(star_mount-1)</span> <span class="comment">; Dispatch hi-1 -> star_mount</span></td> </tr> <tr id="addr-9F6F"> <td class="addr"><a href="#addr-9F6F">9F6F</a></td> <td> <span class="directive">EQUB</span> <span data-tip="93 &5D %01011101 ']'"><(star_mount-1)</span> <span class="comment">; Dispatch lo-1 -> star_mount</span></td> </tr> <tr id="addr-9F70"> <td class="addr"><a href="#addr-9F70">9F70</a></td> <td> <span class="directive">EQUB</span> <span data-tip="64 &40 %01000000 '@'">&40</span> <span class="comment">; Params &40: (<Drive>)</span></td> </tr> <tr id="addr-9F71"> <td class="addr"><a href="#addr-9F71">9F71</a></td> <td> <span class="directive">EQUS</span> <span class="string">"REMOVE"</span> <span class="comment">; "REMOVE" command name</span></td> </tr> <tr id="addr-9F77"> <td class="addr"><a href="#addr-9F77">9F77</a></td> <td> <span class="directive">EQUB</span> <span data-tip="145 &91 %10010001">>(star_remove-1)</span> <span class="comment">; Dispatch hi-1 -> star_remove</span></td> </tr> <tr id="addr-9F78"> <td class="addr"><a href="#addr-9F78">9F78</a></td> <td> <span class="directive">EQUB</span> <span data-tip="8 &08 %00001000 BS"><(star_remove-1)</span> <span class="comment">; Dispatch lo-1 -> star_remove</span></td> </tr> <tr id="addr-9F79"> <td class="addr"><a href="#addr-9F79">9F79</a></td> <td> <span class="directive">EQUB</span> <span data-tip="32 &20 %00100000 SP">&20</span> <span class="comment">; Params &20: <Ob Spec></span></td> </tr> <tr id="addr-9F7A"> <td class="addr"><a href="#addr-9F7A">9F7A</a></td> <td> <span class="directive">EQUS</span> <span class="string">"RENAME"</span> <span class="comment">; "RENAME" command name</span></td> </tr> <tr id="addr-9F80"> <td class="addr"><a href="#addr-9F80">9F80</a></td> <td> <span class="directive">EQUB</span> <span data-tip="165 &A5 %10100101">>(star_rename-1)</span> <span class="comment">; Dispatch hi-1 -> star_rename</span></td> </tr> <tr id="addr-9F81"> <td class="addr"><a href="#addr-9F81">9F81</a></td> <td> <span class="directive">EQUB</span> <span data-tip="2 &02 %00000010 STX"><(star_rename-1)</span> <span class="comment">; Dispatch lo-1 -> star_rename</span></td> </tr> <tr id="addr-9F82"> <td class="addr"><a href="#addr-9F82">9F82</a></td> <td> <span class="directive">EQUB</span> <span data-tip="34 &22 %00100010 '"'">&22</span> <span class="comment">; Params &22: <Ob Spec> <Ob Spec></span></td> </tr> <tr id="addr-9F83"> <td class="addr"><a href="#addr-9F83">9F83</a></td> <td> <span class="directive">EQUS</span> <span class="string">"TITLE"</span> <span class="comment">; "TITLE" command name</span></td> </tr> <tr id="addr-9F88"> <td class="addr"><a href="#addr-9F88">9F88</a></td> <td> <span class="directive">EQUB</span> <span data-tip="162 &A2 %10100010">>(star_title-1)</span> <span class="comment">; Dispatch hi-1 -> star_title</span></td> </tr> <tr id="addr-9F89"> <td class="addr"><a href="#addr-9F89">9F89</a></td> <td> <span class="directive">EQUB</span> <span data-tip="81 &51 %01010001 'Q'"><(star_title-1)</span> <span class="comment">; Dispatch lo-1 -> star_title</span></td> </tr> <tr id="addr-9F8A"> <td class="addr"><a href="#addr-9F8A">9F8A</a></td> <td> <span class="directive">EQUB</span> <span data-tip="112 &70 %01110000 'p'">&70</span> <span class="comment">; Params &70: <Title></span></td> </tr> <tr id="addr-9F8B"> <td class="addr"><a href="#addr-9F8B">9F8B</a></td> <td> <span class="directive">EQUB</span> <span data-tip="163 &A3 %10100011">HI(star_run-1)</span> <span class="comment">; End: dispatch hi-1 -> star_run</span></td> </tr> <tr id="addr-9F8C"> <td class="addr"><a href="#addr-9F8C">9F8C</a></td> <td> <span class="directive">EQUB</span> <span data-tip="152 &98 %10011000">LO(star_run-1)</span> <span class="comment">; End: dispatch lo-1 -> star_run</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9F8D"> <td colspan="2"><div class="sub-header"> <h3>*HELP parameter format strings</h3> <div class="sub-desc"><p>Seven NUL-terminated strings displayed after command names in the *HELP ADFS listing. Indexed via tbl_help_param_ptrs using nibble pairs from each command's parameter byte. Index 0 points to the NUL at &9FD7 (end of the last string), producing no output for commands with no parameters.</p> <p>1: "<List Spec>" Wildcard file specification 2: "<Ob Spec>" Single object specification 3: "<<em>Ob Spec</em>>" Optional wildcard specification 4: "(<Drive>)" Optional drive number 5: "<SP> <LP>" Start page and length page 6: "(L)(W)(R)(E)" Access attribute flags 7: "<Title>" Directory title string</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9F8D">9F8D</a></td> <td><span class="label">.help_param_list_spec</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"<List Spec>"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 1: file list specification</span></td> </tr> <tr id="addr-9F99"> <td class="addr"><a href="#addr-9F99">9F99</a></td> <td><span class="label">.help_param_ob_spec</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"<Ob Spec>"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 2: object specification</span></td> </tr> <tr id="addr-9FA3"> <td class="addr"><a href="#addr-9FA3">9FA3</a></td> <td><span class="label">.help_param_wild_ob_spec</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"<*Ob Spec*>"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 3: wildcard object specification</span></td> </tr> <tr id="addr-9FAF"> <td class="addr"><a href="#addr-9FAF">9FAF</a></td> <td><span class="label">.help_param_drive</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"(<Drive>)"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 4: optional drive number</span></td> </tr> <tr id="addr-9FB9"> <td class="addr"><a href="#addr-9FB9">9FB9</a></td> <td><span class="label">.help_param_sp_lp</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"<SP> <LP>"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 5: *COMPACT start/length pages</span></td> </tr> <tr id="addr-9FC3"> <td class="addr"><a href="#addr-9FC3">9FC3</a></td> <td><span class="label">.help_param_access</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"(L)(W)(R)(E)"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 6: access attribute flags</span></td> </tr> <tr id="addr-9FD0"> <td class="addr"><a href="#addr-9FD0">9FD0</a></td> <td><span class="label">.help_param_title</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"<Title>"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Index 7: directory title string</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9FD8"> <td colspan="2"><div class="sub-header"> <h3>FSC 7: return ADFS file handle range</h3> <div class="sub-desc"><p>Return the range of file handles used by ADFS. The MOS calls FSC 7 to determine which handles belong to the current filing system. ADFS uses handles &30-&39 (ASCII '0'-'9', 10 channels).</p> </div> <div class="sub-registers"><table> <tr><th rowspan="3">On Exit</th><td>A</td><td>corrupted</td></tr> <tr><td>X</td><td>&30 (lowest handle, ASCII '0')</td></tr> <tr><td>Y</td><td>&39 (highest handle, ASCII '9')</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9FD8">9FD8</a></td> <td><span class="label">.fsc7_read_handle_range</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; X=&30 ('0'): lowest ADFS file handle</span></td> </tr> <tr id="addr-9FDA"> <td class="addr"><a href="#addr-9FDA">9FDA</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="57 &39 %00111001 '9'">#&39</span></span> <span class="comment">; Y=&39 ('9'): highest ADFS file handle</span></td> </tr> <tr id="addr-9FDC"> <td class="addr"><a href="#addr-9FDC">9FDC</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return X=&30, Y=&39 to MOS</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-9FDD"> <td colspan="2"><div class="sub-header"> <h3>FSC 0: *OPT command handler</h3> <div class="sub-desc"><p>Handle the *OPT command. *OPT 1,N controls verbose mode (bit 2 of zp_adfs_flags). *OPT 4,N sets the disc boot option in the free space map.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="2">On Entry</th><td>X</td><td>first *OPT parameter (option number)</td></tr> <tr><td>Y</td><td>second *OPT parameter (value)</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-9FDD">9FDD</a></td> <td><span class="label">.fsc0_star_opt</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Get *OPT first parameter</span></td> </tr> <tr id="addr-9FDF"> <td class="addr"><a href="#addr-9FDF">9FDF</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-9FED" data-tip="&9FED">clear_opt1_verbose</a></span> <span class="comment">; Param=0: *OPT 0 (clear OPT1)</span></td> </tr> <tr id="addr-9FE1"> <td class="addr"><a href="#addr-9FE1">9FE1</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Param-1: check for *OPT 1</span></td> </tr> <tr id="addr-9FE2"> <td class="addr"><a href="#addr-9FE2">9FE2</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-9FF6" data-tip="&9FF6">check_opt4_boot</a></span> <span class="comment">; Not *OPT 1: check *OPT 4</span></td> </tr> <tr id="addr-9FE4"> <td class="addr"><a href="#addr-9FE4">9FE4</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Param=1: check second parameter</span></td> </tr> <tr id="addr-9FE5"> <td class="addr"><a href="#addr-9FE5">9FE5</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-9FED" data-tip="&9FED">clear_opt1_verbose</a></span> <span class="comment">; Second param=0: clear OPT1</span></td> </tr> <tr id="addr-9FE7"> <td class="addr"><a href="#addr-9FE7">9FE7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Get ADFS flags</span></td> </tr> <tr id="addr-9FE9"> <td class="addr"><a href="#addr-9FE9">9FE9</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Set bit 2 (*OPT1 verbose on)</span></td> </tr> <tr id="addr-9FEB"> <td class="addr"><a href="#addr-9FEB">9FEB</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-9FF1" data-tip="&9FF1">store_opt_flags</a></span></td> </tr> <tr id="addr-9FED"> <td class="addr"><a href="#addr-9FED">9FED</a></td> <td><span class="label">.clear_opt1_verbose<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-9FDF">← 9FDF BEQ</a><a href="#addr-9FE5">← 9FE5 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Clear bit 2 (OPT1 verbose off)</span></td> </tr> <tr id="addr-9FEF"> <td class="addr"><a href="#addr-9FEF">9FEF</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="251 &FB %11111011">#&fb</span></span> <span class="comment">; Clear bit 2 (*OPT1 verbose off)</span></td> </tr> <tr id="addr-9FF1"> <td class="addr"><a href="#addr-9FF1">9FF1</a></td> <td><span class="label">.store_opt_flags<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9FEB">← 9FEB BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-9FF3"> <td class="addr"><a href="#addr-9FF3">9FF3</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return</span></td> </tr> <tr id="addr-9FF6"> <td class="addr"><a href="#addr-9FF6">9FF6</a></td> <td><span class="label">.check_opt4_boot<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9FE2">← 9FE2 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Check for *OPT 4 (boot option)</span></td> </tr> <tr id="addr-9FF8"> <td class="addr"><a href="#addr-9FF8">9FF8</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A00A" data-tip="&A00A">bad_opt_error</a></span> <span class="comment">; Not *OPT 4: bad opt error</span></td> </tr> <tr id="addr-9FFA"> <td class="addr"><a href="#addr-9FFA">9FFA</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FEA" data-tip="&8FEA – Validate FSM entry structure and checksums">validate_fsm_checksums</a></span> <span class="comment">; Validate FSM before modification</span></td> </tr> <tr id="addr-9FFD"> <td class="addr"><a href="#addr-9FFD">9FFD</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B4F5" data-tip="&B4F5 – Check disc changed and reload FSM if needed">check_drive_and_reload_fsm</a></span> <span class="comment">; Check for disc change, reload if needed</span></td> </tr> <tr id="addr-A000"> <td class="addr"><a href="#addr-A000">A000</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Get boot option value (second param)</span></td> </tr> <tr id="addr-A002"> <td class="addr"><a href="#addr-A002">A002</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Mask to 2 bits (options 0-3)</span></td> </tr> <tr id="addr-A004"> <td class="addr"><a href="#addr-A004">A004</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0FFD">fsm_s1_boot_option</span></span> <span class="comment">; Store in FSM boot option byte</span></td> </tr> <tr id="addr-A007"> <td class="addr"><a href="#addr-A007">A007</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write directory and FSM to disc</span></td> </tr> <tr id="addr-A00A"> <td class="addr"><a href="#addr-A00A">A00A</a></td> <td><span class="label">.bad_opt_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9FF8">← 9FF8 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-A00D"> <td class="addr"><a href="#addr-A00D">A00D</a></td> <td> <span class="directive">EQUB</span> <span data-tip="203 &CB %11001011">&CB</span> <span class="comment">; Error &CB: Bad opt</span></td> </tr> <tr id="addr-A00E"> <td class="addr"><a href="#addr-A00E">A00E</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Bad opt"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A016"> <td colspan="2"><div class="sub-header"> <h3>Print a space character</h3> <div class="sub-desc"><p>Print a single space (&20) via OSWRCH.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A016">A016</a></td> <td><span class="label">.print_space<span class="ref-badge">←8</span><span class="ref-popup"><a href="#addr-9299">← 9299 JSR</a><a href="#addr-92E3">← 92E3 JSR</a><a href="#addr-92FC">← 92FC JSR</a><a href="#addr-9313">← 9313 JMP</a><a href="#addr-93F5">← 93F5 JSR</a><a href="#addr-952B">← 952B JSR</a><a href="#addr-952E">← 952E JSR</a><a href="#addr-9E25">← 9E25 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; A=&20: space character</span></td> </tr> <tr id="addr-A018"> <td class="addr"><a href="#addr-A018">A018</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><span class="ext-label" data-tip="&FFEE">oswrch</span></span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A01B"> <td colspan="2"><div class="sub-header"> <h3>*FREE command handler</h3> <div class="sub-desc"><p>Display the free space remaining on the current or specified drive, in bytes and as a number of sectors.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A01B">A01B</a></td> <td><span class="label">.star_free</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A1AA" data-tip="&A1AA – Calculate total free space on disc">calc_total_free_space</a></span> <span class="comment">; Calculate total free space</span></td> </tr> <tr id="addr-A01E"> <td class="addr"><a href="#addr-A01E">A01E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A1C6" data-tip="&A1C6 – Print space value in hex and decimal">print_space_value</a></span> <span class="comment">; Print free space with header</span></td> </tr> <tr id="addr-A021"> <td class="addr"><a href="#addr-A021">A021</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span> <span class="comment">; Print "Free" + CR</span></td> </tr> <tr id="addr-A024"> <td class="addr"><a href="#addr-A024">A024</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Free"</span></td> </tr> <tr id="addr-A028"> <td class="addr"><a href="#addr-A028">A028</a></td> <td> <span class="directive">EQUB</span> <span data-tip="141 &8D %10001101">&8D</span> <span class="comment">; CR + bit 7: end of inline string</span></td> </tr> <tr id="addr-A029"> <td class="addr"><a href="#addr-A029">A029</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A1AA" data-tip="&A1AA – Calculate total free space on disc">calc_total_free_space</a></span> <span class="comment">; Calculate total free space again</span></td> </tr> <tr id="addr-A02C"> <td class="addr"><a href="#addr-A02C">A02C</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: offset to disc size low byte</span></td> </tr> <tr id="addr-A02E"> <td class="addr"><a href="#addr-A02E">A02E</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: loop counter for 3-byte subtract</span></td> </tr> <tr id="addr-A030"> <td class="addr"><a href="#addr-A030">A030</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-A031"> <td class="addr"><a href="#addr-A031">A031</a></td> <td><span class="label">.print_used_space<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A03C">← A03C BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0EFB">fsm_s0_pre_disc_size</span>,y</span> <span class="comment">; Get disc size byte (Y-indexed)</span></td> </tr> <tr id="addr-A034"> <td class="addr"><a href="#addr-A034">A034</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,y</span> <span class="comment">; Subtract free space</span></td> </tr> <tr id="addr-A037"> <td class="addr"><a href="#addr-A037">A037</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,y</span> <span class="comment">; Store result (used space)</span></td> </tr> <tr id="addr-A03A"> <td class="addr"><a href="#addr-A03A">A03A</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next FSM byte</span></td> </tr> <tr id="addr-A03B"> <td class="addr"><a href="#addr-A03B">A03B</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement byte counter</span></td> </tr> <tr id="addr-A03C"> <td class="addr"><a href="#addr-A03C">A03C</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A031" data-tip="&A031">print_used_space</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A03E"> <td class="addr"><a href="#addr-A03E">A03E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A1C6" data-tip="&A1C6 – Print space value in hex and decimal">print_space_value</a></span> <span class="comment">; Print used space with header</span></td> </tr> <tr id="addr-A041"> <td class="addr"><a href="#addr-A041">A041</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span> <span class="comment">; Print "Used" + CR</span></td> </tr> <tr id="addr-A044"> <td class="addr"><a href="#addr-A044">A044</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Used"</span></td> </tr> <tr id="addr-A048"> <td class="addr"><a href="#addr-A048">A048</a></td> <td> <span class="directive">EQUB</span> <span data-tip="141 &8D %10001101">&8D</span> <span class="comment">; CR + bit 7: end of inline string</span></td> </tr> <tr id="addr-A049"> <td class="addr"><a href="#addr-A049">A049</a></td> <td><span class="label">.return_27<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-A064">← A064 BEQ</a><a href="#addr-A097">← A097 BNE</a><a href="#addr-A09E">← A09E BCC</a><a href="#addr-A0BE">← A0BE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A04A"> <td colspan="2"><div class="sub-header"> <h3>*MAP command handler</h3> <div class="sub-desc"><p>Display the free space map of the current or specified drive, showing the address and length of each free space region.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A04A">A04A</a></td> <td><span class="label">.star_map</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span> <span class="comment">; Print "Address : Length" + CR header</span></td> </tr> <tr id="addr-A04D"> <td class="addr"><a href="#addr-A04D">A04D</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Address : Length"</span></td> </tr> <tr id="addr-A05E"> <td class="addr"><a href="#addr-A05E">A05E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="141 &8D %10001101">&8D</span> <span class="comment">; CR + bit 7: end of inline string</span></td> </tr> <tr id="addr-A05F"> <td class="addr"><a href="#addr-A05F">A05F</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: start of FSM entries</span></td> </tr> <tr id="addr-A061"> <td class="addr"><a href="#addr-A061">A061</a></td> <td><span class="label">.print_map_header<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A092">← A092 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="ext-label" data-tip="&0FFE">fsm_s1_end_of_list_ptr</span></span> <span class="comment">; Past end of free space list?</span></td> </tr> <tr id="addr-A064"> <td class="addr"><a href="#addr-A064">A064</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A049" data-tip="&A049">return_27</a></span> <span class="comment">; Yes, done</span></td> </tr> <tr id="addr-A066"> <td class="addr"><a href="#addr-A066">A066</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Advance X to entry+3</span></td> </tr> <tr id="addr-A067"> <td class="addr"><a href="#addr-A067">A067</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Advance X: 2nd byte of 3-byte entry</span></td> </tr> <tr id="addr-A068"> <td class="addr"><a href="#addr-A068">A068</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Advance X: 3rd byte of 3-byte entry</span></td> </tr> <tr id="addr-A069"> <td class="addr"><a href="#addr-A069">A069</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span></span> <span class="comment">; Save FSM index for next iteration</span></td> </tr> <tr id="addr-A06B"> <td class="addr"><a href="#addr-A06B">A06B</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: print 3 address bytes</span></td> </tr> <tr id="addr-A06D"> <td class="addr"><a href="#addr-A06D">A06D</a></td> <td><span class="label">.print_fsm_entries_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A075">← A075 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Back up to previous byte</span></td> </tr> <tr id="addr-A06E"> <td class="addr"><a href="#addr-A06E">A06E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0E00">fsm_sector_0</span>,x</span> <span class="comment">; Get address byte from FSM sector 0</span></td> </tr> <tr id="addr-A071"> <td class="addr"><a href="#addr-A071">A071</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-931B" data-tip="&931B – Print a byte as two hex digits">print_hex_byte</a></span> <span class="comment">; Print as 2 hex digits</span></td> </tr> <tr id="addr-A074"> <td class="addr"><a href="#addr-A074">A074</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A075"> <td class="addr"><a href="#addr-A075">A075</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A06D" data-tip="&A06D">print_fsm_entries_loop</a></span> <span class="comment">; Loop for 3 bytes (high to low)</span></td> </tr> <tr id="addr-A077"> <td class="addr"><a href="#addr-A077">A077</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span> <span class="comment">; Print " : " separator</span></td> </tr> <tr id="addr-A07A"> <td class="addr"><a href="#addr-A07A">A07A</a></td> <td> <span class="directive">EQUS</span> <span class="string">" : "</span></td> </tr> <tr id="addr-A07E"> <td class="addr"><a href="#addr-A07E">A07E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">&A0</span> <span class="comment">; ' ' + bit 7: end of inline string</span></td> </tr> <tr id="addr-A07F"> <td class="addr"><a href="#addr-A07F">A07F</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span></span> <span class="comment">; Restore FSM index</span></td> </tr> <tr id="addr-A081"> <td class="addr"><a href="#addr-A081">A081</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: print 3 length bytes</span></td> </tr> <tr id="addr-A083"> <td class="addr"><a href="#addr-A083">A083</a></td> <td><span class="label">.print_entry_hex_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A08B">← A08B BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Back up to previous byte</span></td> </tr> <tr id="addr-A084"> <td class="addr"><a href="#addr-A084">A084</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0F00">fsm_sector_1</span>,x</span> <span class="comment">; Get length byte from FSM sector 1</span></td> </tr> <tr id="addr-A087"> <td class="addr"><a href="#addr-A087">A087</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-931B" data-tip="&931B – Print a byte as two hex digits">print_hex_byte</a></span> <span class="comment">; Print as 2 hex digits</span></td> </tr> <tr id="addr-A08A"> <td class="addr"><a href="#addr-A08A">A08A</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A08B"> <td class="addr"><a href="#addr-A08B">A08B</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A083" data-tip="&A083">print_entry_hex_loop</a></span> <span class="comment">; Loop for 3 bytes (high to low)</span></td> </tr> <tr id="addr-A08D"> <td class="addr"><a href="#addr-A08D">A08D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFE7">osnewl</span></span> <span class="comment">; Print newline after each entry</span></td> </tr> <tr id="addr-A090"> <td class="addr"><a href="#addr-A090">A090</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span></span> <span class="comment">; Restore FSM index for next entry</span></td> </tr> <tr id="addr-A092"> <td class="addr"><a href="#addr-A092">A092</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A061" data-tip="&A061">print_map_header</a></span> <span class="comment">; Loop if more entries</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A094"> <td colspan="2"><div class="sub-header"> <h3>Check if disc compaction is recommended</h3> <div class="sub-desc"><p>After *MAP output, check if the FSM has become fragmented enough to recommend compaction. Prints a recommendation message if the free space list pointer exceeds &E1.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A094">A094</a></td> <td><span class="label">.check_compaction_recommended</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&10D8">wksp_compaction_reported</span></span> <span class="comment">; Check if already reported</span></td> </tr> <tr id="addr-A097"> <td class="addr"><a href="#addr-A097">A097</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A049" data-tip="&A049">return_27</a></span> <span class="comment">; Already done, skip</span></td> </tr> <tr id="addr-A099"> <td class="addr"><a href="#addr-A099">A099</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&0FFE">fsm_s1_end_of_list_ptr</span></span> <span class="comment">; Get FSM end-of-list pointer</span></td> </tr> <tr id="addr-A09C"> <td class="addr"><a href="#addr-A09C">A09C</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="225 &E1 %11100001">#&e1</span></span> <span class="comment">; Pointer >= &E1 (many fragments)?</span></td> </tr> <tr id="addr-A09E"> <td class="addr"><a href="#addr-A09E">A09E</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A049" data-tip="&A049">return_27</a></span> <span class="comment">; No, space not fragmented enough</span></td> </tr> <tr id="addr-A0A0"> <td class="addr"><a href="#addr-A0A0">A0A0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span> <span class="comment">; Print "Compaction recommended" + CR</span></td> </tr> <tr id="addr-A0A3"> <td class="addr"><a href="#addr-A0A3">A0A3</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Compaction recommended"</span></td> </tr> <tr id="addr-A0B9"> <td class="addr"><a href="#addr-A0B9">A0B9</a></td> <td> <span class="directive">EQUB</span> <span data-tip="141 &8D %10001101">&8D</span> <span class="comment">; CR + bit 7: end of inline string</span></td> </tr> <tr id="addr-A0BA"> <td class="addr"><a href="#addr-A0BA">A0BA</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return to caller</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A0BB"> <td colspan="2"><div class="sub-header"> <h3>*DELETE command handler</h3> <div class="sub-desc"><p>Delete a file from the current directory. Reports an error if the file is locked.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A0BB">A0BB</a></td> <td><span class="label">.star_delete</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-9109" data-tip="&9109 – *REMOVE command handler">star_remove</a></span> <span class="comment">; Try to remove the file</span></td> </tr> <tr id="addr-A0BE"> <td class="addr"><a href="#addr-A0BE">A0BE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A049" data-tip="&A049">return_27</a></span> <span class="comment">; Not found? Just return</span></td> </tr> <tr id="addr-A0C0"> <td class="addr"><a href="#addr-A0C0">A0C0</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8BD7" data-tip="&8BD7">bad_parms_error</a></span> <span class="comment">; Found: delete from directory</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A0C3"> <td colspan="2"><div class="sub-header"> <h3>*BYE command handler</h3> <div class="sub-desc"><p>Close all open files and dismount all drives. Equivalent to *CLOSE followed by *DISMOUNT for all drives.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A0C3">A0C3</a></td> <td><span class="label">.star_bye</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Save current drive for restore</span></td> </tr> <tr id="addr-A0C6"> <td class="addr"><a href="#addr-A0C6">A0C6</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save current drive on stack</span></td> </tr> <tr id="addr-A0C7"> <td class="addr"><a href="#addr-A0C7">A0C7</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X to check for &FF</span></td> </tr> <tr id="addr-A0C8"> <td class="addr"><a href="#addr-A0C8">A0C8</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Drive &FF = uninitialised?</span></td> </tr> <tr id="addr-A0C9"> <td class="addr"><a href="#addr-A0C9">A0C9</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A0CE" data-tip="&A0CE">close_all_drives_start</a></span> <span class="comment">; Yes, skip close</span></td> </tr> <tr id="addr-A0CB"> <td class="addr"><a href="#addr-A0CB">A0CB</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B1B3" data-tip="&B1B3 – *CLOSE command handler">star_close</a></span> <span class="comment">; Close all open files</span></td> </tr> <tr id="addr-A0CE"> <td class="addr"><a href="#addr-A0CE">A0CE</a></td> <td><span class="label">.close_all_drives_start<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A0C9">← A0C9 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="96 &60 %01100000 '`'">#&60</span></span> <span class="comment">; Start with drive 3 (ID = &60)</span></td> </tr> <tr id="addr-A0D0"> <td class="addr"><a href="#addr-A0D0">A0D0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set as current drive</span></td> </tr> <tr id="addr-A0D3"> <td class="addr"><a href="#addr-A0D3">A0D3</a></td> <td><span class="label">.close_each_drive_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A0E3">← A0E3 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="234 &EA %11101010">#&ea</span></span> <span class="comment">; X=&EA: scsi_cmd_park control block low</span></td> </tr> <tr id="addr-A0D5"> <td class="addr"><a href="#addr-A0D5">A0D5</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="160 &A0 %10100000">#&a0</span></span> <span class="comment">; Y=&A0: scsi_cmd_park control block high</span></td> </tr> <tr id="addr-A0D7"> <td class="addr"><a href="#addr-A0D7">A0D7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8089" data-tip="&8089 – Execute disc command with control block at (X,Y)">command_exec_xy</a></span> <span class="comment">; Park heads on this drive</span></td> </tr> <tr id="addr-A0DA"> <td class="addr"><a href="#addr-A0DA">A0DA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive ID</span></td> </tr> <tr id="addr-A0DD"> <td class="addr"><a href="#addr-A0DD">A0DD</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-A0DE"> <td class="addr"><a href="#addr-A0DE">A0DE</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Next drive (subtract &20)</span></td> </tr> <tr id="addr-A0E0"> <td class="addr"><a href="#addr-A0E0">A0E0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Store updated drive ID</span></td> </tr> <tr id="addr-A0E3"> <td class="addr"><a href="#addr-A0E3">A0E3</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A0D3" data-tip="&A0D3">close_each_drive_loop</a></span> <span class="comment">; Loop while drive ID >= 0</span></td> </tr> <tr id="addr-A0E5"> <td class="addr"><a href="#addr-A0E5">A0E5</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore original drive</span></td> </tr> <tr id="addr-A0E6"> <td class="addr"><a href="#addr-A0E6">A0E6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Store back as current drive</span></td> </tr> <tr id="addr-A0E9"> <td class="addr"><a href="#addr-A0E9">A0E9</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A0EA"> <td colspan="2"><div class="sub-header"> <h3>SCSI park heads disc operation control block</h3> <div class="sub-desc"><p>Disc operation control block used by *BYE to park the hard drive heads on shutdown. Referenced indirectly as X=&EA, Y=&A0 from the close_each_drive_loop. Issues SCSI command &1B (Start/Stop Unit) with count=0 (stop/park). The companion block at scsi_cmd_unpark (&A19F) has count=1 (start/unpark) and is used by *MOUNT.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A0EA">A0EA</a></td> <td><span class="label">.scsi_cmd_park</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Result: &00</span></td> </tr> <tr id="addr-A0EB"> <td class="addr"><a href="#addr-A0EB">A0EB</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Memory address low: &00</span></td> </tr> <tr id="addr-A0EC"> <td class="addr"><a href="#addr-A0EC">A0EC</a></td> <td> <span class="directive">EQUB</span> <span data-tip="23 &17 %00010111 ETB">&17</span> <span class="comment">; Memory address high: &17 (buffer page)</span></td> </tr> <tr id="addr-A0ED"> <td class="addr"><a href="#addr-A0ED">A0ED</a></td> <td> <span class="directive">EQUB</span> <span data-tip="255 &FF %11111111">&FF</span> <span class="comment">; Memory address byte 3: &FF (host memory)</span></td> </tr> <tr id="addr-A0EE"> <td class="addr"><a href="#addr-A0EE">A0EE</a></td> <td> <span class="directive">EQUB</span> <span data-tip="255 &FF %11111111">&FF</span> <span class="comment">; Memory address byte 4: &FF (host memory)</span></td> </tr> <tr id="addr-A0EF"> <td class="addr"><a href="#addr-A0EF">A0EF</a></td> <td> <span class="directive">EQUB</span> <span data-tip="27 &1B %00011011 ESC">&1B</span> <span class="comment">; Command: &1B (SCSI Start/Stop Unit)</span></td> </tr> <tr id="addr-A0F0"> <td class="addr"><a href="#addr-A0F0">A0F0</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector high: &00</span></td> </tr> <tr id="addr-A0F1"> <td class="addr"><a href="#addr-A0F1">A0F1</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector mid: &00</span></td> </tr> <tr id="addr-A0F2"> <td class="addr"><a href="#addr-A0F2">A0F2</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector low: &00</span></td> </tr> <tr id="addr-A0F3"> <td class="addr"><a href="#addr-A0F3">A0F3</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector count: &00 (stop/park heads)</span></td> </tr> <tr id="addr-A0F4"> <td class="addr"><a href="#addr-A0F4">A0F4</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Control: &00</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A0F5"> <td colspan="2"><div class="sub-header"> <h3>Parse optional drive number argument</h3> <div class="sub-desc"><p>Parse an optional drive number from the command line for commands like *DISMOUNT, *MOUNT, *FREE, *MAP. If no argument given, uses the current drive.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A0F5">A0F5</a></td> <td><span class="label">.parse_drive_argument<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A111">← A111 JSR</a><a href="#addr-A15E">← A15E JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4CF" data-tip="&A4CF – Skip leading spaces in command argument">skip_spaces</a></span> <span class="comment">; Skip leading spaces</span></td> </tr> <tr id="addr-A0F8"> <td class="addr"><a href="#addr-A0F8">A0F8</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive</span></td> </tr> <tr id="addr-A0FB"> <td class="addr"><a href="#addr-A0FB">A0FB</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Drive uninitialised (&FF)?</span></td> </tr> <tr id="addr-A0FC"> <td class="addr"><a href="#addr-A0FC">A0FC</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A0FF" data-tip="&A0FF">store_default_drive</a></span> <span class="comment">; Yes, use 0 instead</span></td> </tr> <tr id="addr-A0FE"> <td class="addr"><a href="#addr-A0FE">A0FE</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement Y (was INY+1)</span></td> </tr> <tr id="addr-A0FF"> <td class="addr"><a href="#addr-A0FF">A0FF</a></td> <td><span class="label">.store_default_drive<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A0FC">← A0FC BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Store default drive number</span></td> </tr> <tr id="addr-A102"> <td class="addr"><a href="#addr-A102">A102</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check for argument</span></td> </tr> <tr id="addr-A104"> <td class="addr"><a href="#addr-A104">A104</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get first argument char</span></td> </tr> <tr id="addr-A106"> <td class="addr"><a href="#addr-A106">A106</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Is it a printable char?</span></td> </tr> <tr id="addr-A108"> <td class="addr"><a href="#addr-A108">A108</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A110" data-tip="&A110">return_28</a></span> <span class="comment">; No argument: use default drive</span></td> </tr> <tr id="addr-A10A"> <td class="addr"><a href="#addr-A10A">A10A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8822" data-tip="&8822 – Parse drive number from ASCII character">parse_drive_from_ascii</a></span> <span class="comment">; Parse drive number from argument</span></td> </tr> <tr id="addr-A10D"> <td class="addr"><a href="#addr-A10D">A10D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Store parsed drive number</span></td> </tr> <tr id="addr-A110"> <td class="addr"><a href="#addr-A110">A110</a></td> <td><span class="label">.return_28<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A108">← A108 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A111"> <td colspan="2"><div class="sub-header"> <h3>*DISMOUNT command handler</h3> <div class="sub-desc"><p>Close all open files on the specified drive and mark the drive as not mounted.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A111">A111</a></td> <td><span class="label">.star_dismount</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A0F5" data-tip="&A0F5 – Parse optional drive number argument">parse_drive_argument</a></span> <span class="comment">; Parse drive number argument</span></td> </tr> <tr id="addr-A114"> <td class="addr"><a href="#addr-A114">A114</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: check all 10 channels</span></td> </tr> <tr id="addr-A116"> <td class="addr"><a href="#addr-A116">A116</a></td> <td><span class="label">.close_drive_channels_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A130">← A130 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-A119"> <td class="addr"><a href="#addr-A119">A119</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A12F" data-tip="&A12F">check_csd_on_drive</a></span> <span class="comment">; Channel not open? Skip</span></td> </tr> <tr id="addr-A11B"> <td class="addr"><a href="#addr-A11B">A11B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel's drive number</span></td> </tr> <tr id="addr-A11E"> <td class="addr"><a href="#addr-A11E">A11E</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="224 &E0 %11100000">#&e0</span></span> <span class="comment">; Isolate drive bits</span></td> </tr> <tr id="addr-A120"> <td class="addr"><a href="#addr-A120">A120</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Compare with target drive</span></td> </tr> <tr id="addr-A123"> <td class="addr"><a href="#addr-A123">A123</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A12F" data-tip="&A12F">check_csd_on_drive</a></span> <span class="comment">; Different drive? Skip</span></td> </tr> <tr id="addr-A125"> <td class="addr"><a href="#addr-A125">A125</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-A126"> <td class="addr"><a href="#addr-A126">A126</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Channel index to A</span></td> </tr> <tr id="addr-A127"> <td class="addr"><a href="#addr-A127">A127</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Add &30 to get file handle</span></td> </tr> <tr id="addr-A129"> <td class="addr"><a href="#addr-A129">A129</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer to Y for OSFIND</span></td> </tr> <tr id="addr-A12A"> <td class="addr"><a href="#addr-A12A">A12A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: close file</span></td> </tr> <tr id="addr-A12C"> <td class="addr"><a href="#addr-A12C">A12C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B1B6" data-tip="&B1B6 – OSFIND handler">osfind_handler</a></span> <span class="comment">; Close this file</span></td> </tr> <tr id="addr-A12F"> <td class="addr"><a href="#addr-A12F">A12F</a></td> <td><span class="label">.check_csd_on_drive<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A119">← A119 BEQ</a><a href="#addr-A123">← A123 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next channel</span></td> </tr> <tr id="addr-A130"> <td class="addr"><a href="#addr-A130">A130</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A116" data-tip="&A116">close_drive_channels_loop</a></span> <span class="comment">; Loop for all 10 channels</span></td> </tr> <tr id="addr-A132"> <td class="addr"><a href="#addr-A132">A132</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Is dismounted drive the CSD drive?</span></td> </tr> <tr id="addr-A135"> <td class="addr"><a href="#addr-A135">A135</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Compare with target drive</span></td> </tr> <tr id="addr-A138"> <td class="addr"><a href="#addr-A138">A138</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A179" data-tip="&A179">mount_read_root_dir</a></span> <span class="comment">; Different drive: CSD unaffected</span></td> </tr> <tr id="addr-A13A"> <td class="addr"><a href="#addr-A13A">A13A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Mark current drive as uninitialised</span></td> </tr> <tr id="addr-A13C"> <td class="addr"><a href="#addr-A13C">A13C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set CSD drive to &FF (unset)</span></td> </tr> <tr id="addr-A13F"> <td class="addr"><a href="#addr-A13F">A13F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1116">wksp_csd_sector_hi</span></span> <span class="comment">; Invalidate drive status</span></td> </tr> <tr id="addr-A142"> <td class="addr"><a href="#addr-A142">A142</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: reset CSD name to "Unset"</span></td> </tr> <tr id="addr-A144"> <td class="addr"><a href="#addr-A144">A144</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A149" data-tip="&A149 – Copy default directory name to workspace">copy_default_dir_name</a></span> <span class="comment">; Copy default name to CSD workspace</span></td> </tr> <tr id="addr-A147"> <td class="addr"><a href="#addr-A147">A147</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-A179" data-tip="&A179">mount_read_root_dir</a></span> <span class="comment">; Always branch to exit code</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A149"> <td colspan="2"><div class="sub-header"> <h3>Copy default directory name to workspace</h3> <div class="sub-desc"><p>Copy the reversed string 'Unset' (with quotes and CR padding) to the CSD or library name workspace at &1100+X. Used when dismounting or initialising to set the directory name to the default 'Unset' value.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>X</td><td>workspace offset (0 for CSD, 10 for library)</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A149">A149</a></td> <td><span class="label">.copy_default_dir_name<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-8483">← 8483 JSR</a><a href="#addr-8486">← 8486 JSR</a><a href="#addr-A144">← A144 JSR</a><a href="#addr-A19B">← A19B JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy 10 bytes</span></td> </tr> <tr id="addr-A14B"> <td class="addr"><a href="#addr-A14B">A14B</a></td> <td><span class="label">.copy_default_name_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A153">← A153 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&A154">la154</span>,y</span> <span class="comment">; Get byte from reversed name table</span></td> </tr> <tr id="addr-A14E"> <td class="addr"><a href="#addr-A14E">A14E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1100">wksp_csd_name</span>,x</span> <span class="comment">; Store in CSD/lib name workspace</span></td> </tr> <tr id="addr-A151"> <td class="addr"><a href="#addr-A151">A151</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Next workspace byte</span></td> </tr> <tr id="addr-A152"> <td class="addr"><a href="#addr-A152">A152</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next table byte (backwards)</span></td> </tr> <tr id="addr-A153"> <td class="addr"><a href="#addr-A153">A153</a></td> <td><span class="label">.sub_ca153</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A14B" data-tip="&A14B">copy_default_name_loop</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A155"> <td class="addr"><a href="#addr-A155">A155</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-A156"> <td class="addr"><a href="#addr-A156">A156</a></td> <td> <span class="directive">EQUB</span> <span data-tip="13 &0D %00001101 CR">&0D</span> <span class="comment">; CR (read backwards as name terminator)</span></td> </tr> <tr id="addr-A157"> <td class="addr"><a href="#addr-A157">A157</a></td> <td> <span class="directive">EQUS</span> <span data-tip="34 &22 %00100010 '"'">&22</span>, <span class="string">"tesnU"</span>, <span data-tip="34 &22 %00100010 '"'">&22</span> <span class="comment">; Reversed: '"Unset"' default dir name</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A15E"> <td colspan="2"><div class="sub-header"> <h3>*MOUNT command handler</h3> <div class="sub-desc"><p>Mount a drive by loading its free space map and root directory into memory.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A15E">A15E</a></td> <td><span class="label">.star_mount</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A0F5" data-tip="&A0F5 – Parse optional drive number argument">parse_drive_argument</a></span> <span class="comment">; Parse drive number argument</span></td> </tr> <tr id="addr-A161"> <td class="addr"><a href="#addr-A161">A161</a></td> <td><span class="label">.mount_drive_setup<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9C94">← 9C94 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Get drive number to mount</span></td> </tr> <tr id="addr-A164"> <td class="addr"><a href="#addr-A164">A164</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set as current drive</span></td> </tr> <tr id="addr-A167"> <td class="addr"><a href="#addr-A167">A167</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="159 &9F %10011111">#&9f</span></span> <span class="comment">; Point to unpark command block</span></td> </tr> <tr id="addr-A169"> <td class="addr"><a href="#addr-A169">A169</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="161 &A1 %10100001">#&a1</span></span> <span class="comment">; Y=&A1: control block page</span></td> </tr> <tr id="addr-A16B"> <td class="addr"><a href="#addr-A16B">A16B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8089" data-tip="&8089 – Execute disc command with control block at (X,Y)">command_exec_xy</a></span> <span class="comment">; Send unpark command to drive</span></td> </tr> <tr id="addr-A16E"> <td class="addr"><a href="#addr-A16E">A16E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="170 &AA %10101010">#&aa</span></span> <span class="comment">; Point to root directory path</span></td> </tr> <tr id="addr-A170"> <td class="addr"><a href="#addr-A170">A170</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Point to root dir path '$'</span></td> </tr> <tr id="addr-A172"> <td class="addr"><a href="#addr-A172">A172</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="162 &A2 %10100010">#&a2</span></span> <span class="comment">; Path string is in this page</span></td> </tr> <tr id="addr-A174"> <td class="addr"><a href="#addr-A174">A174</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store path high byte</span></td> </tr> <tr id="addr-A176"> <td class="addr"><a href="#addr-A176">A176</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-953F" data-tip="&953F – *DIR command handler">star_dir</a></span> <span class="comment">; Set root as CSD via *DIR</span></td> </tr> <tr id="addr-A179"> <td class="addr"><a href="#addr-A179">A179</a></td> <td><span class="label">.mount_read_root_dir<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A138">← A138 BNE</a><a href="#addr-A147">← A147 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&111F">wksp_prev_dir_sector_hi</span></span> <span class="comment">; Check if previous dir is on drive</span></td> </tr> <tr id="addr-A17C"> <td class="addr"><a href="#addr-A17C">A17C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Compare with target drive</span></td> </tr> <tr id="addr-A17F"> <td class="addr"><a href="#addr-A17F">A17F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A189" data-tip="&A189">mount_set_boot_option</a></span> <span class="comment">; Different drive, leave previous</span></td> </tr> <tr id="addr-A181"> <td class="addr"><a href="#addr-A181">A181</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Invalidate previous directory</span></td> </tr> <tr id="addr-A183"> <td class="addr"><a href="#addr-A183">A183</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&111E">wksp_prev_dir_sector_mid</span></span> <span class="comment">; Invalidate prev dir high byte</span></td> </tr> <tr id="addr-A186"> <td class="addr"><a href="#addr-A186">A186</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&111F">wksp_prev_dir_sector_hi</span></span> <span class="comment">; Invalidate prev dir drive byte</span></td> </tr> <tr id="addr-A189"> <td class="addr"><a href="#addr-A189">A189</a></td> <td><span class="label">.mount_set_boot_option<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A17F">← A17F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&111B">wksp_lib_sector_hi</span></span> <span class="comment">; Check if library is on this drive</span></td> </tr> <tr id="addr-A18C"> <td class="addr"><a href="#addr-A18C">A18C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Compare with target drive</span></td> </tr> <tr id="addr-A18F"> <td class="addr"><a href="#addr-A18F">A18F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A19E" data-tip="&A19E">return_29</a></span> <span class="comment">; Different drive, leave library</span></td> </tr> <tr id="addr-A191"> <td class="addr"><a href="#addr-A191">A191</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Invalidate library sector</span></td> </tr> <tr id="addr-A193"> <td class="addr"><a href="#addr-A193">A193</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&111A">wksp_lib_sector_mid</span></span> <span class="comment">; Invalidate lib sector high</span></td> </tr> <tr id="addr-A196"> <td class="addr"><a href="#addr-A196">A196</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&111B">wksp_lib_sector_hi</span></span> <span class="comment">; Invalidate lib drive byte</span></td> </tr> <tr id="addr-A199"> <td class="addr"><a href="#addr-A199">A199</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; X=&0A: reset lib name to "Unset"</span></td> </tr> <tr id="addr-A19B"> <td class="addr"><a href="#addr-A19B">A19B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A149" data-tip="&A149 – Copy default directory name to workspace">copy_default_dir_name</a></span> <span class="comment">; Copy default name to library</span></td> </tr> <tr id="addr-A19E"> <td class="addr"><a href="#addr-A19E">A19E</a></td> <td><span class="label">.return_29<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A18F">← A18F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A19F"> <td colspan="2"><div class="sub-header"> <h3>SCSI unpark heads disc operation control block</h3> <div class="sub-desc"><p>Disc operation control block used by *MOUNT to unpark (spin up) the hard drive heads. Referenced indirectly as X=&9F, Y=&A1 from star_mount. Issues SCSI command &1B (Start/Stop Unit) with count=1 (start/unpark). The companion block at scsi_cmd_park (&A0EA) has count=0 (stop/park) and is used by *BYE.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A19F">A19F</a></td> <td><span class="label">.scsi_cmd_unpark</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Result: &00</span></td> </tr> <tr id="addr-A1A0"> <td class="addr"><a href="#addr-A1A0">A1A0</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Memory address low: &00</span></td> </tr> <tr id="addr-A1A1"> <td class="addr"><a href="#addr-A1A1">A1A1</a></td> <td> <span class="directive">EQUB</span> <span data-tip="23 &17 %00010111 ETB">&17</span> <span class="comment">; Memory address high: &17 (buffer page)</span></td> </tr> <tr id="addr-A1A2"> <td class="addr"><a href="#addr-A1A2">A1A2</a></td> <td> <span class="directive">EQUB</span> <span data-tip="255 &FF %11111111">&FF</span> <span class="comment">; Memory address byte 3: &FF (host memory)</span></td> </tr> <tr id="addr-A1A3"> <td class="addr"><a href="#addr-A1A3">A1A3</a></td> <td> <span class="directive">EQUB</span> <span data-tip="255 &FF %11111111">&FF</span> <span class="comment">; Memory address byte 4: &FF (host memory)</span></td> </tr> <tr id="addr-A1A4"> <td class="addr"><a href="#addr-A1A4">A1A4</a></td> <td> <span class="directive">EQUB</span> <span data-tip="27 &1B %00011011 ESC">&1B</span> <span class="comment">; Command: &1B (SCSI Start/Stop Unit)</span></td> </tr> <tr id="addr-A1A5"> <td class="addr"><a href="#addr-A1A5">A1A5</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector high: &00</span></td> </tr> <tr id="addr-A1A6"> <td class="addr"><a href="#addr-A1A6">A1A6</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector mid: &00</span></td> </tr> <tr id="addr-A1A7"> <td class="addr"><a href="#addr-A1A7">A1A7</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Sector low: &00</span></td> </tr> <tr id="addr-A1A8"> <td class="addr"><a href="#addr-A1A8">A1A8</a></td> <td> <span class="directive">EQUB</span> <span data-tip="1 &01 %00000001 SOH">&01</span> <span class="comment">; Sector count: &01 (start/unpark heads)</span></td> </tr> <tr id="addr-A1A9"> <td class="addr"><a href="#addr-A1A9">A1A9</a></td> <td> <span class="directive">EQUB</span> <span data-tip="0 &00 %00000000 NUL">&00</span> <span class="comment">; Control: &00</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A1AA"> <td colspan="2"><div class="sub-header"> <h3>Calculate total free space on disc</h3> <div class="sub-desc"><p>Sum all free space entries in the FSM to get the total free space. Prepares workspace for display by *FREE.</p> <p>On exit: 3-byte sum in wksp_disc_op_result (little-endian)</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A1AA">A1AA</a></td> <td><span class="label">.calc_total_free_space<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-9D98">← 9D98 JSR</a><a href="#addr-A01B">← A01B JSR</a><a href="#addr-A029">← A029 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear accumulators</span></td> </tr> <tr id="addr-A1AC"> <td class="addr"><a href="#addr-A1AC">A1AC</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: clear 4 bytes</span></td> </tr> <tr id="addr-A1AE"> <td class="addr"><a href="#addr-A1AE">A1AE</a></td> <td><span class="label">.clear_accumulators_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A1B5">← A1B5 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,x</span> <span class="comment">; Clear disc op result bytes</span></td> </tr> <tr id="addr-A1B1"> <td class="addr"><a href="#addr-A1B1">A1B1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1027">wksp_tube_transfer_addr_1</span>,x</span> <span class="comment">; Clear Tube transfer bytes</span></td> </tr> <tr id="addr-A1B4"> <td class="addr"><a href="#addr-A1B4">A1B4</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A1B5"> <td class="addr"><a href="#addr-A1B5">A1B5</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A1AE" data-tip="&A1AE">clear_accumulators_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A1B7"> <td class="addr"><a href="#addr-A1B7">A1B7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8609" data-tip="&8609 – Sum all free space in FSM">sum_free_space</a></span> <span class="comment">; Sum the free space entries</span></td> </tr> <tr id="addr-A1BA"> <td class="addr"><a href="#addr-A1BA">A1BA</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: copy 3 bytes of result</span></td> </tr> <tr id="addr-A1BC"> <td class="addr"><a href="#addr-A1BC">A1BC</a></td> <td><span class="label">.copy_result_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A1C3">← A1C3 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&105D">wksp_access_accum</span>,x</span> <span class="comment">; Get result byte</span></td> </tr> <tr id="addr-A1BF"> <td class="addr"><a href="#addr-A1BF">A1BF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span>,x</span> <span class="comment">; Store in disc op workspace</span></td> </tr> <tr id="addr-A1C2"> <td class="addr"><a href="#addr-A1C2">A1C2</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next result byte</span></td> </tr> <tr id="addr-A1C3"> <td class="addr"><a href="#addr-A1C3">A1C3</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A1BC" data-tip="&A1BC">copy_result_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A1C5"> <td class="addr"><a href="#addr-A1C5">A1C5</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A1C6"> <td colspan="2"><div class="sub-header"> <h3>Print space value in hex and decimal</h3> <div class="sub-desc"><p>Print a 3-byte sector count from the disc op workspace as hex bytes, then convert to decimal bytes and print as ' Sectors = NNN,NNN,NNN Bytes'. Used by *FREE to display free and used space.</p> <p>The hex part prints the 3-byte value at &1016-&1018. The decimal part uses the double-dabble algorithm (also called shift-and-add-3) to convert the 4-byte binary value at &1015-&1018 into 10 BCD digits stored at &1040-&1049. Each iteration shifts the binary value left one bit and rotates the carry into the BCD digits, subtracting 10 from any digit that reaches 10 or above (carrying into the next digit). After 31 iterations (32 bits minus the sign bit), the BCD digits are printed with leading-zero suppression and comma separators at positions 3 and 6 (thousands and millions).</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A1C6">A1C6</a></td> <td><span class="label">.print_space_value<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A01E">← A01E JSR</a><a href="#addr-A03E">← A03E JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1018">wksp_disc_op_mem_addr_2</span></span> <span class="comment">; Print high byte as hex</span></td> </tr> <tr id="addr-A1C9"> <td class="addr"><a href="#addr-A1C9">A1C9</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-931B" data-tip="&931B – Print a byte as two hex digits">print_hex_byte</a></span> <span class="comment">; Print mid byte as hex</span></td> </tr> <tr id="addr-A1CC"> <td class="addr"><a href="#addr-A1CC">A1CC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1017">wksp_disc_op_mem_addr_1</span></span> <span class="comment">; Print mid byte as hex</span></td> </tr> <tr id="addr-A1CF"> <td class="addr"><a href="#addr-A1CF">A1CF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-931B" data-tip="&931B – Print a byte as two hex digits">print_hex_byte</a></span> <span class="comment">; Print low byte as hex</span></td> </tr> <tr id="addr-A1D2"> <td class="addr"><a href="#addr-A1D2">A1D2</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span></span> <span class="comment">; Print low byte as hex</span></td> </tr> <tr id="addr-A1D5"> <td class="addr"><a href="#addr-A1D5">A1D5</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-931B" data-tip="&931B – Print a byte as two hex digits">print_hex_byte</a></span> <span class="comment">; Print result byte as hex</span></td> </tr> <tr id="addr-A1D8"> <td class="addr"><a href="#addr-A1D8">A1D8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span> <span class="comment">; Print " Sectors ="</span></td> </tr> <tr id="addr-A1DB"> <td class="addr"><a href="#addr-A1DB">A1DB</a></td> <td> <span class="directive">EQUS</span> <span class="string">" Sectors ="</span></td> </tr> <tr id="addr-A1E5"> <td class="addr"><a href="#addr-A1E5">A1E5</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">&A0</span> <span class="comment">; ' ' + bit 7: end of inline string</span></td> </tr> <tr id="addr-A1E6"> <td class="addr"><a href="#addr-A1E6">A1E6</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="31 &1F %00011111 US">#&1f</span></span> <span class="comment">; X=&1F: 31 bit shifts (32-bit value)</span></td> </tr> <tr id="addr-A1E8"> <td class="addr"><a href="#addr-A1E8">A1E8</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&1033">wksp_last_access_drive</span></span> <span class="comment">; Store bit counter in workspace</span></td> </tr> <tr id="addr-A1EB"> <td class="addr"><a href="#addr-A1EB">A1EB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear all BCD digit accumulators</span></td> </tr> <tr id="addr-A1ED"> <td class="addr"><a href="#addr-A1ED">A1ED</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: clear 10 BCD digits (0-9)</span></td> </tr> <tr id="addr-A1EF"> <td class="addr"><a href="#addr-A1EF">A1EF</a></td> <td><span class="label">.clear_bcd_digits_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A1F3">← A1F3 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span>,x</span> <span class="comment">; Clear BCD digit at &1040+X</span></td> </tr> <tr id="addr-A1F2"> <td class="addr"><a href="#addr-A1F2">A1F2</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next digit</span></td> </tr> <tr id="addr-A1F3"> <td class="addr"><a href="#addr-A1F3">A1F3</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A1EF" data-tip="&A1EF">clear_bcd_digits_loop</a></span> <span class="comment">; Loop for all 10 digits</span></td> </tr> <tr id="addr-A1F5"> <td class="addr"><a href="#addr-A1F5">A1F5</a></td> <td><span class="label">.shift_binary_bit<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A219">← A219 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ASL</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span></span> <span class="comment">; Shift binary value left: byte 0</span></td> </tr> <tr id="addr-A1F8"> <td class="addr"><a href="#addr-A1F8">A1F8</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span></span> <span class="comment">; Rotate carry into byte 1</span></td> </tr> <tr id="addr-A1FB"> <td class="addr"><a href="#addr-A1FB">A1FB</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&1017">wksp_disc_op_mem_addr_1</span></span> <span class="comment">; Rotate carry into byte 2</span></td> </tr> <tr id="addr-A1FE"> <td class="addr"><a href="#addr-A1FE">A1FE</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&1018">wksp_disc_op_mem_addr_2</span></span> <span class="comment">; Rotate carry into byte 3</span></td> </tr> <tr id="addr-A201"> <td class="addr"><a href="#addr-A201">A201</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: start from least significant digit</span></td> </tr> <tr id="addr-A203"> <td class="addr"><a href="#addr-A203">A203</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: process 10 BCD digits</span></td> </tr> <tr id="addr-A205"> <td class="addr"><a href="#addr-A205">A205</a></td> <td><span class="label">.dabble_digit_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A214">← A214 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span>,x</span> <span class="comment">; Get BCD digit</span></td> </tr> <tr id="addr-A208"> <td class="addr"><a href="#addr-A208">A208</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Rotate shifted bit into digit</span></td> </tr> <tr id="addr-A209"> <td class="addr"><a href="#addr-A209">A209</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; Digit >= 10?</span></td> </tr> <tr id="addr-A20B"> <td class="addr"><a href="#addr-A20B">A20B</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A20F" data-tip="&A20F">store_bcd_digit</a></span> <span class="comment">; No: digit is valid (0-9)</span></td> </tr> <tr id="addr-A20D"> <td class="addr"><a href="#addr-A20D">A20D</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; Yes: subtract 10 (carry propagates)</span></td> </tr> <tr id="addr-A20F"> <td class="addr"><a href="#addr-A20F">A20F</a></td> <td><span class="label">.store_bcd_digit<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A20B">← A20B BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span>,x</span> <span class="comment">; Store corrected BCD digit</span></td> </tr> <tr id="addr-A212"> <td class="addr"><a href="#addr-A212">A212</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Next digit (toward most significant)</span></td> </tr> <tr id="addr-A213"> <td class="addr"><a href="#addr-A213">A213</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement digit counter</span></td> </tr> <tr id="addr-A214"> <td class="addr"><a href="#addr-A214">A214</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A205" data-tip="&A205">dabble_digit_loop</a></span> <span class="comment">; Loop for all 10 digits</span></td> </tr> <tr id="addr-A216"> <td class="addr"><a href="#addr-A216">A216</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&1033">wksp_last_access_drive</span></span> <span class="comment">; Decrement bit counter</span></td> </tr> <tr id="addr-A219"> <td class="addr"><a href="#addr-A219">A219</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A1F5" data-tip="&A1F5">shift_binary_bit</a></span> <span class="comment">; Loop for all 31 bits</span></td> </tr> <tr id="addr-A21B"> <td class="addr"><a href="#addr-A21B">A21B</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Y=' ': separator starts as space</span></td> </tr> <tr id="addr-A21D"> <td class="addr"><a href="#addr-A21D">A21D</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; X=8: start from most significant digit</span></td> </tr> <tr id="addr-A21F"> <td class="addr"><a href="#addr-A21F">A21F</a></td> <td><span class="label">.print_digit_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A245">← A245 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A223" data-tip="&A223">check_leading_zero</a></span> <span class="comment">; X!=0: not at units position yet</span></td> </tr> <tr id="addr-A221"> <td class="addr"><a href="#addr-A221">A221</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="44 &2C %00101100 ','">#&2c</span></span> <span class="comment">; X=0: switch separator to comma</span></td> </tr> <tr id="addr-A223"> <td class="addr"><a href="#addr-A223">A223</a></td> <td><span class="label">.check_leading_zero<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A21F">← A21F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span>,x</span> <span class="comment">; Get BCD digit value</span></td> </tr> <tr id="addr-A226"> <td class="addr"><a href="#addr-A226">A226</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A230" data-tip="&A230">print_nonzero_digit</a></span> <span class="comment">; Non-zero: print this digit</span></td> </tr> <tr id="addr-A228"> <td class="addr"><a href="#addr-A228">A228</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="44 &2C %00101100 ','">#&2c</span></span> <span class="comment">; Zero: has a non-zero digit been seen?</span></td> </tr> <tr id="addr-A22A"> <td class="addr"><a href="#addr-A22A">A22A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A230" data-tip="&A230">print_nonzero_digit</a></span> <span class="comment">; Yes (separator=comma): print zero</span></td> </tr> <tr id="addr-A22C"> <td class="addr"><a href="#addr-A22C">A22C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; No: suppress leading zero with space</span></td> </tr> <tr id="addr-A22E"> <td class="addr"><a href="#addr-A22E">A22E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A235" data-tip="&A235">output_digit_char</a></span> <span class="comment">; Skip to output</span></td> </tr> <tr id="addr-A230"> <td class="addr"><a href="#addr-A230">A230</a></td> <td><span class="label">.print_nonzero_digit<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A226">← A226 BNE</a><a href="#addr-A22A">← A22A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="44 &2C %00101100 ','">#&2c</span></span> <span class="comment">; Mark that we've seen a non-zero digit</span></td> </tr> <tr id="addr-A232"> <td class="addr"><a href="#addr-A232">A232</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-A233"> <td class="addr"><a href="#addr-A233">A233</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Convert BCD digit to ASCII ('0'-'9')</span></td> </tr> <tr id="addr-A235"> <td class="addr"><a href="#addr-A235">A235</a></td> <td><span class="label">.output_digit_char<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A22E">← A22E BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFEE">oswrch</span></span> <span class="comment">; Print digit or space</span></td> </tr> <tr id="addr-A238"> <td class="addr"><a href="#addr-A238">A238</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="6 &06 %00000110 ACK">#6</span></span> <span class="comment">; At position 6 (millions boundary)?</span></td> </tr> <tr id="addr-A23A"> <td class="addr"><a href="#addr-A23A">A23A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A240" data-tip="&A240">print_comma_separator</a></span> <span class="comment">; Yes: print comma separator</span></td> </tr> <tr id="addr-A23C"> <td class="addr"><a href="#addr-A23C">A23C</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; At position 3 (thousands boundary)?</span></td> </tr> <tr id="addr-A23E"> <td class="addr"><a href="#addr-A23E">A23E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A244" data-tip="&A244">next_digit</a></span> <span class="comment">; No: skip separator</span></td> </tr> <tr id="addr-A240"> <td class="addr"><a href="#addr-A240">A240</a></td> <td><span class="label">.print_comma_separator<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A23A">← A23A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Print separator (space or comma)</span></td> </tr> <tr id="addr-A241"> <td class="addr"><a href="#addr-A241">A241</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFEE">oswrch</span></span> <span class="comment">; Output separator character</span></td> </tr> <tr id="addr-A244"> <td class="addr"><a href="#addr-A244">A244</a></td> <td><span class="label">.next_digit<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A23E">← A23E BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next digit (toward least significant)</span></td> </tr> <tr id="addr-A245"> <td class="addr"><a href="#addr-A245">A245</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A21F" data-tip="&A21F">print_digit_loop</a></span> <span class="comment">; Loop for 9 digits (8 down to 0)</span></td> </tr> <tr id="addr-A247"> <td class="addr"><a href="#addr-A247">A247</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-92A0" data-tip="&92A0 – Print bit-7-terminated inline string">print_inline_string</a></span></td> </tr> <tr id="addr-A24A"> <td class="addr"><a href="#addr-A24A">A24A</a></td> <td> <span class="directive">EQUS</span> <span class="string">" Bytes"</span></td> </tr> <tr id="addr-A250"> <td class="addr"><a href="#addr-A250">A250</a></td> <td> <span class="directive">EQUB</span> <span data-tip="160 &A0 %10100000">&A0</span> <span class="comment">; ' ' + bit 7: end of inline string</span></td> </tr> <tr id="addr-A251"> <td class="addr"><a href="#addr-A251">A251</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return to caller</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A252"> <td colspan="2"><div class="sub-header"> <h3>*TITLE command handler</h3> <div class="sub-desc"><p>Change the title of the currently selected directory. The title may be up to 19 characters long.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A252">A252</a></td> <td><span class="label">.star_title</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B4F5" data-tip="&B4F5 – Check disc changed and reload FSM if needed">check_drive_and_reload_fsm</a></span> <span class="comment">; Ensure dir is loaded and writable</span></td> </tr> <tr id="addr-A255"> <td class="addr"><a href="#addr-A255">A255</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FEA" data-tip="&8FEA – Validate FSM entry structure and checksums">validate_fsm_checksums</a></span> <span class="comment">; Validate FSM before modification</span></td> </tr> <tr id="addr-A258"> <td class="addr"><a href="#addr-A258">A258</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4CF" data-tip="&A4CF – Skip leading spaces in command argument">skip_spaces</a></span> <span class="comment">; Skip leading spaces in argument</span></td> </tr> <tr id="addr-A25B"> <td class="addr"><a href="#addr-A25B">A25B</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: index into title string</span></td> </tr> <tr id="addr-A25D"> <td class="addr"><a href="#addr-A25D">A25D</a></td> <td><span class="label">.copy_title_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A271">← A271 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get next character</span></td> </tr> <tr id="addr-A25F"> <td class="addr"><a href="#addr-A25F">A25F</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7</span></td> </tr> <tr id="addr-A261"> <td class="addr"><a href="#addr-A261">A261</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="34 &22 %00100010 '"'">#&22</span></span> <span class="comment">; Double-quote terminates title</span></td> </tr> <tr id="addr-A263"> <td class="addr"><a href="#addr-A263">A263</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A269" data-tip="&A269">pad_title_with_cr</a></span> <span class="comment">; Yes, pad with CR</span></td> </tr> <tr id="addr-A265"> <td class="addr"><a href="#addr-A265">A265</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Control char terminates title</span></td> </tr> <tr id="addr-A267"> <td class="addr"><a href="#addr-A267">A267</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A26B" data-tip="&A26B">store_title_char</a></span> <span class="comment">; Printable, store it</span></td> </tr> <tr id="addr-A269"> <td class="addr"><a href="#addr-A269">A269</a></td> <td><span class="label">.pad_title_with_cr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A263">← A263 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="13 &0D %00001101 CR">#&0d</span></span> <span class="comment">; Use CR as padding character</span></td> </tr> <tr id="addr-A26B"> <td class="addr"><a href="#addr-A26B">A26B</a></td> <td><span class="label">.store_title_char<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A267">← A267 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&16D9">dir_title</span>,y</span> <span class="comment">; Store in directory title field</span></td> </tr> <tr id="addr-A26E"> <td class="addr"><a href="#addr-A26E">A26E</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next character</span></td> </tr> <tr id="addr-A26F"> <td class="addr"><a href="#addr-A26F">A26F</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="19 &13 %00010011 DC3">#&13</span></span> <span class="comment">; Title is 19 characters max</span></td> </tr> <tr id="addr-A271"> <td class="addr"><a href="#addr-A271">A271</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A25D" data-tip="&A25D">copy_title_loop</a></span> <span class="comment">; Loop for all 19 characters</span></td> </tr> <tr id="addr-A273"> <td class="addr"><a href="#addr-A273">A273</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write directory back to disc</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A276"> <td colspan="2"><div class="sub-header"> <h3>*COMPACT command handler</h3> <div class="sub-desc"><p>Compact the free space on a drive by moving files to consolidate fragmented free space into a single contiguous region.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A276">A276</a></td> <td><span class="label">.star_compact</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4CF" data-tip="&A4CF – Skip leading spaces in command argument">skip_spaces</a></span> <span class="comment">; Skip leading spaces</span></td> </tr> <tr id="addr-A279"> <td class="addr"><a href="#addr-A279">A279</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check for argument</span></td> </tr> <tr id="addr-A27B"> <td class="addr"><a href="#addr-A27B">A27B</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get first char</span></td> </tr> <tr id="addr-A27D"> <td class="addr"><a href="#addr-A27D">A27D</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Printable char? Parse SP and LP</span></td> </tr> <tr id="addr-A27F"> <td class="addr"><a href="#addr-A27F">A27F</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A2AB" data-tip="&A2AB">parse_compact_start_page</a></span> <span class="comment">; Yes, parse hex SP LP arguments</span></td> </tr> <tr id="addr-A281"> <td class="addr"><a href="#addr-A281">A281</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="132 &84 %10000100">#osbyte_read_himem</span></span> <span class="comment">; OSBYTE &84: read top of user memory</span></td> </tr> <tr id="addr-A283"> <td class="addr"><a href="#addr-A283">A283</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Read top of available user RAM (HIMEM)</span></td> </tr> <tr id="addr-A286"> <td class="addr"><a href="#addr-A286">A286</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; X = HIMEM low byte</span></td> </tr> <tr id="addr-A287"> <td class="addr"><a href="#addr-A287">A287</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Non-zero low byte: bad compact</span></td> </tr> <tr id="addr-A289"> <td class="addr"><a href="#addr-A289">A289</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Y = HIMEM high byte</span></td> </tr> <tr id="addr-A28A"> <td class="addr"><a href="#addr-A28A">A28A</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Bit 7 set (>= &80): bad compact</span></td> </tr> <tr id="addr-A28C"> <td class="addr"><a href="#addr-A28C">A28C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Store HIMEM page as start page</span></td> </tr> <tr id="addr-A28F"> <td class="addr"><a href="#addr-A28F">A28F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; Calculate length: &80 - start</span></td> </tr> <tr id="addr-A291"> <td class="addr"><a href="#addr-A291">A291</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-A292"> <td class="addr"><a href="#addr-A292">A292</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Subtract start page from HIMEM</span></td> </tr> <tr id="addr-A295"> <td class="addr"><a href="#addr-A295">A295</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1061">wksp_compact_length</span></span> <span class="comment">; Store buffer length in pages</span></td> </tr> <tr id="addr-A298"> <td class="addr"><a href="#addr-A298">A298</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A344" data-tip="&A344">begin_compaction</a></span> <span class="comment">; Jump to compaction main loop</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A29B"> <td colspan="2"><div class="sub-header"> <h3>Raise Bad compact error</h3> <div class="sub-desc"><p>Reload FSM and directory then raise error &94: Bad compact.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A29B">A29B</a></td> <td><span class="label">.bad_compact_error<span class="ref-badge">←11</span><span class="ref-popup"><a href="#addr-A287">← A287 BNE</a><a href="#addr-A28A">← A28A BMI</a><a href="#addr-A2BD">← A2BD BNE</a><a href="#addr-A2E6">← A2E6 BCS</a><a href="#addr-A2EF">← A2EF BCC</a><a href="#addr-A301">← A301 BCC</a><a href="#addr-A305">← A305 BCS</a><a href="#addr-A313">← A313 BMI</a><a href="#addr-A31F">← A31F JMP</a><a href="#addr-A331">← A331 JMP</a><a href="#addr-A341">← A341 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-A29E"> <td class="addr"><a href="#addr-A29E">A29E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="148 &94 %10010100">&94</span> <span class="comment">; Error &94: Bad compact</span></td> </tr> <tr id="addr-A29F"> <td class="addr"><a href="#addr-A29F">A29F</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Bad compact"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-A2AB"> <td class="addr"><a href="#addr-A2AB">A2AB</a></td> <td><span class="label">.parse_compact_start_page<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A27F">← A27F BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span></span> <span class="comment">; Store first hex digit</span></td> </tr> <tr id="addr-A2AE"> <td class="addr"><a href="#addr-A2AE">A2AE</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next argument character</span></td> </tr> <tr id="addr-A2AF"> <td class="addr"><a href="#addr-A2AF">A2AF</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get second hex digit</span></td> </tr> <tr id="addr-A2B1"> <td class="addr"><a href="#addr-A2B1">A2B1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span></span> <span class="comment">; Store as second digit</span></td> </tr> <tr id="addr-A2B4"> <td class="addr"><a href="#addr-A2B4">A2B4</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next character</span></td> </tr> <tr id="addr-A2B5"> <td class="addr"><a href="#addr-A2B5">A2B5</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get separator/terminator</span></td> </tr> <tr id="addr-A2B7"> <td class="addr"><a href="#addr-A2B7">A2B7</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Is it a space?</span></td> </tr> <tr id="addr-A2B9"> <td class="addr"><a href="#addr-A2B9">A2B9</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A2BF" data-tip="&A2BF">skip_separator_spaces</a></span> <span class="comment">; Yes, skip to length parameter</span></td> </tr> <tr id="addr-A2BB"> <td class="addr"><a href="#addr-A2BB">A2BB</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="44 &2C %00101100 ','">#&2c</span></span> <span class="comment">; Is it a comma?</span></td> </tr> <tr id="addr-A2BD"> <td class="addr"><a href="#addr-A2BD">A2BD</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; No separator: bad compact error</span></td> </tr> <tr id="addr-A2BF"> <td class="addr"><a href="#addr-A2BF">A2BF</a></td> <td><span class="label">.skip_separator_spaces<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A2B9">← A2B9 BEQ</a><a href="#addr-A2C4">← A2C4 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Skip spaces/commas</span></td> </tr> <tr id="addr-A2C0"> <td class="addr"><a href="#addr-A2C0">A2C0</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get length first digit</span></td> </tr> <tr id="addr-A2C2"> <td class="addr"><a href="#addr-A2C2">A2C2</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Is it a space?</span></td> </tr> <tr id="addr-A2C4"> <td class="addr"><a href="#addr-A2C4">A2C4</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A2BF" data-tip="&A2BF">skip_separator_spaces</a></span> <span class="comment">; Yes, skip more spaces</span></td> </tr> <tr id="addr-A2C6"> <td class="addr"><a href="#addr-A2C6">A2C6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1017">wksp_disc_op_mem_addr_1</span></span> <span class="comment">; Store length first digit</span></td> </tr> <tr id="addr-A2C9"> <td class="addr"><a href="#addr-A2C9">A2C9</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next character</span></td> </tr> <tr id="addr-A2CA"> <td class="addr"><a href="#addr-A2CA">A2CA</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get length second digit</span></td> </tr> <tr id="addr-A2CC"> <td class="addr"><a href="#addr-A2CC">A2CC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1018">wksp_disc_op_mem_addr_2</span></span> <span class="comment">; Store length second digit</span></td> </tr> <tr id="addr-A2CF"> <td class="addr"><a href="#addr-A2CF">A2CF</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Is second digit printable?</span></td> </tr> <tr id="addr-A2D1"> <td class="addr"><a href="#addr-A2D1">A2D1</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A2DF" data-tip="&A2DF">skip_trailing_spaces</a></span> <span class="comment">; Yes, both digits present</span></td> </tr> <tr id="addr-A2D3"> <td class="addr"><a href="#addr-A2D3">A2D3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1017">wksp_disc_op_mem_addr_1</span></span> <span class="comment">; Only one digit: treat as low nibble</span></td> </tr> <tr id="addr-A2D6"> <td class="addr"><a href="#addr-A2D6">A2D6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1018">wksp_disc_op_mem_addr_2</span></span> <span class="comment">; Move to high position</span></td> </tr> <tr id="addr-A2D9"> <td class="addr"><a href="#addr-A2D9">A2D9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Set low nibble to '0'</span></td> </tr> <tr id="addr-A2DB"> <td class="addr"><a href="#addr-A2DB">A2DB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1017">wksp_disc_op_mem_addr_1</span></span> <span class="comment">; Store '0' as low nibble</span></td> </tr> <tr id="addr-A2DE"> <td class="addr"><a href="#addr-A2DE">A2DE</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Back up one char position</span></td> </tr> <tr id="addr-A2DF"> <td class="addr"><a href="#addr-A2DF">A2DF</a></td> <td><span class="label">.skip_trailing_spaces<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A2D1">← A2D1 BCS</a><a href="#addr-A2E4">← A2E4 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Skip past length argument</span></td> </tr> <tr id="addr-A2E0"> <td class="addr"><a href="#addr-A2E0">A2E0</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get next character</span></td> </tr> <tr id="addr-A2E2"> <td class="addr"><a href="#addr-A2E2">A2E2</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Is it a space?</span></td> </tr> <tr id="addr-A2E4"> <td class="addr"><a href="#addr-A2E4">A2E4</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A2DF" data-tip="&A2DF">skip_trailing_spaces</a></span> <span class="comment">; Yes, skip spaces</span></td> </tr> <tr id="addr-A2E6"> <td class="addr"><a href="#addr-A2E6">A2E6</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Printable after length: bad compact</span></td> </tr> <tr id="addr-A2E8"> <td class="addr"><a href="#addr-A2E8">A2E8</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: convert 4 hex digits to 2 bytes</span></td> </tr> <tr id="addr-A2EA"> <td class="addr"><a href="#addr-A2EA">A2EA</a></td> <td><span class="label">.convert_hex_digits_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A30D">← A30D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,x</span> <span class="comment">; Get hex digit</span></td> </tr> <tr id="addr-A2ED"> <td class="addr"><a href="#addr-A2ED">A2ED</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Is it '0'-'9'?</span></td> </tr> <tr id="addr-A2EF"> <td class="addr"><a href="#addr-A2EF">A2EF</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Below '0': bad compact</span></td> </tr> <tr id="addr-A2F1"> <td class="addr"><a href="#addr-A2F1">A2F1</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="58 &3A %00111010 ':'">#&3a</span></span> <span class="comment">; Above '9'?</span></td> </tr> <tr id="addr-A2F3"> <td class="addr"><a href="#addr-A2F3">A2F3</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A2FD" data-tip="&A2FD">check_hex_af</a></span> <span class="comment">; No, it's '0'-'9': convert</span></td> </tr> <tr id="addr-A2F5"> <td class="addr"><a href="#addr-A2F5">A2F5</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-A2F6"> <td class="addr"><a href="#addr-A2F6">A2F6</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Convert ASCII digit to value</span></td> </tr> <tr id="addr-A2F8"> <td class="addr"><a href="#addr-A2F8">A2F8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,x</span> <span class="comment">; Store value</span></td> </tr> <tr id="addr-A2FB"> <td class="addr"><a href="#addr-A2FB">A2FB</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A30C" data-tip="&A30C">store_converted_byte</a></span> <span class="comment">; Always branch (non-negative)</span></td> </tr> <tr id="addr-A2FD"> <td class="addr"><a href="#addr-A2FD">A2FD</a></td> <td><span class="label">.check_hex_af<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A2F3">← A2F3 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="95 &5F %01011111 '_'">#&5f</span></span> <span class="comment">; Convert to uppercase</span></td> </tr> <tr id="addr-A2FF"> <td class="addr"><a href="#addr-A2FF">A2FF</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="65 &41 %01000001 'A'">#&41</span></span> <span class="comment">; Below 'A'?</span></td> </tr> <tr id="addr-A301"> <td class="addr"><a href="#addr-A301">A301</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Yes: bad compact</span></td> </tr> <tr id="addr-A303"> <td class="addr"><a href="#addr-A303">A303</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="71 &47 %01000111 'G'">#&47</span></span> <span class="comment">; Above 'F'?</span></td> </tr> <tr id="addr-A305"> <td class="addr"><a href="#addr-A305">A305</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Yes: bad compact</span></td> </tr> <tr id="addr-A307"> <td class="addr"><a href="#addr-A307">A307</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="54 &36 %00110110 '6'">#&36</span></span> <span class="comment">; Convert 'A'-'F' to 10-15</span></td> </tr> <tr id="addr-A309"> <td class="addr"><a href="#addr-A309">A309</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,x</span> <span class="comment">; Store value</span></td> </tr> <tr id="addr-A30C"> <td class="addr"><a href="#addr-A30C">A30C</a></td> <td><span class="label">.store_converted_byte<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A2FB">← A2FB BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next digit</span></td> </tr> <tr id="addr-A30D"> <td class="addr"><a href="#addr-A30D">A30D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A2EA" data-tip="&A2EA">convert_hex_digits_loop</a></span> <span class="comment">; Loop for 4 digits</span></td> </tr> <tr id="addr-A30F"> <td class="addr"><a href="#addr-A30F">A30F</a></td> <td> <span class="opcode">INX</span> <span class="comment">; X=0: combine first pair</span></td> </tr> <tr id="addr-A310"> <td class="addr"><a href="#addr-A310">A310</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A35A" data-tip="&A35A – Combine two hex nibbles into a byte">combine_hex_digit_pair</a></span> <span class="comment">; Combine two hex digits into byte</span></td> </tr> <tr id="addr-A313"> <td class="addr"><a href="#addr-A313">A313</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Negative result: bad compact</span></td> </tr> <tr id="addr-A315"> <td class="addr"><a href="#addr-A315">A315</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Store as start page</span></td> </tr> <tr id="addr-A318"> <td class="addr"><a href="#addr-A318">A318</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: combine second pair</span></td> </tr> <tr id="addr-A31A"> <td class="addr"><a href="#addr-A31A">A31A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A35A" data-tip="&A35A – Combine two hex nibbles into a byte">combine_hex_digit_pair</a></span> <span class="comment">; Combine two hex digits into byte</span></td> </tr> <tr id="addr-A31D"> <td class="addr"><a href="#addr-A31D">A31D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A322" data-tip="&A322">convert_two_digits</a></span> <span class="comment">; Positive result: valid</span></td> </tr> <tr id="addr-A31F"> <td class="addr"><a href="#addr-A31F">A31F</a></td> <td><span class="label">.check_hex_digit_valid<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A322">← A322 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Zero length: bad compact</span></td> </tr> <tr id="addr-A322"> <td class="addr"><a href="#addr-A322">A322</a></td> <td><span class="label">.convert_two_digits<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A31D">← A31D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A31F" data-tip="&A31F">check_hex_digit_valid</a></span> <span class="comment">; Also zero: bad compact</span></td> </tr> <tr id="addr-A324"> <td class="addr"><a href="#addr-A324">A324</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1061">wksp_compact_length</span></span> <span class="comment">; Store as buffer length in pages</span></td> </tr> <tr id="addr-A327"> <td class="addr"><a href="#addr-A327">A327</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00F4">romsel_copy</span></span> <span class="comment">; Get our ROM number</span></td> </tr> <tr id="addr-A329"> <td class="addr"><a href="#addr-A329">A329</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0DF0">rom_wksp_table</span>,x</span> <span class="comment">; Get workspace page from ROM table</span></td> </tr> <tr id="addr-A32C"> <td class="addr"><a href="#addr-A32C">A32C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Start page below workspace?</span></td> </tr> <tr id="addr-A32F"> <td class="addr"><a href="#addr-A32F">A32F</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A334" data-tip="&A334">combine_hex_nibbles</a></span> <span class="comment">; Yes: buffer doesn't overlap</span></td> </tr> <tr id="addr-A331"> <td class="addr"><a href="#addr-A331">A331</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; No: bad compact (overlaps workspace)</span></td> </tr> <tr id="addr-A334"> <td class="addr"><a href="#addr-A334">A334</a></td> <td><span class="label">.combine_hex_nibbles<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A32F">← A32F BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-A335"> <td class="addr"><a href="#addr-A335">A335</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Start page + length</span></td> </tr> <tr id="addr-A338"> <td class="addr"><a href="#addr-A338">A338</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1061">wksp_compact_length</span></span> <span class="comment">; Add buffer length</span></td> </tr> <tr id="addr-A33B"> <td class="addr"><a href="#addr-A33B">A33B</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A344" data-tip="&A344">begin_compaction</a></span> <span class="comment">; Result > &7F: check for exactly &80</span></td> </tr> <tr id="addr-A33D"> <td class="addr"><a href="#addr-A33D">A33D</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; Is it exactly &80?</span></td> </tr> <tr id="addr-A33F"> <td class="addr"><a href="#addr-A33F">A33F</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A344" data-tip="&A344">begin_compaction</a></span> <span class="comment">; Yes: OK (up to screen memory)</span></td> </tr> <tr id="addr-A341"> <td class="addr"><a href="#addr-A341">A341</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A29B" data-tip="&A29B – Raise Bad compact error">bad_compact_error</a></span> <span class="comment">; Above &80: bad compact</span></td> </tr> <tr id="addr-A344"> <td class="addr"><a href="#addr-A344">A344</a></td> <td><span class="label">.begin_compaction<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-A298">← A298 JMP</a><a href="#addr-A33B">← A33B BPL</a><a href="#addr-A33F">← A33F BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B1B3" data-tip="&B1B3 – *CLOSE command handler">star_close</a></span></td> </tr> <tr id="addr-A347"> <td class="addr"><a href="#addr-A347">A347</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8305" data-tip="&8305 – Wait while files are being ensured">wait_ensuring</a></span></td> </tr> <tr id="addr-A34A"> <td class="addr"><a href="#addr-A34A">A34A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Set bit 3 of ADFS flags</span></td> </tr> <tr id="addr-A34C"> <td class="addr"><a href="#addr-A34C">A34C</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; Indicate compaction in progress</span></td> </tr> <tr id="addr-A34E"> <td class="addr"><a href="#addr-A34E">A34E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-A350"> <td class="addr"><a href="#addr-A350">A350</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-98AE" data-tip="&98AE">calculate_total_sectors</a></span> <span class="comment">; Execute compaction algorithm</span></td> </tr> <tr id="addr-A353"> <td class="addr"><a href="#addr-A353">A353</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Clear bit 3 when done</span></td> </tr> <tr id="addr-A355"> <td class="addr"><a href="#addr-A355">A355</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="247 &F7 %11110111">#&f7</span></span> <span class="comment">; Mask off bit 3</span></td> </tr> <tr id="addr-A357"> <td class="addr"><a href="#addr-A357">A357</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store cleared flags</span></td> </tr> <tr id="addr-A359"> <td class="addr"><a href="#addr-A359">A359</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A35A"> <td colspan="2"><div class="sub-header"> <h3>Combine two hex nibbles into a byte</h3> <div class="sub-desc"><p>Take high nibble from workspace, shift left 4, and OR with low nibble to produce a combined byte.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>X</td><td>offset into wksp_disc_op_result (0 or 2)</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>combined byte value</td></tr> <tr><td>X</td><td>preserved</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A35A">A35A</a></td> <td><span class="label">.combine_hex_digit_pair<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A310">← A310 JSR</a><a href="#addr-A31A">← A31A JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span>,x</span> <span class="comment">; Get hex digit pair high nibble</span></td> </tr> <tr id="addr-A35D"> <td class="addr"><a href="#addr-A35D">A35D</a></td> <td> <span class="opcode">ASL</span> <span class="comment">; Shift left 4 positions</span></td> </tr> <tr id="addr-A35E"> <td class="addr"><a href="#addr-A35E">A35E</a></td> <td> <span class="opcode">ASL</span> <span class="comment">; Second shift</span></td> </tr> <tr id="addr-A35F"> <td class="addr"><a href="#addr-A35F">A35F</a></td> <td> <span class="opcode">ASL</span> <span class="comment">; Third shift</span></td> </tr> <tr id="addr-A360"> <td class="addr"><a href="#addr-A360">A360</a></td> <td> <span class="opcode">ASL</span> <span class="comment">; Fourth shift</span></td> </tr> <tr id="addr-A361"> <td class="addr"><a href="#addr-A361">A361</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span>,x</span> <span class="comment">; OR in low nibble</span></td> </tr> <tr id="addr-A364"> <td class="addr"><a href="#addr-A364">A364</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return combined byte</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A365"> <td colspan="2"><div class="sub-header"> <h3>Parse second filename from command line</h3> <div class="sub-desc"><p>Skip past the first filename, save the text pointer, then parse the second filename for commands like *RENAME and *COPY. Raises Bad command if extra arguments follow.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A365">A365</a></td> <td><span class="label">.parse_second_filename<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-A544">← A544 JSR</a><a href="#addr-A5A1">← A5A1 JSR</a><a href="#addr-A62E">← A62E JSR</a><a href="#addr-A864">← A864 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4B7" data-tip="&A4B7 – Skip past filename in command string">skip_filename</a></span></td> </tr> <tr id="addr-A368"> <td class="addr"><a href="#addr-A368">A368</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Save text pointer high</span></td> </tr> <tr id="addr-A36A"> <td class="addr"><a href="#addr-A36A">A36A</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-A36B"> <td class="addr"><a href="#addr-A36B">A36B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save text pointer low</span></td> </tr> <tr id="addr-A36D"> <td class="addr"><a href="#addr-A36D">A36D</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-A36E"> <td class="addr"><a href="#addr-A36E">A36E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4B7" data-tip="&A4B7 – Skip past filename in command string">skip_filename</a></span></td> </tr> <tr id="addr-A371"> <td class="addr"><a href="#addr-A371">A371</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check for argument</span></td> </tr> <tr id="addr-A373"> <td class="addr"><a href="#addr-A373">A373</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get first char</span></td> </tr> <tr id="addr-A375"> <td class="addr"><a href="#addr-A375">A375</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Is it printable?</span></td> </tr> <tr id="addr-A377"> <td class="addr"><a href="#addr-A377">A377</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A389" data-tip="&A389">bad_command_error</a></span> <span class="comment">; No: end of command</span></td> </tr> <tr id="addr-A379"> <td class="addr"><a href="#addr-A379">A379</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore text pointer low</span></td> </tr> <tr id="addr-A37A"> <td class="addr"><a href="#addr-A37A">A37A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store in (&B4)</span></td> </tr> <tr id="addr-A37C"> <td class="addr"><a href="#addr-A37C">A37C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span></span> <span class="comment">; Also in OSFILE block</span></td> </tr> <tr id="addr-A37F"> <td class="addr"><a href="#addr-A37F">A37F</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore text pointer high</span></td> </tr> <tr id="addr-A380"> <td class="addr"><a href="#addr-A380">A380</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store in (&B5)</span></td> </tr> <tr id="addr-A382"> <td class="addr"><a href="#addr-A382">A382</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1041">wksp_osfile_block_1</span></span> <span class="comment">; Also in OSFILE block+1</span></td> </tr> <tr id="addr-A385"> <td class="addr"><a href="#addr-A385">A385</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-A386"> <td class="addr"><a href="#addr-A386">A386</a></td> <td><span class="label">.restore_csd_and_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A3B7">← A3B7 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A473" data-tip="&A473 – Restore CSD sector from saved copy">restore_csd</a></span></td> </tr> <tr id="addr-A389"> <td class="addr"><a href="#addr-A389">A389</a></td> <td><span class="label">.bad_command_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A377">← A377 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-A38C"> <td class="addr"><a href="#addr-A38C">A38C</a></td> <td> <span class="directive">EQUB</span> <span data-tip="254 &FE %11111110">&FE</span> <span class="comment">; Error &FE: Bad command</span></td> </tr> <tr id="addr-A38D"> <td class="addr"><a href="#addr-A38D">A38D</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Bad command"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A399"> <td colspan="2"><div class="sub-header"> <h3>*RUN command handler</h3> <div class="sub-desc"><p>Load and execute a file. Sets the execution address from the file's catalogue entry.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A399">A399</a></td> <td><span class="label">.star_run</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save filename pointer for retry</span></td> </tr> <tr id="addr-A39B"> <td class="addr"><a href="#addr-A39B">A39B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00C0">zp_name_ptr_lo</span></span> <span class="comment">; Store in save area low</span></td> </tr> <tr id="addr-A39D"> <td class="addr"><a href="#addr-A39D">A39D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Get filename high byte</span></td> </tr> <tr id="addr-A39F"> <td class="addr"><a href="#addr-A39F">A39F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00C1">zp_name_ptr_hi</span></span> <span class="comment">; Store in save area high</span></td> </tr> <tr id="addr-A3A1"> <td class="addr"><a href="#addr-A3A1">A3A1</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8BB3" data-tip="&8BB3 – Search for non-directory file">search_for_file</a></span> <span class="comment">; Try to find file in CSD</span></td> </tr> <tr id="addr-A3A4"> <td class="addr"><a href="#addr-A3A4">A3A4</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A3BC" data-tip="&A3BC">search_lib_for_command</a></span> <span class="comment">; Found in CSD, proceed to load</span></td> </tr> <tr id="addr-A3A6"> <td class="addr"><a href="#addr-A3A6">A3A6</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Not found: save workspace state</span></td> </tr> <tr id="addr-A3A9"> <td class="addr"><a href="#addr-A3A9">A3A9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00C0">zp_name_ptr_lo</span></span> <span class="comment">; Restore filename pointer</span></td> </tr> <tr id="addr-A3AB"> <td class="addr"><a href="#addr-A3AB">A3AB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Restore filename low</span></td> </tr> <tr id="addr-A3AD"> <td class="addr"><a href="#addr-A3AD">A3AD</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00C1">zp_name_ptr_hi</span></span> <span class="comment">; Get saved high byte</span></td> </tr> <tr id="addr-A3AF"> <td class="addr"><a href="#addr-A3AF">A3AF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Restore filename high</span></td> </tr> <tr id="addr-A3B1"> <td class="addr"><a href="#addr-A3B1">A3B1</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A460" data-tip="&A460 – Switch CSD to library directory">switch_to_library</a></span> <span class="comment">; Switch CSD to library directory</span></td> </tr> <tr id="addr-A3B4"> <td class="addr"><a href="#addr-A3B4">A3B4</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8BB3" data-tip="&8BB3 – Search for non-directory file">search_for_file</a></span> <span class="comment">; Try to find file in library</span></td> </tr> <tr id="addr-A3B7"> <td class="addr"><a href="#addr-A3B7">A3B7</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A386" data-tip="&A386">restore_csd_and_error</a></span> <span class="comment">; Not in library either: Not found</span></td> </tr> <tr id="addr-A3B9"> <td class="addr"><a href="#addr-A3B9">A3B9</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A473" data-tip="&A473 – Restore CSD sector from saved copy">restore_csd</a></span> <span class="comment">; Restore CSD after library search</span></td> </tr> <tr id="addr-A3BC"> <td class="addr"><a href="#addr-A3BC">A3BC</a></td> <td><span class="label">.search_lib_for_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A3A4">← A3A4 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save filename address for OSFILE</span></td> </tr> <tr id="addr-A3BE"> <td class="addr"><a href="#addr-A3BE">A3BE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A2">wksp_copy_read_sector</span></span> <span class="comment">; Store filename addr for OSFILE</span></td> </tr> <tr id="addr-A3C1"> <td class="addr"><a href="#addr-A3C1">A3C1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Get filename pointer high</span></td> </tr> <tr id="addr-A3C3"> <td class="addr"><a href="#addr-A3C3">A3C3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A3">wksp_copy_read_sector_1</span></span> <span class="comment">; Store in OSFILE block</span></td> </tr> <tr id="addr-A3C6"> <td class="addr"><a href="#addr-A3C6">A3C6</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="14 &0E %00001110 SO">#&0e</span></span> <span class="comment">; Y=&0E: check exec address bytes</span></td> </tr> <tr id="addr-A3C8"> <td class="addr"><a href="#addr-A3C8">A3C8</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get exec addr byte 0</span></td> </tr> <tr id="addr-A3CA"> <td class="addr"><a href="#addr-A3CA">A3CA</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: AND with bytes 1 and 2</span></td> </tr> <tr id="addr-A3CC"> <td class="addr"><a href="#addr-A3CC">A3CC</a></td> <td><span class="label">.copy_run_params_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A3D0">← A3D0 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; AND exec addr bytes together</span></td> </tr> <tr id="addr-A3CD"> <td class="addr"><a href="#addr-A3CD">A3CD</a></td> <td> <span class="opcode">AND</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; AND exec addr bytes together</span></td> </tr> <tr id="addr-A3CF"> <td class="addr"><a href="#addr-A3CF">A3CF</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A3D0"> <td class="addr"><a href="#addr-A3D0">A3D0</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A3CC" data-tip="&A3CC">copy_run_params_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A3D2"> <td class="addr"><a href="#addr-A3D2">A3D2</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; All &FF? Exec addr = &FFFFFFFF</span></td> </tr> <tr id="addr-A3D4"> <td class="addr"><a href="#addr-A3D4">A3D4</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A3E9" data-tip="&A3E9">execute_loaded_file</a></span> <span class="comment">; No, check load address</span></td> </tr> <tr id="addr-A3D6"> <td class="addr"><a href="#addr-A3D6">A3D6</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span></span> <span class="comment">; Exec = &FFFFFFFF: open with OSFIND</span></td> </tr> <tr id="addr-A3D8"> <td class="addr"><a href="#addr-A3D8">A3D8</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B7">zp_entry_ptr_hi</span></span> <span class="comment">; Get directory entry pointer high</span></td> </tr> <tr id="addr-A3DA"> <td class="addr"><a href="#addr-A3DA">A3DA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; A=&40: open for reading</span></td> </tr> <tr id="addr-A3DC"> <td class="addr"><a href="#addr-A3DC">A3DC</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B1B6" data-tip="&B1B6 – OSFIND handler">osfind_handler</a></span> <span class="comment">; Open the file</span></td> </tr> <tr id="addr-A3DF"> <td class="addr"><a href="#addr-A3DF">A3DF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1132">wksp_exec_handle</span></span> <span class="comment">; Save handle for *EXEC</span></td> </tr> <tr id="addr-A3E2"> <td class="addr"><a href="#addr-A3E2">A3E2</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="133 &85 %10000101">#&85</span></span> <span class="comment">; Point to "E.$.!BOOT" string</span></td> </tr> <tr id="addr-A3E4"> <td class="addr"><a href="#addr-A3E4">A3E4</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="154 &9A %10011010">#&9a</span></span> <span class="comment">; Y: high byte of E.$.!BOOT string</span></td> </tr> <tr id="addr-A3E6"> <td class="addr"><a href="#addr-A3E6">A3E6</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><span class="ext-label" data-tip="&FFF7">oscli</span></span> <span class="comment">; Execute via OSCLI</span></td> </tr> <tr id="addr-A3E9"> <td class="addr"><a href="#addr-A3E9">A3E9</a></td> <td><span class="label">.execute_loaded_file<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A3D4">← A3D4 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="11 &0B %00001011 VT">#&0b</span></span> <span class="comment">; Y=&0B: check load addr bytes</span></td> </tr> <tr id="addr-A3EB"> <td class="addr"><a href="#addr-A3EB">A3EB</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get load addr byte 1</span></td> </tr> <tr id="addr-A3ED"> <td class="addr"><a href="#addr-A3ED">A3ED</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-A3EE"> <td class="addr"><a href="#addr-A3EE">A3EE</a></td> <td> <span class="opcode">AND</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; AND with byte 2</span></td> </tr> <tr id="addr-A3F0"> <td class="addr"><a href="#addr-A3F0">A3F0</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-A3F1"> <td class="addr"><a href="#addr-A3F1">A3F1</a></td> <td> <span class="opcode">AND</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; AND with byte 3</span></td> </tr> <tr id="addr-A3F3"> <td class="addr"><a href="#addr-A3F3">A3F3</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; All &FF? Load addr = &FFFFFFFF</span></td> </tr> <tr id="addr-A3F5"> <td class="addr"><a href="#addr-A3F5">A3F5</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A401" data-tip="&A401">run_tube_transfer</a></span> <span class="comment">; No, proceed with load and execute</span></td> </tr> <tr id="addr-A3F7"> <td class="addr"><a href="#addr-A3F7">A3F7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-A3FA"> <td class="addr"><a href="#addr-A3FA">A3FA</a></td> <td> <span class="directive">EQUB</span> <span data-tip="147 &93 %10010011">&93</span> <span class="comment">; Error &93: Wont</span></td> </tr> <tr id="addr-A3FB"> <td class="addr"><a href="#addr-A3FB">A3FB</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Won't"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-A401"> <td class="addr"><a href="#addr-A401">A401</a></td> <td><span class="label">.run_tube_transfer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A3F5">← A3F5 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="165 &A5 %10100101">#&a5</span></span> <span class="comment">; Set up OSFILE block for load</span></td> </tr> <tr id="addr-A403"> <td class="addr"><a href="#addr-A403">A403</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A8">wksp_copy_src_sector</span></span> <span class="comment">; Store exec addr for later JMP</span></td> </tr> <tr id="addr-A406"> <td class="addr"><a href="#addr-A406">A406</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="162 &A2 %10100010">#&a2</span></span> <span class="comment">; X=&A2: OSFILE block offset</span></td> </tr> <tr id="addr-A408"> <td class="addr"><a href="#addr-A408">A408</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: OSFILE block page</span></td> </tr> <tr id="addr-A40A"> <td class="addr"><a href="#addr-A40A">A40A</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store block pointer low</span></td> </tr> <tr id="addr-A40C"> <td class="addr"><a href="#addr-A40C">A40C</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store block pointer high</span></td> </tr> <tr id="addr-A40E"> <td class="addr"><a href="#addr-A40E">A40E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8BB3" data-tip="&8BB3 – Search for non-directory file">search_for_file</a></span> <span class="comment">; Load the file</span></td> </tr> <tr id="addr-A411"> <td class="addr"><a href="#addr-A411">A411</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Y=4: check if Tube/IO address</span></td> </tr> <tr id="addr-A413"> <td class="addr"><a href="#addr-A413">A413</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get exec addr high byte</span></td> </tr> <tr id="addr-A415"> <td class="addr"><a href="#addr-A415">A415</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check low byte of exec addr</span></td> </tr> <tr id="addr-A417"> <td class="addr"><a href="#addr-A417">A417</a></td> <td> <span class="opcode">ORA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; OR with lowest byte</span></td> </tr> <tr id="addr-A419"> <td class="addr"><a href="#addr-A419">A419</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-A41E" data-tip="&A41E">run_set_exec_addr</a></span> <span class="comment">; Bit 7 set: I/O or Tube address</span></td> </tr> <tr id="addr-A41B"> <td class="addr"><a href="#addr-A41B">A41B</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8BF0" data-tip="&8BF0">validate_found_entry</a></span> <span class="comment">; Host address: jump directly</span></td> </tr> <tr id="addr-A41E"> <td class="addr"><a href="#addr-A41E">A41E</a></td> <td><span class="label">.run_set_exec_addr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A419">← A419 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8C10" data-tip="&8C10">create_new_dir_entry</a></span> <span class="comment">; Set up Tube transfer</span></td> </tr> <tr id="addr-A421"> <td class="addr"><a href="#addr-A421">A421</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10AB">wksp_copy_dest_sector</span></span> <span class="comment">; Check exec addr for &FFxx (Tube)</span></td> </tr> <tr id="addr-A424"> <td class="addr"><a href="#addr-A424">A424</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; High byte = &FF (Tube address)?</span></td> </tr> <tr id="addr-A426"> <td class="addr"><a href="#addr-A426">A426</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A434" data-tip="&A434">run_jump_to_file</a></span> <span class="comment">; No, check further</span></td> </tr> <tr id="addr-A428"> <td class="addr"><a href="#addr-A428">A428</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10AA">wksp_copy_src_sector_2</span></span> <span class="comment">; Get next exec addr byte</span></td> </tr> <tr id="addr-A42B"> <td class="addr"><a href="#addr-A42B">A42B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="254 &FE %11111110">#&fe</span></span> <span class="comment">; Is it >= &FE (I/O space)?</span></td> </tr> <tr id="addr-A42D"> <td class="addr"><a href="#addr-A42D">A42D</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A434" data-tip="&A434">run_jump_to_file</a></span> <span class="comment">; No, normal Tube address</span></td> </tr> <tr id="addr-A42F"> <td class="addr"><a href="#addr-A42F">A42F</a></td> <td><span class="label">.copy_exec_addr_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A436">← A436 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: language entry point</span></td> </tr> <tr id="addr-A431"> <td class="addr"><a href="#addr-A431">A431</a></td> <td> <span class="opcode">JMP</span> <span class="operand">(<span class="ext-label" data-tip="&10A8">wksp_copy_src_sector</span>)</span> <span class="comment">; Jump to execution address</span></td> </tr> <tr id="addr-A434"> <td class="addr"><a href="#addr-A434">A434</a></td> <td><span class="label">.run_jump_to_file<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A426">← A426 BNE</a><a href="#addr-A42D">← A42D BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Check if Tube present</span></td> </tr> <tr id="addr-A436"> <td class="addr"><a href="#addr-A436">A436</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A42F" data-tip="&A42F">copy_exec_addr_loop</a></span> <span class="comment">; No Tube: execute directly</span></td> </tr> <tr id="addr-A438"> <td class="addr"><a href="#addr-A438">A438</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-803B" data-tip="&803B">claim_tube_retry</a></span> <span class="comment">; Tube: set up Tube transfer</span></td> </tr> <tr id="addr-A43B"> <td class="addr"><a href="#addr-A43B">A43B</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="168 &A8 %10101000">#&a8</span></span> <span class="comment">; Point to exec addr block</span></td> </tr> <tr id="addr-A43D"> <td class="addr"><a href="#addr-A43D">A43D</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: Tube workspace page</span></td> </tr> <tr id="addr-A43F"> <td class="addr"><a href="#addr-A43F">A43F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; A=4: Tube transfer type</span></td> </tr> <tr id="addr-A441"> <td class="addr"><a href="#addr-A441">A441</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><span class="ext-label" data-tip="&0406">tube_entry</span></span> <span class="comment">; Start Tube execution</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A444"> <td colspan="2"><div class="sub-header"> <h3>*LIB command handler</h3> <div class="sub-desc"><p>Change the current library directory. The library is searched for commands not found in the current directory.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A444">A444</a></td> <td><span class="label">.star_lib</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-947F" data-tip="&947F – Parse path and load target directory">parse_path_and_load</a></span> <span class="comment">; Parse path and load target dir</span></td> </tr> <tr id="addr-A447"> <td class="addr"><a href="#addr-A447">A447</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy 10-byte directory name</span></td> </tr> <tr id="addr-A449"> <td class="addr"><a href="#addr-A449">A449</a></td> <td><span class="label">.copy_lib_name_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A450">← A450 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&16CC">dir_name</span>,y</span> <span class="comment">; Get name byte from dir buffer</span></td> </tr> <tr id="addr-A44C"> <td class="addr"><a href="#addr-A44C">A44C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&110A">wksp_lib_name</span>,y</span> <span class="comment">; Store as library name</span></td> </tr> <tr id="addr-A44F"> <td class="addr"><a href="#addr-A44F">A44F</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte in name copy</span></td> </tr> <tr id="addr-A450"> <td class="addr"><a href="#addr-A450">A450</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A449" data-tip="&A449">copy_lib_name_loop</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A452"> <td class="addr"><a href="#addr-A452">A452</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4-byte sector+drive</span></td> </tr> <tr id="addr-A454"> <td class="addr"><a href="#addr-A454">A454</a></td> <td><span class="label">.copy_lib_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A45B">← A45B BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get sector address byte</span></td> </tr> <tr id="addr-A457"> <td class="addr"><a href="#addr-A457">A457</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1118">wksp_lib_sector</span>,y</span> <span class="comment">; Store as library sector</span></td> </tr> <tr id="addr-A45A"> <td class="addr"><a href="#addr-A45A">A45A</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte in sector copy</span></td> </tr> <tr id="addr-A45B"> <td class="addr"><a href="#addr-A45B">A45B</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A454" data-tip="&A454">copy_lib_sector_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A45D"> <td class="addr"><a href="#addr-A45D">A45D</a></td> <td><span class="label">.save_workspace_and_return<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A471">← A471 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A460"> <td colspan="2"><div class="sub-header"> <h3>Switch CSD to library directory</h3> <div class="sub-desc"><p>Save the current CSD sector address, then replace it with the library directory sector address. Used before *LCAT and *LEX to temporarily operate on the library.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A460">A460</a></td> <td><span class="label">.switch_to_library<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-A3B1">← A3B1 JSR</a><a href="#addr-A47F">← A47F JSR</a><a href="#addr-A48B">← A48B JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4 bytes</span></td> </tr> <tr id="addr-A462"> <td class="addr"><a href="#addr-A462">A462</a></td> <td><span class="label">.swap_csd_to_lib_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A46F">← A46F BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Save current CSD sector</span></td> </tr> <tr id="addr-A465"> <td class="addr"><a href="#addr-A465">A465</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1030">wksp_temp_sector</span>,y</span> <span class="comment">; To temporary workspace</span></td> </tr> <tr id="addr-A468"> <td class="addr"><a href="#addr-A468">A468</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1118">wksp_lib_sector</span>,y</span> <span class="comment">; Get library sector</span></td> </tr> <tr id="addr-A46B"> <td class="addr"><a href="#addr-A46B">A46B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,y</span> <span class="comment">; Set as CSD sector</span></td> </tr> <tr id="addr-A46E"> <td class="addr"><a href="#addr-A46E">A46E</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A46F"> <td class="addr"><a href="#addr-A46F">A46F</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A462" data-tip="&A462">swap_csd_to_lib_loop</a></span> <span class="comment">; Always branch (loop back)</span></td> </tr> <tr id="addr-A471"> <td class="addr"><a href="#addr-A471">A471</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-A45D" data-tip="&A45D">save_workspace_and_return</a></span> <span class="comment">; Load the library directory</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A473"> <td colspan="2"><div class="sub-header"> <h3>Restore CSD sector from saved copy</h3> <div class="sub-desc"><p>Restore the CSD sector address from the temporary save in wksp_1030. Used after *LCAT/*LEX to switch back.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A473">A473</a></td> <td><span class="label">.restore_csd<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-A386">← A386 JSR</a><a href="#addr-A3B9">← A3B9 JSR</a><a href="#addr-A482">← A482 JSR</a><a href="#addr-A48E">← A48E JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4 bytes</span></td> </tr> <tr id="addr-A475"> <td class="addr"><a href="#addr-A475">A475</a></td> <td><span class="label">.restore_csd_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A47C">← A47C BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1030">wksp_temp_sector</span>,y</span> <span class="comment">; Get saved CSD sector</span></td> </tr> <tr id="addr-A478"> <td class="addr"><a href="#addr-A478">A478</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,y</span> <span class="comment">; Restore to CSD workspace</span></td> </tr> <tr id="addr-A47B"> <td class="addr"><a href="#addr-A47B">A47B</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A47C"> <td class="addr"><a href="#addr-A47C">A47C</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A475" data-tip="&A475">restore_csd_sector_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A47E"> <td class="addr"><a href="#addr-A47E">A47E</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A47F"> <td colspan="2"><div class="sub-header"> <h3>*LCAT command handler</h3> <div class="sub-desc"><p>Display a catalogue of the current library directory.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A47F">A47F</a></td> <td><span class="label">.star_lcat</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A460" data-tip="&A460 – Switch CSD to library directory">switch_to_library</a></span> <span class="comment">; Switch CSD to library</span></td> </tr> <tr id="addr-A482"> <td class="addr"><a href="#addr-A482">A482</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A473" data-tip="&A473 – Restore CSD sector from saved copy">restore_csd</a></span> <span class="comment">; Restore CSD after catalogue</span></td> </tr> <tr id="addr-A485"> <td class="addr"><a href="#addr-A485">A485</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-93D4" data-tip="&93D4">print_cat_header_and_entries</a></span> <span class="comment">; Print catalogue (*CAT format)</span></td> </tr> <tr id="addr-A488"> <td class="addr"><a href="#addr-A488">A488</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A48B"> <td colspan="2"><div class="sub-header"> <h3>*LEX command handler</h3> <div class="sub-desc"><p>Display a full catalogue of the current library directory, with the same format as *EX.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A48B">A48B</a></td> <td><span class="label">.star_lex</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A460" data-tip="&A460 – Switch CSD to library directory">switch_to_library</a></span> <span class="comment">; Switch CSD to library</span></td> </tr> <tr id="addr-A48E"> <td class="addr"><a href="#addr-A48E">A48E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A473" data-tip="&A473 – Restore CSD sector from saved copy">restore_csd</a></span> <span class="comment">; Restore CSD after display</span></td> </tr> <tr id="addr-A491"> <td class="addr"><a href="#addr-A491">A491</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-9436" data-tip="&9436">load_dir_and_list_entries</a></span> <span class="comment">; Print full catalogue (*EX format)</span></td> </tr> <tr id="addr-A494"> <td class="addr"><a href="#addr-A494">A494</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A497"> <td colspan="2"><div class="sub-header"> <h3>*BACK command handler</h3> <div class="sub-desc"><p>Switch the current directory to the previously selected directory and vice versa.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A497">A497</a></td> <td><span class="label">.star_back</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: swap 4 bytes of sector+drive</span></td> </tr> <tr id="addr-A499"> <td class="addr"><a href="#addr-A499">A499</a></td> <td><span class="label">.swap_dir_sectors_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4A6">← A4A6 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&111C">wksp_prev_dir_sector</span>,y</span> <span class="comment">; Get previous dir sector byte</span></td> </tr> <tr id="addr-A49C"> <td class="addr"><a href="#addr-A49C">A49C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,y</span> <span class="comment">; Store as CSD sector</span></td> </tr> <tr id="addr-A49F"> <td class="addr"><a href="#addr-A49F">A49F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get current CSD sector byte</span></td> </tr> <tr id="addr-A4A2"> <td class="addr"><a href="#addr-A4A2">A4A2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&111C">wksp_prev_dir_sector</span>,y</span> <span class="comment">; Store as previous dir sector</span></td> </tr> <tr id="addr-A4A5"> <td class="addr"><a href="#addr-A4A5">A4A5</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A4A6"> <td class="addr"><a href="#addr-A4A6">A4A6</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A499" data-tip="&A499">swap_dir_sectors_loop</a></span> <span class="comment">; Loop for 4 bytes (sector+drive)</span></td> </tr> <tr id="addr-A4A8"> <td class="addr"><a href="#addr-A4A8">A4A8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Reload directory from new sector</span></td> </tr> <tr id="addr-A4AB"> <td class="addr"><a href="#addr-A4AB">A4AB</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy 10-byte directory name</span></td> </tr> <tr id="addr-A4AD"> <td class="addr"><a href="#addr-A4AD">A4AD</a></td> <td><span class="label">.copy_prev_dir_name_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4B4">← A4B4 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&16CC">dir_name</span>,y</span> <span class="comment">; Get dir name from buffer</span></td> </tr> <tr id="addr-A4B0"> <td class="addr"><a href="#addr-A4B0">A4B0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1100">wksp_csd_name</span>,y</span> <span class="comment">; Store as CSD name</span></td> </tr> <tr id="addr-A4B3"> <td class="addr"><a href="#addr-A4B3">A4B3</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A4B4"> <td class="addr"><a href="#addr-A4B4">A4B4</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A4AD" data-tip="&A4AD">copy_prev_dir_name_loop</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A4B6"> <td class="addr"><a href="#addr-A4B6">A4B6</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A4B7"> <td colspan="2"><div class="sub-header"> <h3>Skip past filename in command string</h3> <div class="sub-desc"><p>Advance (&B4) past the next filename component in the command string, handling dots as path separators.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A4B7">A4B7</a></td> <td><span class="label">.skip_filename<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A365">← A365 JSR</a><a href="#addr-A36E">← A36E JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: start scanning</span></td> </tr> <tr id="addr-A4B9"> <td class="addr"><a href="#addr-A4B9">A4B9</a></td> <td><span class="label">.scan_filename_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4BF">← A4BF BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-871A" data-tip="&871A – Check if character is a filename terminator">check_char_is_terminator</a></span> <span class="comment">; Check if char is a terminator</span></td> </tr> <tr id="addr-A4BC"> <td class="addr"><a href="#addr-A4BC">A4BC</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A4C1" data-tip="&A4C1">check_dot_separator</a></span> <span class="comment">; Yes, check if it's a dot</span></td> </tr> <tr id="addr-A4BE"> <td class="addr"><a href="#addr-A4BE">A4BE</a></td> <td><span class="label">.advance_past_char<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4C3">← A4C3 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Advance past non-terminator</span></td> </tr> <tr id="addr-A4BF"> <td class="addr"><a href="#addr-A4BF">A4BF</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A4B9" data-tip="&A4B9">scan_filename_loop</a></span> <span class="comment">; Loop scanning characters</span></td> </tr> <tr id="addr-A4C1"> <td class="addr"><a href="#addr-A4C1">A4C1</a></td> <td><span class="label">.check_dot_separator<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4BC">← A4BC BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="46 &2E %00101110 '.'">#&2e</span></span> <span class="comment">; Is terminator a dot?</span></td> </tr> <tr id="addr-A4C3"> <td class="addr"><a href="#addr-A4C3">A4C3</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A4BE" data-tip="&A4BE">advance_past_char</a></span> <span class="comment">; Yes, skip dot and continue</span></td> </tr> <tr id="addr-A4C5"> <td class="addr"><a href="#addr-A4C5">A4C5</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Y = number of chars scanned</span></td> </tr> <tr id="addr-A4C6"> <td class="addr"><a href="#addr-A4C6">A4C6</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-A4C7"> <td class="addr"><a href="#addr-A4C7">A4C7</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Add to (&B4) to advance pointer</span></td> </tr> <tr id="addr-A4C9"> <td class="addr"><a href="#addr-A4C9">A4C9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store updated pointer low</span></td> </tr> <tr id="addr-A4CB"> <td class="addr"><a href="#addr-A4CB">A4CB</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A4CF" data-tip="&A4CF – Skip leading spaces in command argument">skip_spaces</a></span></td> </tr> <tr id="addr-A4CD"> <td class="addr"><a href="#addr-A4CD">A4CD</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Increment pointer high on overflow</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A4CF"> <td colspan="2"><div class="sub-header"> <h3>Skip leading spaces in command argument</h3> <div class="sub-desc"><p>Advance (&B4) past leading spaces. Also handles double-quoted strings (skips to closing quote).</p> <p>On exit: (&B4) points past the skipped characters</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A4CF">A4CF</a></td> <td><span class="label">.skip_spaces<span class="ref-badge">←11</span><span class="ref-popup"><a href="#addr-870F">← 870F JSR</a><a href="#addr-87E7">← 87E7 JSR</a><a href="#addr-8917">← 8917 JSR</a><a href="#addr-9109">← 9109 JSR</a><a href="#addr-93CE">← 93CE JSR</a><a href="#addr-9E8A">← 9E8A JSR</a><a href="#addr-9ECD">← 9ECD JSR</a><a href="#addr-A0F5">← A0F5 JSR</a><a href="#addr-A258">← A258 JSR</a><a href="#addr-A276">← A276 JSR</a><a href="#addr-A4CB">← A4CB BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: start scanning</span></td> </tr> <tr id="addr-A4D1"> <td class="addr"><a href="#addr-A4D1">A4D1</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; C=0: not inside quotes</span></td> </tr> <tr id="addr-A4D2"> <td class="addr"><a href="#addr-A4D2">A4D2</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save quote tracking flag</span></td> </tr> <tr id="addr-A4D3"> <td class="addr"><a href="#addr-A4D3">A4D3</a></td> <td><span class="label">.scan_spaces_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4E8">← A4E8 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get character from command line</span></td> </tr> <tr id="addr-A4D5"> <td class="addr"><a href="#addr-A4D5">A4D5</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Compare with space</span></td> </tr> <tr id="addr-A4D7"> <td class="addr"><a href="#addr-A4D7">A4D7</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A4EA" data-tip="&A4EA">end_of_spaces</a></span> <span class="comment">; Control char: end of argument</span></td> </tr> <tr id="addr-A4D9"> <td class="addr"><a href="#addr-A4D9">A4D9</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A4E7" data-tip="&A4E7">advance_and_continue</a></span> <span class="comment">; Space: skip it</span></td> </tr> <tr id="addr-A4DB"> <td class="addr"><a href="#addr-A4DB">A4DB</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="34 &22 %00100010 '"'">#&22</span></span> <span class="comment">; Double-quote?</span></td> </tr> <tr id="addr-A4DD"> <td class="addr"><a href="#addr-A4DD">A4DD</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A4EA" data-tip="&A4EA">end_of_spaces</a></span> <span class="comment">; No, end of argument</span></td> </tr> <tr id="addr-A4DF"> <td class="addr"><a href="#addr-A4DF">A4DF</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore C (quote tracking flag)</span></td> </tr> <tr id="addr-A4E0"> <td class="addr"><a href="#addr-A4E0">A4E0</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A4E5" data-tip="&A4E5">enter_quoted_string</a></span> <span class="comment">; C=0 first quote: start quoted str</span></td> </tr> <tr id="addr-A4E2"> <td class="addr"><a href="#addr-A4E2">A4E2</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8737" data-tip="&8737">bad_name_error</a></span> <span class="comment">; C=1 second quote: bad name error</span></td> </tr> <tr id="addr-A4E5"> <td class="addr"><a href="#addr-A4E5">A4E5</a></td> <td><span class="label">.enter_quoted_string<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4E0">← A4E0 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">SEC</span> <span class="comment">; C=1: inside quoted string now</span></td> </tr> <tr id="addr-A4E6"> <td class="addr"><a href="#addr-A4E6">A4E6</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save updated quote flag</span></td> </tr> <tr id="addr-A4E7"> <td class="addr"><a href="#addr-A4E7">A4E7</a></td> <td><span class="label">.advance_and_continue<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A4D9">← A4D9 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Next character</span></td> </tr> <tr id="addr-A4E8"> <td class="addr"><a href="#addr-A4E8">A4E8</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A4D3" data-tip="&A4D3">scan_spaces_loop</a></span> <span class="comment">; Continue scanning</span></td> </tr> <tr id="addr-A4EA"> <td class="addr"><a href="#addr-A4EA">A4EA</a></td> <td><span class="label">.end_of_spaces<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A4D7">← A4D7 BCC</a><a href="#addr-A4DD">← A4DD BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Y = number of chars to skip</span></td> </tr> <tr id="addr-A4EB"> <td class="addr"><a href="#addr-A4EB">A4EB</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore quote flag</span></td> </tr> <tr id="addr-A4EC"> <td class="addr"><a href="#addr-A4EC">A4EC</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-A4ED"> <td class="addr"><a href="#addr-A4ED">A4ED</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Add to (&B4) to advance pointer</span></td> </tr> <tr id="addr-A4EF"> <td class="addr"><a href="#addr-A4EF">A4EF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store updated pointer low</span></td> </tr> <tr id="addr-A4F1"> <td class="addr"><a href="#addr-A4F1">A4F1</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A4F5" data-tip="&A4F5">return_30</a></span> <span class="comment">; No overflow, return</span></td> </tr> <tr id="addr-A4F3"> <td class="addr"><a href="#addr-A4F3">A4F3</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Increment pointer high on overflow</span></td> </tr> <tr id="addr-A4F5"> <td class="addr"><a href="#addr-A4F5">A4F5</a></td> <td><span class="label">.return_30<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A4F1">← A4F1 BCC</a><a href="#addr-A4FE">← A4FE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A4F6"> <td colspan="2"><div class="sub-header"> <h3>Check for drive specifier colon</h3> <div class="sub-desc"><p>Check if the next character at (&B4) is a colon, indicating a drive number follows.</p> <p>On exit: Z set if no colon found If colon found, jumps to parse drive number</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A4F6">A4F6</a></td> <td><span class="label">.check_drive_colon<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A509">← A509 JSR</a><a href="#addr-A547">← A547 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0</span></td> </tr> <tr id="addr-A4F8"> <td class="addr"><a href="#addr-A4F8">A4F8</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get next character</span></td> </tr> <tr id="addr-A4FA"> <td class="addr"><a href="#addr-A4FA">A4FA</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7</span></td> </tr> <tr id="addr-A4FC"> <td class="addr"><a href="#addr-A4FC">A4FC</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="58 &3A %00111010 ':'">#&3a</span></span> <span class="comment">; Is it ':'?</span></td> </tr> <tr id="addr-A4FE"> <td class="addr"><a href="#addr-A4FE">A4FE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A4F5" data-tip="&A4F5">return_30</a></span> <span class="comment">; No, return</span></td> </tr> <tr id="addr-A500"> <td class="addr"><a href="#addr-A500">A500</a></td> <td><span class="label">.parse_drive_specifier<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A532">← A532 BEQ</a><a href="#addr-A53B">← A53B BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8982" data-tip="&8982">clean_dir_rename_bit</a></span> <span class="comment">; Yes, parse drive number</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A503"> <td colspan="2"><div class="sub-header"> <h3>*RENAME command handler</h3> <div class="sub-desc"><p>Rename a file or move it between directories on the same drive. The source and destination must be on the same drive.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A503">A503</a></td> <td><span class="label">.star_rename</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save first argument pointer</span></td> </tr> <tr id="addr-A505"> <td class="addr"><a href="#addr-A505">A505</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save first arg pointer low</span></td> </tr> <tr id="addr-A506"> <td class="addr"><a href="#addr-A506">A506</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Get first arg pointer high</span></td> </tr> <tr id="addr-A508"> <td class="addr"><a href="#addr-A508">A508</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save on stack</span></td> </tr> <tr id="addr-A509"> <td class="addr"><a href="#addr-A509">A509</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4F6" data-tip="&A4F6 – Check for drive specifier colon">check_drive_colon</a></span> <span class="comment">; Check for drive specifier</span></td> </tr> <tr id="addr-A50C"> <td class="addr"><a href="#addr-A50C">A50C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8DBD" data-tip="&8DBD">set_up_gsinit_path</a></span> <span class="comment">; Parse and validate destination path</span></td> </tr> <tr id="addr-A50F"> <td class="addr"><a href="#addr-A50F">A50F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8BE5" data-tip="&8BE5">find_file_and_validate</a></span> <span class="comment">; Search for source file</span></td> </tr> <tr id="addr-A512"> <td class="addr"><a href="#addr-A512">A512</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A517" data-tip="&A517">source_is_found</a></span> <span class="comment">; Found?</span></td> </tr> <tr id="addr-A514"> <td class="addr"><a href="#addr-A514">A514</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8BC8" data-tip="&8BC8 – Generate Not found error">not_found_error</a></span> <span class="comment">; Not found: report error</span></td> </tr> <tr id="addr-A517"> <td class="addr"><a href="#addr-A517">A517</a></td> <td><span class="label">.source_is_found<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A512">← A512 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: check if source is directory</span></td> </tr> <tr id="addr-A519"> <td class="addr"><a href="#addr-A519">A519</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get source entry access byte</span></td> </tr> <tr id="addr-A51B"> <td class="addr"><a href="#addr-A51B">A51B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-A51E"> <td class="addr"><a href="#addr-A51E">A51E</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A544" data-tip="&A544">parse_destination_name</a></span> <span class="comment">; Not a directory: skip self-ref check</span></td> </tr> <tr id="addr-A520"> <td class="addr"><a href="#addr-A520">A520</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore first argument pointer</span></td> </tr> <tr id="addr-A521"> <td class="addr"><a href="#addr-A521">A521</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X for save</span></td> </tr> <tr id="addr-A522"> <td class="addr"><a href="#addr-A522">A522</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore first arg low from stack</span></td> </tr> <tr id="addr-A523"> <td class="addr"><a href="#addr-A523">A523</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store in (&B4)</span></td> </tr> <tr id="addr-A525"> <td class="addr"><a href="#addr-A525">A525</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Re-save on stack</span></td> </tr> <tr id="addr-A526"> <td class="addr"><a href="#addr-A526">A526</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Get saved high byte from X</span></td> </tr> <tr id="addr-A527"> <td class="addr"><a href="#addr-A527">A527</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store in (&B5)</span></td> </tr> <tr id="addr-A529"> <td class="addr"><a href="#addr-A529">A529</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save on stack</span></td> </tr> <tr id="addr-A52A"> <td class="addr"><a href="#addr-A52A">A52A</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check path for $ root ref</span></td> </tr> <tr id="addr-A52C"> <td class="addr"><a href="#addr-A52C">A52C</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Check for '$' (root specifier)</span></td> </tr> <tr id="addr-A52E"> <td class="addr"><a href="#addr-A52E">A52E</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="125 &7D %01111101 '}'">#&7d</span></span> <span class="comment">; Mask to ignore L and D bits</span></td> </tr> <tr id="addr-A530"> <td class="addr"><a href="#addr-A530">A530</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="36 &24 %00100100 '$'">#&24</span></span> <span class="comment">; Is it '$'?</span></td> </tr> <tr id="addr-A532"> <td class="addr"><a href="#addr-A532">A532</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A500" data-tip="&A500">parse_drive_specifier</a></span> <span class="comment">; Root: Bad rename error</span></td> </tr> <tr id="addr-A534"> <td class="addr"><a href="#addr-A534">A534</a></td> <td><span class="label">.scan_dest_for_parent_ref<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A53E">← A53E BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-871A" data-tip="&871A – Check if character is a filename terminator">check_char_is_terminator</a></span> <span class="comment">; Scan for '^' in destination path</span></td> </tr> <tr id="addr-A537"> <td class="addr"><a href="#addr-A537">A537</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A540" data-tip="&A540">check_dot_in_dest</a></span> <span class="comment">; Terminator found, check type</span></td> </tr> <tr id="addr-A539"> <td class="addr"><a href="#addr-A539">A539</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="94 &5E %01011110 '^'">#&5e</span></span> <span class="comment">; Is it '^' (parent)?</span></td> </tr> <tr id="addr-A53B"> <td class="addr"><a href="#addr-A53B">A53B</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A500" data-tip="&A500">parse_drive_specifier</a></span> <span class="comment">; Parent ref in dest: Bad rename error</span></td> </tr> <tr id="addr-A53D"> <td class="addr"><a href="#addr-A53D">A53D</a></td> <td><span class="label">.advance_dest_scan<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A542">← A542 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Next character in scan</span></td> </tr> <tr id="addr-A53E"> <td class="addr"><a href="#addr-A53E">A53E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A534" data-tip="&A534">scan_dest_for_parent_ref</a></span> <span class="comment">; Loop scanning destination path</span></td> </tr> <tr id="addr-A540"> <td class="addr"><a href="#addr-A540">A540</a></td> <td><span class="label">.check_dot_in_dest<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A537">← A537 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="46 &2E %00101110 '.'">#&2e</span></span> <span class="comment">; Check for '.' separator</span></td> </tr> <tr id="addr-A542"> <td class="addr"><a href="#addr-A542">A542</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A53D" data-tip="&A53D">advance_dest_scan</a></span> <span class="comment">; Dot separator: continue past it</span></td> </tr> <tr id="addr-A544"> <td class="addr"><a href="#addr-A544">A544</a></td> <td><span class="label">.parse_destination_name<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A51E">← A51E BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A365" data-tip="&A365 – Parse second filename from command line">parse_second_filename</a></span> <span class="comment">; Parse second arg (destination)</span></td> </tr> <tr id="addr-A547"> <td class="addr"><a href="#addr-A547">A547</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A4F6" data-tip="&A4F6 – Check for drive specifier colon">check_drive_colon</a></span></td> </tr> <tr id="addr-A54A"> <td class="addr"><a href="#addr-A54A">A54A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; Set up OSFILE block pointer</span></td> </tr> <tr id="addr-A54C"> <td class="addr"><a href="#addr-A54C">A54C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store low byte</span></td> </tr> <tr id="addr-A54E"> <td class="addr"><a href="#addr-A54E">A54E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Block page = &10</span></td> </tr> <tr id="addr-A550"> <td class="addr"><a href="#addr-A550">A550</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store high byte</span></td> </tr> <tr id="addr-A552"> <td class="addr"><a href="#addr-A552">A552</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8CE2" data-tip="&8CE2">build_osfile_control_block</a></span> <span class="comment">; Search for dest filename</span></td> </tr> <tr id="addr-A555"> <td class="addr"><a href="#addr-A555">A555</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save search result flags</span></td> </tr> <tr id="addr-A556"> <td class="addr"><a href="#addr-A556">A556</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8DF6" data-tip="&8DF6">search_dir_for_new_entry</a></span> <span class="comment">; Check directory state</span></td> </tr> <tr id="addr-A559"> <td class="addr"><a href="#addr-A559">A559</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-A55A"> <td class="addr"><a href="#addr-A55A">A55A</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A569" data-tip="&A569">check_alt_workspace</a></span> <span class="comment">; Dest not found: good for rename</span></td> </tr> <tr id="addr-A55C"> <td class="addr"><a href="#addr-A55C">A55C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span></span> <span class="comment">; Dest exists: save entry pointer</span></td> </tr> <tr id="addr-A55E"> <td class="addr"><a href="#addr-A55E">A55E</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy sector+entry info</span></td> </tr> <tr id="addr-A560"> <td class="addr"><a href="#addr-A560">A560</a></td> <td><span class="label">.save_dest_dir_info_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A567">← A567 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span>,y</span> <span class="comment">; Store in object sector workspace</span></td> </tr> <tr id="addr-A563"> <td class="addr"><a href="#addr-A563">A563</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1113">wksp_csd_sector</span>,y</span> <span class="comment">; Get CSD sector byte</span></td> </tr> <tr id="addr-A566"> <td class="addr"><a href="#addr-A566">A566</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A567"> <td class="addr"><a href="#addr-A567">A567</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A560" data-tip="&A560">save_dest_dir_info_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A569"> <td class="addr"><a href="#addr-A569">A569</a></td> <td><span class="label">.check_alt_workspace<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A55A">← A55A BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&102E">wksp_alt_sector_hi</span></span> <span class="comment">; Check if alt workspace is set</span></td> </tr> <tr id="addr-A56C"> <td class="addr"><a href="#addr-A56C">A56C</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A579" data-tip="&A579">reload_and_parse_source</a></span> <span class="comment">; Set: skip CSD restore</span></td> </tr> <tr id="addr-A56E"> <td class="addr"><a href="#addr-A56E">A56E</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: copy CSD sector from backup</span></td> </tr> <tr id="addr-A570"> <td class="addr"><a href="#addr-A570">A570</a></td> <td><span class="label">.restore_csd_sector_loop2<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A577">← A577 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get saved CSD sector byte</span></td> </tr> <tr id="addr-A573"> <td class="addr"><a href="#addr-A573">A573</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,y</span> <span class="comment">; Restore to CSD workspace</span></td> </tr> <tr id="addr-A576"> <td class="addr"><a href="#addr-A576">A576</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A577"> <td class="addr"><a href="#addr-A577">A577</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A570" data-tip="&A570">restore_csd_sector_loop2</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A579"> <td class="addr"><a href="#addr-A579">A579</a></td> <td><span class="label">.reload_and_parse_source<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A56C">← A56C BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and reload dir</span></td> </tr> <tr id="addr-A57C"> <td class="addr"><a href="#addr-A57C">A57C</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore second arg pointer</span></td> </tr> <tr id="addr-A57D"> <td class="addr"><a href="#addr-A57D">A57D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store in (&B5)</span></td> </tr> <tr id="addr-A57F"> <td class="addr"><a href="#addr-A57F">A57F</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Save in X</span></td> </tr> <tr id="addr-A580"> <td class="addr"><a href="#addr-A580">A580</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore first arg pointer</span></td> </tr> <tr id="addr-A581"> <td class="addr"><a href="#addr-A581">A581</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store in (&B4)</span></td> </tr> <tr id="addr-A583"> <td class="addr"><a href="#addr-A583">A583</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Re-save for later</span></td> </tr> <tr id="addr-A584"> <td class="addr"><a href="#addr-A584">A584</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Get high byte from X</span></td> </tr> <tr id="addr-A585"> <td class="addr"><a href="#addr-A585">A585</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Re-save</span></td> </tr> <tr id="addr-A586"> <td class="addr"><a href="#addr-A586">A586</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FDF" data-tip="&8FDF – Find first matching directory entry">find_first_matching_entry</a></span> <span class="comment">; Search source in original dir</span></td> </tr> <tr id="addr-A589"> <td class="addr"><a href="#addr-A589">A589</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8D10" data-tip="&8D10 – Check file is not locked or open">check_file_not_open</a></span> <span class="comment">; Check if file is open</span></td> </tr> <tr id="addr-A58C"> <td class="addr"><a href="#addr-A58C">A58C</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: compare directories</span></td> </tr> <tr id="addr-A58E"> <td class="addr"><a href="#addr-A58E">A58E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span></span> <span class="comment">; Get source entry pointer</span></td> </tr> <tr id="addr-A590"> <td class="addr"><a href="#addr-A590">A590</a></td> <td><span class="label">.compare_src_dest_dir_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A599">← A599 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span>,y</span> <span class="comment">; Compare with dest dir sector</span></td> </tr> <tr id="addr-A593"> <td class="addr"><a href="#addr-A593">A593</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A5EB" data-tip="&A5EB">cross_dir_rename</a></span> <span class="comment">; Different: cross-dir rename</span></td> </tr> <tr id="addr-A595"> <td class="addr"><a href="#addr-A595">A595</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1113">wksp_csd_sector</span>,y</span> <span class="comment">; Get CSD sector byte</span></td> </tr> <tr id="addr-A598"> <td class="addr"><a href="#addr-A598">A598</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A599"> <td class="addr"><a href="#addr-A599">A599</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A590" data-tip="&A590">compare_src_dest_dir_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A59B"> <td class="addr"><a href="#addr-A59B">A59B</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Same dir: restore dest name ptr</span></td> </tr> <tr id="addr-A59C"> <td class="addr"><a href="#addr-A59C">A59C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store in (&B5)</span></td> </tr> <tr id="addr-A59E"> <td class="addr"><a href="#addr-A59E">A59E</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore first arg low</span></td> </tr> <tr id="addr-A59F"> <td class="addr"><a href="#addr-A59F">A59F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store in (&B4)</span></td> </tr> <tr id="addr-A5A1"> <td class="addr"><a href="#addr-A5A1">A5A1</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A365" data-tip="&A365 – Parse second filename from command line">parse_second_filename</a></span> <span class="comment">; Parse last component of dest path</span></td> </tr> <tr id="addr-A5A4"> <td class="addr"><a href="#addr-A5A4">A5A4</a></td> <td><span class="label">.find_last_path_component<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A5BA">← A5BA BCC</a><a href="#addr-A5BE">← A5BE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: scan for end of path component</span></td> </tr> <tr id="addr-A5A6"> <td class="addr"><a href="#addr-A5A6">A5A6</a></td> <td><span class="label">.scan_component_chars<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5B3">← A5B3 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get next character</span></td> </tr> <tr id="addr-A5A8"> <td class="addr"><a href="#addr-A5A8">A5A8</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="46 &2E %00101110 '.'">#&2e</span></span> <span class="comment">; Is it '.' separator?</span></td> </tr> <tr id="addr-A5AA"> <td class="addr"><a href="#addr-A5AA">A5AA</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A5B5" data-tip="&A5B5">advance_past_component</a></span> <span class="comment">; Yes: advance past component</span></td> </tr> <tr id="addr-A5AC"> <td class="addr"><a href="#addr-A5AC">A5AC</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="125 &7D %01111101 '}'">#&7d</span></span> <span class="comment">; Strip to printable range</span></td> </tr> <tr id="addr-A5AE"> <td class="addr"><a href="#addr-A5AE">A5AE</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Control char: end of name</span></td> </tr> <tr id="addr-A5B0"> <td class="addr"><a href="#addr-A5B0">A5B0</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A5C0" data-tip="&A5C0">copy_new_name_to_entry</a></span> <span class="comment">; End of destination name found</span></td> </tr> <tr id="addr-A5B2"> <td class="addr"><a href="#addr-A5B2">A5B2</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next character</span></td> </tr> <tr id="addr-A5B3"> <td class="addr"><a href="#addr-A5B3">A5B3</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A5A6" data-tip="&A5A6">scan_component_chars</a></span> <span class="comment">; Loop scanning</span></td> </tr> <tr id="addr-A5B5"> <td class="addr"><a href="#addr-A5B5">A5B5</a></td> <td><span class="label">.advance_past_component<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5AA">← A5AA BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Advance pointer past component</span></td> </tr> <tr id="addr-A5B6"> <td class="addr"><a href="#addr-A5B6">A5B6</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Add Y to pointer</span></td> </tr> <tr id="addr-A5B8"> <td class="addr"><a href="#addr-A5B8">A5B8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store updated pointer</span></td> </tr> <tr id="addr-A5BA"> <td class="addr"><a href="#addr-A5BA">A5BA</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A5A4" data-tip="&A5A4">find_last_path_component</a></span> <span class="comment">; No carry: scan next component</span></td> </tr> <tr id="addr-A5BC"> <td class="addr"><a href="#addr-A5BC">A5BC</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Increment high byte on overflow</span></td> </tr> <tr id="addr-A5BE"> <td class="addr"><a href="#addr-A5BE">A5BE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A5A4" data-tip="&A5A4">find_last_path_component</a></span> <span class="comment">; Always branch back to scan</span></td> </tr> <tr id="addr-A5C0"> <td class="addr"><a href="#addr-A5C0">A5C0</a></td> <td><span class="label">.copy_new_name_to_entry<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5B0">← A5B0 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy 10-byte new name</span></td> </tr> <tr id="addr-A5C2"> <td class="addr"><a href="#addr-A5C2">A5C2</a></td> <td><span class="label">.merge_name_attributes_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5DD">← A5DD BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get old name byte (with attributes)</span></td> </tr> <tr id="addr-A5C4"> <td class="addr"><a href="#addr-A5C4">A5C4</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; Keep only bit 7 (attribute flag)</span></td> </tr> <tr id="addr-A5C6"> <td class="addr"><a href="#addr-A5C6">A5C6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102B">wksp_csd_sector_temp</span></span> <span class="comment">; Save attribute bit</span></td> </tr> <tr id="addr-A5C9"> <td class="addr"><a href="#addr-A5C9">A5C9</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get new name character</span></td> </tr> <tr id="addr-A5CB"> <td class="addr"><a href="#addr-A5CB">A5CB</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7</span></td> </tr> <tr id="addr-A5CD"> <td class="addr"><a href="#addr-A5CD">A5CD</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="34 &22 %00100010 '"'">#&22</span></span> <span class="comment">; Is it '"'?</span></td> </tr> <tr id="addr-A5CF"> <td class="addr"><a href="#addr-A5CF">A5CF</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A5D5" data-tip="&A5D5">pad_with_cr</a></span> <span class="comment">; Yes: pad with CR</span></td> </tr> <tr id="addr-A5D1"> <td class="addr"><a href="#addr-A5D1">A5D1</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Is it printable?</span></td> </tr> <tr id="addr-A5D3"> <td class="addr"><a href="#addr-A5D3">A5D3</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A5D7" data-tip="&A5D7">store_merged_name_byte</a></span> <span class="comment">; Yes: use as-is</span></td> </tr> <tr id="addr-A5D5"> <td class="addr"><a href="#addr-A5D5">A5D5</a></td> <td><span class="label">.pad_with_cr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5CF">← A5CF BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="13 &0D %00001101 CR">#&0d</span></span> <span class="comment">; Non-printable: use CR padding</span></td> </tr> <tr id="addr-A5D7"> <td class="addr"><a href="#addr-A5D7">A5D7</a></td> <td><span class="label">.store_merged_name_byte<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5D3">← A5D3 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&102B">wksp_csd_sector_temp</span></span> <span class="comment">; Merge attribute bit with new char</span></td> </tr> <tr id="addr-A5DA"> <td class="addr"><a href="#addr-A5DA">A5DA</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Store renamed byte in entry</span></td> </tr> <tr id="addr-A5DC"> <td class="addr"><a href="#addr-A5DC">A5DC</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A5DD"> <td class="addr"><a href="#addr-A5DD">A5DD</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A5C2" data-tip="&A5C2">merge_name_attributes_loop</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A5DF"> <td class="addr"><a href="#addr-A5DF">A5DF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write directory back to disc</span></td> </tr> <tr id="addr-A5E2"> <td class="addr"><a href="#addr-A5E2">A5E2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A685" data-tip="&A685">update_moved_dir_parent</a></span> <span class="comment">; Update directory checksums</span></td> </tr> <tr id="addr-A5E5"> <td class="addr"><a href="#addr-A5E5">A5E5</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return</span></td> </tr> <tr id="addr-A5E8"> <td class="addr"><a href="#addr-A5E8">A5E8</a></td> <td><span class="label">.already_exists_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A5EE">← A5EE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-95A4" data-tip="&95A4">already_exists_error2</a></span> <span class="comment">; Already exists: error</span></td> </tr> <tr id="addr-A5EB"> <td class="addr"><a href="#addr-A5EB">A5EB</a></td> <td><span class="label">.cross_dir_rename<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A593">← A593 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1037">wksp_object_size</span></span> <span class="comment">; Check if dest has zero size</span></td> </tr> <tr id="addr-A5EE"> <td class="addr"><a href="#addr-A5EE">A5EE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A5E8" data-tip="&A5E8">already_exists_error</a></span> <span class="comment">; Non-zero: Already exists error</span></td> </tr> <tr id="addr-A5F0"> <td class="addr"><a href="#addr-A5F0">A5F0</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: mark old entry as deleted</span></td> </tr> <tr id="addr-A5F2"> <td class="addr"><a href="#addr-A5F2">A5F2</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get last name byte</span></td> </tr> <tr id="addr-A5F4"> <td class="addr"><a href="#addr-A5F4">A5F4</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; Set bit 7 (mark as directory?)</span></td> </tr> <tr id="addr-A5F6"> <td class="addr"><a href="#addr-A5F6">A5F6</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Store back</span></td> </tr> <tr id="addr-A5F8"> <td class="addr"><a href="#addr-A5F8">A5F8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write source directory</span></td> </tr> <tr id="addr-A5FB"> <td class="addr"><a href="#addr-A5FB">A5FB</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; Y=&0A: copy entry data to workspace</span></td> </tr> <tr id="addr-A5FD"> <td class="addr"><a href="#addr-A5FD">A5FD</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; X=7: 8 bytes of entry metadata</span></td> </tr> <tr id="addr-A5FF"> <td class="addr"><a href="#addr-A5FF">A5FF</a></td> <td><span class="label">.copy_entry_metadata_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A606">← A606 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get entry data byte</span></td> </tr> <tr id="addr-A601"> <td class="addr"><a href="#addr-A601">A601</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1038">wksp_object_size_mid</span>,y</span> <span class="comment">; Store in workspace for dest entry</span></td> </tr> <tr id="addr-A604"> <td class="addr"><a href="#addr-A604">A604</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A605"> <td class="addr"><a href="#addr-A605">A605</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement counter</span></td> </tr> <tr id="addr-A606"> <td class="addr"><a href="#addr-A606">A606</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A5FF" data-tip="&A5FF">copy_entry_metadata_loop</a></span> <span class="comment">; Loop for 8 bytes</span></td> </tr> <tr id="addr-A608"> <td class="addr"><a href="#addr-A608">A608</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear OSFILE block fields</span></td> </tr> <tr id="addr-A60A"> <td class="addr"><a href="#addr-A60A">A60A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&104A">wksp_osfile_start_addr</span></span> <span class="comment">; Clear load address</span></td> </tr> <tr id="addr-A60D"> <td class="addr"><a href="#addr-A60D">A60D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&104B">wksp_osfile_start_addr_1</span></span> <span class="comment">; Clear exec address</span></td> </tr> <tr id="addr-A610"> <td class="addr"><a href="#addr-A610">A610</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&104C">wksp_osfile_start_addr_2</span></span> <span class="comment">; Clear length</span></td> </tr> <tr id="addr-A613"> <td class="addr"><a href="#addr-A613">A613</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&104D">wksp_osfile_start_addr_3</span></span> <span class="comment">; Clear attributes</span></td> </tr> <tr id="addr-A616"> <td class="addr"><a href="#addr-A616">A616</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: copy 3+1 start sector bytes</span></td> </tr> <tr id="addr-A618"> <td class="addr"><a href="#addr-A618">A618</a></td> <td><span class="label">.copy_entry_sector_loop2<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A61F">← A61F BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get sector byte from entry</span></td> </tr> <tr id="addr-A61A"> <td class="addr"><a href="#addr-A61A">A61A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&103C">wksp_saved_count_1</span>,y</span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-A61D"> <td class="addr"><a href="#addr-A61D">A61D</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A61E"> <td class="addr"><a href="#addr-A61E">A61E</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement counter</span></td> </tr> <tr id="addr-A61F"> <td class="addr"><a href="#addr-A61F">A61F</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A618" data-tip="&A618">copy_entry_sector_loop2</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A621"> <td class="addr"><a href="#addr-A621">A621</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: build access byte from entry</span></td> </tr> <tr id="addr-A623"> <td class="addr"><a href="#addr-A623">A623</a></td> <td><span class="label">.build_access_byte_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A62C">← A62C BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get name byte</span></td> </tr> <tr id="addr-A625"> <td class="addr"><a href="#addr-A625">A625</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Shift bit 7 into carry</span></td> </tr> <tr id="addr-A626"> <td class="addr"><a href="#addr-A626">A626</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&105D">wksp_access_accum</span></span> <span class="comment">; Rotate into access accumulator</span></td> </tr> <tr id="addr-A629"> <td class="addr"><a href="#addr-A629">A629</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next name byte</span></td> </tr> <tr id="addr-A62A"> <td class="addr"><a href="#addr-A62A">A62A</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Done 4 bytes?</span></td> </tr> <tr id="addr-A62C"> <td class="addr"><a href="#addr-A62C">A62C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A623" data-tip="&A623">build_access_byte_loop</a></span> <span class="comment">; No, continue building access</span></td> </tr> <tr id="addr-A62E"> <td class="addr"><a href="#addr-A62E">A62E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A365" data-tip="&A365 – Parse second filename from command line">parse_second_filename</a></span> <span class="comment">; Parse dest path and switch dir</span></td> </tr> <tr id="addr-A631"> <td class="addr"><a href="#addr-A631">A631</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="24 &18 %00011000 CAN">#&18</span></span> <span class="comment">; Y=&18: start sector in entry</span></td> </tr> <tr id="addr-A633"> <td class="addr"><a href="#addr-A633">A633</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: copy 3 sector bytes</span></td> </tr> <tr id="addr-A635"> <td class="addr"><a href="#addr-A635">A635</a></td> <td><span class="label">.copy_start_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A63C">← A63C BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get start sector byte</span></td> </tr> <tr id="addr-A637"> <td class="addr"><a href="#addr-A637">A637</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&103A">wksp_alloc_sector</span>,x</span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-A63A"> <td class="addr"><a href="#addr-A63A">A63A</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte (decreasing)</span></td> </tr> <tr id="addr-A63B"> <td class="addr"><a href="#addr-A63B">A63B</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next workspace byte</span></td> </tr> <tr id="addr-A63C"> <td class="addr"><a href="#addr-A63C">A63C</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A635" data-tip="&A635">copy_start_sector_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A63E"> <td class="addr"><a href="#addr-A63E">A63E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-A641"> <td class="addr"><a href="#addr-A641">A641</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; Set up OSFILE block for create</span></td> </tr> <tr id="addr-A643"> <td class="addr"><a href="#addr-A643">A643</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store block pointer low</span></td> </tr> <tr id="addr-A645"> <td class="addr"><a href="#addr-A645">A645</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Block page = &10</span></td> </tr> <tr id="addr-A647"> <td class="addr"><a href="#addr-A647">A647</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store block pointer high</span></td> </tr> <tr id="addr-A649"> <td class="addr"><a href="#addr-A649">A649</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8DF3" data-tip="&8DF3 – Copy OSFILE addresses and search for empty entry">copy_addrs_and_find_empty_entry</a></span> <span class="comment">; Create entry in dest directory</span></td> </tr> <tr id="addr-A64C"> <td class="addr"><a href="#addr-A64C">A64C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8E6F" data-tip="&8E6F – Allocate disc space and store in entry">allocate_disc_space_for_file</a></span> <span class="comment">; Allocate disc space</span></td> </tr> <tr id="addr-A64F"> <td class="addr"><a href="#addr-A64F">A64F</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy attributes back to entry</span></td> </tr> <tr id="addr-A651"> <td class="addr"><a href="#addr-A651">A651</a></td> <td><span class="label">.restore_attributes_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A65B">← A65B BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get new entry access byte</span></td> </tr> <tr id="addr-A653"> <td class="addr"><a href="#addr-A653">A653</a></td> <td> <span class="opcode">ASL</span> <span class="comment">; Shift attribute bit to position</span></td> </tr> <tr id="addr-A654"> <td class="addr"><a href="#addr-A654">A654</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&105D">wksp_access_accum</span></span> <span class="comment">; Rotate into access accumulator</span></td> </tr> <tr id="addr-A657"> <td class="addr"><a href="#addr-A657">A657</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Shift back</span></td> </tr> <tr id="addr-A658"> <td class="addr"><a href="#addr-A658">A658</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Store in entry name byte</span></td> </tr> <tr id="addr-A65A"> <td class="addr"><a href="#addr-A65A">A65A</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A65B"> <td class="addr"><a href="#addr-A65B">A65B</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A651" data-tip="&A651">restore_attributes_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A65D"> <td class="addr"><a href="#addr-A65D">A65D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8E8B" data-tip="&8E8B – Copy OSFILE template into directory entry">copy_entry_from_template</a></span> <span class="comment">; Write entry metadata</span></td> </tr> <tr id="addr-A660"> <td class="addr"><a href="#addr-A660">A660</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F58" data-tip="&8F58">copy_length_to_entry</a></span> <span class="comment">; Update entry size</span></td> </tr> <tr id="addr-A663"> <td class="addr"><a href="#addr-A663">A663</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write dest directory to disc</span></td> </tr> <tr id="addr-A666"> <td class="addr"><a href="#addr-A666">A666</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A685" data-tip="&A685">update_moved_dir_parent</a></span> <span class="comment">; Update moved dir's parent pointer</span></td> </tr> <tr id="addr-A669"> <td class="addr"><a href="#addr-A669">A669</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-A66C"> <td class="addr"><a href="#addr-A66C">A66C</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore source name pointer</span></td> </tr> <tr id="addr-A66D"> <td class="addr"><a href="#addr-A66D">A66D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store high byte</span></td> </tr> <tr id="addr-A66F"> <td class="addr"><a href="#addr-A66F">A66F</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore low byte</span></td> </tr> <tr id="addr-A670"> <td class="addr"><a href="#addr-A670">A670</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store low byte</span></td> </tr> <tr id="addr-A672"> <td class="addr"><a href="#addr-A672">A672</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FDF" data-tip="&8FDF – Find first matching directory entry">find_first_matching_entry</a></span> <span class="comment">; Find source entry again</span></td> </tr> <tr id="addr-A675"> <td class="addr"><a href="#addr-A675">A675</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; X=5: clear 6 bytes of sector info</span></td> </tr> <tr id="addr-A677"> <td class="addr"><a href="#addr-A677">A677</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: zero fill</span></td> </tr> <tr id="addr-A679"> <td class="addr"><a href="#addr-A679">A679</a></td> <td><span class="label">.clear_sector_workspace_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A67D">← A67D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span>,x</span> <span class="comment">; Clear sector/size workspace</span></td> </tr> <tr id="addr-A67C"> <td class="addr"><a href="#addr-A67C">A67C</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A67D"> <td class="addr"><a href="#addr-A67D">A67D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A679" data-tip="&A679">clear_sector_workspace_loop</a></span> <span class="comment">; Loop for 6 bytes</span></td> </tr> <tr id="addr-A67F"> <td class="addr"><a href="#addr-A67F">A67F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-9212" data-tip="&9212">write_dir_and_release</a></span> <span class="comment">; Remove entry from source directory</span></td> </tr> <tr id="addr-A682"> <td class="addr"><a href="#addr-A682">A682</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return</span></td> </tr> <tr id="addr-A685"> <td class="addr"><a href="#addr-A685">A685</a></td> <td><span class="label">.update_moved_dir_parent<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A5E2">← A5E2 JSR</a><a href="#addr-A666">← A666 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: check if entry is directory</span></td> </tr> <tr id="addr-A687"> <td class="addr"><a href="#addr-A687">A687</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get access byte</span></td> </tr> <tr id="addr-A689"> <td class="addr"><a href="#addr-A689">A689</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-A68C" data-tip="&A68C">update_parent_sector</a></span> <span class="comment">; Bit 7: is a directory</span></td> </tr> <tr id="addr-A68B"> <td class="addr"><a href="#addr-A68B">A68B</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Not a dir: nothing to update</span></td> </tr> <tr id="addr-A68C"> <td class="addr"><a href="#addr-A68C">A68C</a></td> <td><span class="label">.update_parent_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A689">← A689 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: copy 3 dir sector bytes</span></td> </tr> <tr id="addr-A68E"> <td class="addr"><a href="#addr-A68E">A68E</a></td> <td><span class="label">.copy_parent_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A695">← A695 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get CSD sector byte</span></td> </tr> <tr id="addr-A691"> <td class="addr"><a href="#addr-A691">A691</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1070">wksp_new_parent_sector</span>,y</span> <span class="comment">; Store as new parent sector</span></td> </tr> <tr id="addr-A694"> <td class="addr"><a href="#addr-A694">A694</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A695"> <td class="addr"><a href="#addr-A695">A695</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A68E" data-tip="&A68E">copy_parent_sector_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A697"> <td class="addr"><a href="#addr-A697">A697</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy 10-byte directory name</span></td> </tr> <tr id="addr-A699"> <td class="addr"><a href="#addr-A699">A699</a></td> <td><span class="label">.copy_dir_name_from_entry<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A6A1">← A6A1 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get name byte from entry</span></td> </tr> <tr id="addr-A69B"> <td class="addr"><a href="#addr-A69B">A69B</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7 (attribute)</span></td> </tr> <tr id="addr-A69D"> <td class="addr"><a href="#addr-A69D">A69D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1074">wksp_dest_name</span>,y</span> <span class="comment">; Store as directory name</span></td> </tr> <tr id="addr-A6A0"> <td class="addr"><a href="#addr-A6A0">A6A0</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A6A1"> <td class="addr"><a href="#addr-A6A1">A6A1</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A699" data-tip="&A699">copy_dir_name_from_entry</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A6A3"> <td class="addr"><a href="#addr-A6A3">A6A3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="116 &74 %01110100 't'">#&74</span></span> <span class="comment">; Point to workspace name buffer</span></td> </tr> <tr id="addr-A6A5"> <td class="addr"><a href="#addr-A6A5">A6A5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Low byte = &74</span></td> </tr> <tr id="addr-A6A7"> <td class="addr"><a href="#addr-A6A7">A6A7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Page = &10</span></td> </tr> <tr id="addr-A6A9"> <td class="addr"><a href="#addr-A6A9">A6A9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; High byte</span></td> </tr> <tr id="addr-A6AB"> <td class="addr"><a href="#addr-A6AB">A6AB</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-947F" data-tip="&947F – Parse path and load target directory">parse_path_and_load</a></span> <span class="comment">; Load the moved directory</span></td> </tr> <tr id="addr-A6AE"> <td class="addr"><a href="#addr-A6AE">A6AE</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy name to dir header</span></td> </tr> <tr id="addr-A6B0"> <td class="addr"><a href="#addr-A6B0">A6B0</a></td> <td><span class="label">.write_dir_name_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A6B7">← A6B7 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1074">wksp_dest_name</span>,y</span> <span class="comment">; Get name from workspace</span></td> </tr> <tr id="addr-A6B3"> <td class="addr"><a href="#addr-A6B3">A6B3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&16CC">dir_name</span>,y</span> <span class="comment">; Store in directory name field</span></td> </tr> <tr id="addr-A6B6"> <td class="addr"><a href="#addr-A6B6">A6B6</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A6B7"> <td class="addr"><a href="#addr-A6B7">A6B7</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A6B0" data-tip="&A6B0">write_dir_name_loop</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A6B9"> <td class="addr"><a href="#addr-A6B9">A6B9</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: copy parent sector pointer</span></td> </tr> <tr id="addr-A6BB"> <td class="addr"><a href="#addr-A6BB">A6BB</a></td> <td><span class="label">.write_parent_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A6C2">← A6C2 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1070">wksp_new_parent_sector</span>,y</span> <span class="comment">; Get new parent sector byte</span></td> </tr> <tr id="addr-A6BE"> <td class="addr"><a href="#addr-A6BE">A6BE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&16D6">dir_parent_sector</span>,y</span> <span class="comment">; Store in directory parent field</span></td> </tr> <tr id="addr-A6C1"> <td class="addr"><a href="#addr-A6C1">A6C1</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A6C2"> <td class="addr"><a href="#addr-A6C2">A6C2</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A6BB" data-tip="&A6BB">write_parent_sector_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A6C4"> <td class="addr"><a href="#addr-A6C4">A6C4</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write updated directory to disc</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A6C7"> <td colspan="2"><div class="sub-header"> <h3>Ensure current directory is loaded</h3> <div class="sub-desc"><p>Check that the current directory buffer contains valid data. If not, reload it from disc.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A6C7">A6C7</a></td> <td><span class="label">.check_dir_loaded<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-8090">← 8090 JSR</a><a href="#addr-A6DE">← A6DE JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive number</span></td> </tr> <tr id="addr-A6CA"> <td class="addr"><a href="#addr-A6CA">A6CA</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Increment: &FF becomes 0</span></td> </tr> <tr id="addr-A6CB"> <td class="addr"><a href="#addr-A6CB">A6CB</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A6F8" data-tip="&A6F8">return_31</a></span> <span class="comment">; Non-zero = drive is set, OK</span></td> </tr> <tr id="addr-A6CD"> <td class="addr"><a href="#addr-A6CD">A6CD</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8351" data-tip="&8351 – Generate error without drive/sector suffix">generate_error_no_suffix</a></span> <span class="comment">; Drive is &FF: no directory loaded</span></td> </tr> <tr id="addr-A6D0"> <td class="addr"><a href="#addr-A6D0">A6D0</a></td> <td> <span class="directive">EQUB</span> <span data-tip="169 &A9 %10101001">&A9</span> <span class="comment">; Error &A9: Bad FS map</span></td> </tr> <tr id="addr-A6D1"> <td class="addr"><a href="#addr-A6D1">A6D1</a></td> <td> <span class="directive">EQUS</span> <span class="string">"No directory"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A6DE"> <td colspan="2"><div class="sub-header"> <h3>Verify directory buffer integrity</h3> <div class="sub-desc"><p>Check that the directory buffer contains a valid directory by verifying the Hugo identity string and master sequence number are consistent at both ends of the directory. Raises Broken directory error if verification fails.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A6DE">A6DE</a></td> <td><span class="label">.verify_dir_integrity<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-87ED">← 87ED JSR</a><a href="#addr-8F86">← 8F86 JSR</a><a href="#addr-932A">← 932A JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A6C7" data-tip="&A6C7 – Ensure current directory is loaded">check_dir_loaded</a></span> <span class="comment">; Check drive is loaded</span></td> </tr> <tr id="addr-A6E1"> <td class="addr"><a href="#addr-A6E1">A6E1</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: compare index</span></td> </tr> <tr id="addr-A6E3"> <td class="addr"><a href="#addr-A6E3">A6E3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&16FA">dir_master_sequence</span></span> <span class="comment">; Get master sequence from footer</span></td> </tr> <tr id="addr-A6E6"> <td class="addr"><a href="#addr-A6E6">A6E6</a></td> <td><span class="label">.compare_hugo_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A6F6">← A6F6 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1200">dir_buffer</span>,x</span> <span class="comment">; Compare with header sequence+ID</span></td> </tr> <tr id="addr-A6E9"> <td class="addr"><a href="#addr-A6E9">A6E9</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A6F9" data-tip="&A6F9 – Raise Broken directory error">broken_directory_error</a></span> <span class="comment">; Mismatch: broken directory</span></td> </tr> <tr id="addr-A6EB"> <td class="addr"><a href="#addr-A6EB">A6EB</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&16FA">dir_master_sequence</span>,x</span> <span class="comment">; Compare footer sequence+ID</span></td> </tr> <tr id="addr-A6EE"> <td class="addr"><a href="#addr-A6EE">A6EE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A6F9" data-tip="&A6F9 – Raise Broken directory error">broken_directory_error</a></span> <span class="comment">; Mismatch: broken directory</span></td> </tr> <tr id="addr-A6F0"> <td class="addr"><a href="#addr-A6F0">A6F0</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A6F1"> <td class="addr"><a href="#addr-A6F1">A6F1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-84B0" data-tip="&84B0">str_hugo</a>,x</span> <span class="comment">; Check against "Hugo" string</span></td> </tr> <tr id="addr-A6F4"> <td class="addr"><a href="#addr-A6F4">A6F4</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Checked all 5 bytes (seq+Hugo)?</span></td> </tr> <tr id="addr-A6F6"> <td class="addr"><a href="#addr-A6F6">A6F6</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A6E6" data-tip="&A6E6">compare_hugo_loop</a></span> <span class="comment">; No, continue checking</span></td> </tr> <tr id="addr-A6F8"> <td class="addr"><a href="#addr-A6F8">A6F8</a></td> <td><span class="label">.return_31<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A6CB">← A6CB BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A6F9"> <td colspan="2"><div class="sub-header"> <h3>Raise Broken directory error</h3> <div class="sub-desc"><p>Generate disc error with state recovery, then raise error &A8: Broken directory.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A6F9">A6F9</a></td> <td><span class="label">.broken_directory_error<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A6E9">← A6E9 BNE</a><a href="#addr-A6EE">← A6EE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-832B" data-tip="&832B – Generate disc error with state recovery">generate_disc_error</a></span> <span class="comment">; Dir broken: save drive and error</span></td> </tr> <tr id="addr-A6FC"> <td class="addr"><a href="#addr-A6FC">A6FC</a></td> <td> <span class="directive">EQUB</span> <span data-tip="168 &A8 %10101000">&A8</span> <span class="comment">; Error &A8: Broken directory</span></td> </tr> <tr id="addr-A6FD"> <td class="addr"><a href="#addr-A6FD">A6FD</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Broken directory"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A70E"> <td colspan="2"><div class="sub-header"> <h3>Get workspace address into &BA</h3> <div class="sub-desc"><p>Load a workspace address into zero page locations &BA-&BB.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A70E">A70E</a></td> <td><span class="label">.get_wksp_addr_ba<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-8A22">← 8A22 JSR</a><a href="#addr-9AFB">← 9AFB JSR</a><a href="#addr-A71A">← A71A JSR</a><a href="#addr-A93C">← A93C JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00F4">romsel_copy</span></span> <span class="comment">; Get our ROM number</span></td> </tr> <tr id="addr-A710"> <td class="addr"><a href="#addr-A710">A710</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0DF0">rom_wksp_table</span>,x</span> <span class="comment">; Read workspace page from ROM table</span></td> </tr> <tr id="addr-A713"> <td class="addr"><a href="#addr-A713">A713</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BB">zp_wksp_ptr_hi</span></span> <span class="comment">; Store as high byte of (&BA)</span></td> </tr> <tr id="addr-A715"> <td class="addr"><a href="#addr-A715">A715</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Low byte = 0 (page-aligned)</span></td> </tr> <tr id="addr-A717"> <td class="addr"><a href="#addr-A717">A717</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BA">zp_wksp_ptr_lo</span></span> <span class="comment">; Store low byte</span></td> </tr> <tr id="addr-A719"> <td class="addr"><a href="#addr-A719">A719</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A71A"> <td colspan="2"><div class="sub-header"> <h3>Calculate workspace checksum</h3> <div class="sub-desc"><p>Calculate a checksum over the workspace area for integrity checking.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A71A">A71A</a></td> <td><span class="label">.calc_wksp_checksum<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A72B">← A72B JSR</a><a href="#addr-A731">← A731 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A70E" data-tip="&A70E – Get workspace address into &BA">get_wksp_addr_ba</a></span> <span class="comment">; Get workspace page address</span></td> </tr> <tr id="addr-A71D"> <td class="addr"><a href="#addr-A71D">A71D</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="253 &FD %11111101">#&fd</span></span> <span class="comment">; Y=&FD: start from byte 253</span></td> </tr> <tr id="addr-A71F"> <td class="addr"><a href="#addr-A71F">A71F</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; A=&FD: initial accumulator</span></td> </tr> <tr id="addr-A720"> <td class="addr"><a href="#addr-A720">A720</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-A721"> <td class="addr"><a href="#addr-A721">A721</a></td> <td><span class="label">.sum_workspace_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A724">← A724 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ADC</span> <span class="operand">(<span class="ext-label" data-tip="&00BA">zp_wksp_ptr_lo</span>),y</span> <span class="comment">; Add workspace byte to checksum</span></td> </tr> <tr id="addr-A723"> <td class="addr"><a href="#addr-A723">A723</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte down</span></td> </tr> <tr id="addr-A724"> <td class="addr"><a href="#addr-A724">A724</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A721" data-tip="&A721">sum_workspace_loop</a></span> <span class="comment">; Loop until Y wraps to 0</span></td> </tr> <tr id="addr-A726"> <td class="addr"><a href="#addr-A726">A726</a></td> <td> <span class="opcode">ADC</span> <span class="operand">(<span class="ext-label" data-tip="&00BA">zp_wksp_ptr_lo</span>),y</span> <span class="comment">; Add byte 0</span></td> </tr> <tr id="addr-A728"> <td class="addr"><a href="#addr-A728">A728</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="254 &FE %11111110">#&fe</span></span> <span class="comment">; Y=&FE: index of checksum byte</span></td> </tr> <tr id="addr-A72A"> <td class="addr"><a href="#addr-A72A">A72A</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A72B"> <td colspan="2"><div class="sub-header"> <h3>Calculate and store workspace checksum</h3> <div class="sub-desc"><p>Calculate workspace checksum and store at (zp_wksp_ptr)+&FE.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A72B">A72B</a></td> <td><span class="label">.store_wksp_checksum_ba_y<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-8A34">← 8A34 JSR</a><a href="#addr-9B0D">← 9B0D JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A71A" data-tip="&A71A – Calculate workspace checksum">calc_wksp_checksum</a></span> <span class="comment">; Calculate checksum</span></td> </tr> <tr id="addr-A72E"> <td class="addr"><a href="#addr-A72E">A72E</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BA">zp_wksp_ptr_lo</span>),y</span> <span class="comment">; Store at (&BA)+&FE</span></td> </tr> <tr id="addr-A730"> <td class="addr"><a href="#addr-A730">A730</a></td> <td><span class="label">.return_32<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A736">← A736 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A731"> <td colspan="2"><div class="sub-header"> <h3>Verify workspace checksum</h3> <div class="sub-desc"><p>Check the workspace checksum matches the stored value. Raises a Bad checksum error if verification fails.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A731">A731</a></td> <td><span class="label">.check_wksp_checksum<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-9B10">← 9B10 JSR</a><a href="#addr-9BCD">← 9BCD JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A71A" data-tip="&A71A – Calculate workspace checksum">calc_wksp_checksum</a></span> <span class="comment">; Calculate actual checksum</span></td> </tr> <tr id="addr-A734"> <td class="addr"><a href="#addr-A734">A734</a></td> <td> <span class="opcode">CMP</span> <span class="operand">(<span class="ext-label" data-tip="&00BA">zp_wksp_ptr_lo</span>),y</span> <span class="comment">; Compare with stored checksum</span></td> </tr> <tr id="addr-A736"> <td class="addr"><a href="#addr-A736">A736</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A730" data-tip="&A730">return_32</a></span> <span class="comment">; Match: workspace is valid</span></td> </tr> <tr id="addr-A738"> <td class="addr"><a href="#addr-A738">A738</a></td> <td><span class="label">.bad_checksum_error<span class="ref-badge">←7</span><span class="ref-popup"><a href="#addr-A752">← A752 BNE</a><a href="#addr-A761">← A761 BCS</a><a href="#addr-A765">← A765 BNE</a><a href="#addr-A76D">← A76D BCC</a><a href="#addr-A775">← A775 BNE</a><a href="#addr-ACE6">← ACE6 JMP</a><a href="#addr-AE32">← AE32 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="15 &0F %00001111 SI">#&0f</span></span> <span class="comment">; Checksum mismatch or corruption</span></td> </tr> <tr id="addr-A73A"> <td class="addr"><a href="#addr-A73A">A73A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10CE">wksp_error_suppress</span></span> <span class="comment">; Set error flag</span></td> </tr> <tr id="addr-A73D"> <td class="addr"><a href="#addr-A73D">A73D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8351" data-tip="&8351 – Generate error without drive/sector suffix">generate_error_no_suffix</a></span></td> </tr> <tr id="addr-A740"> <td class="addr"><a href="#addr-A740">A740</a></td> <td> <span class="directive">EQUB</span> <span data-tip="170 &AA %10101010">&AA</span> <span class="comment">; Error &AA: Bad checksum</span></td> </tr> <tr id="addr-A741"> <td class="addr"><a href="#addr-A741">A741</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Bad sum"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A749"> <td colspan="2"><div class="sub-header"> <h3>Save all registers and workspace</h3> <div class="sub-desc"><p>Save registers, validate workspace checksum, check FSM integrity, and store workspace with updated checksum.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A749">A749</a></td> <td><span class="label">.save_workspace_state<span class="ref-badge">←8</span><span class="ref-popup"><a href="#addr-A95F">← A95F JSR</a><a href="#addr-A995">← A995 JSR</a><a href="#addr-AD42">← AD42 JSR</a><a href="#addr-AD76">← AD76 JSR</a><a href="#addr-B0E4">← B0E4 JSR</a><a href="#addr-B12F">← B12F JSR</a><a href="#addr-B1B6">← B1B6 JSR</a><a href="#addr-B57F">← B57F JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save all registers</span></td> </tr> <tr id="addr-A74A"> <td class="addr"><a href="#addr-A74A">A74A</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save A</span></td> </tr> <tr id="addr-A74B"> <td class="addr"><a href="#addr-A74B">A74B</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer Y to A</span></td> </tr> <tr id="addr-A74C"> <td class="addr"><a href="#addr-A74C">A74C</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save Y</span></td> </tr> <tr id="addr-A74D"> <td class="addr"><a href="#addr-A74D">A74D</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer X to A</span></td> </tr> <tr id="addr-A74E"> <td class="addr"><a href="#addr-A74E">A74E</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save X</span></td> </tr> <tr id="addr-A74F"> <td class="addr"><a href="#addr-A74F">A74F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10CE">wksp_error_suppress</span></span> <span class="comment">; Check error flag</span></td> </tr> <tr id="addr-A752"> <td class="addr"><a href="#addr-A752">A752</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; Non-zero: workspace corrupt, error</span></td> </tr> <tr id="addr-A754"> <td class="addr"><a href="#addr-A754">A754</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FEA" data-tip="&8FEA – Validate FSM entry structure and checksums">validate_fsm_checksums</a></span> <span class="comment">; Validate FSM before modification</span></td> </tr> <tr id="addr-A757"> <td class="addr"><a href="#addr-A757">A757</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for scan</span></td> </tr> <tr id="addr-A758"> <td class="addr"><a href="#addr-A758">A758</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; X=&10: scan open channel table</span></td> </tr> <tr id="addr-A75A"> <td class="addr"><a href="#addr-A75A">A75A</a></td> <td><span class="label">.save_wksp_byte_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A76B">← A76B BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state entry</span></td> </tr> <tr id="addr-A75D"> <td class="addr"><a href="#addr-A75D">A75D</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Check bits 0 and 5 (dirty flags)</span></td> </tr> <tr id="addr-A75F"> <td class="addr"><a href="#addr-A75F">A75F</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A767" data-tip="&A767">save_wksp_and_checksum</a></span> <span class="comment">; Both clear: channel clean</span></td> </tr> <tr id="addr-A761"> <td class="addr"><a href="#addr-A761">A761</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; Carry set + dirty: corrupt</span></td> </tr> <tr id="addr-A763"> <td class="addr"><a href="#addr-A763">A763</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Only bit 0 set: check value</span></td> </tr> <tr id="addr-A765"> <td class="addr"><a href="#addr-A765">A765</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; Not exactly 1: corrupt</span></td> </tr> <tr id="addr-A767"> <td class="addr"><a href="#addr-A767">A767</a></td> <td><span class="label">.save_wksp_and_checksum<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A75F">← A75F BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes</span></td> </tr> <tr id="addr-A768"> <td class="addr"><a href="#addr-A768">A768</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-A769"> <td class="addr"><a href="#addr-A769">A769</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-A76A"> <td class="addr"><a href="#addr-A76A">A76A</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-A76B"> <td class="addr"><a href="#addr-A76B">A76B</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A75A" data-tip="&A75A">save_wksp_byte_loop</a></span> <span class="comment">; Loop for all entries</span></td> </tr> <tr id="addr-A76D"> <td class="addr"><a href="#addr-A76D">A76D</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; No dirty entries + C=0: corrupt</span></td> </tr> <tr id="addr-A76F"> <td class="addr"><a href="#addr-A76F">A76F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A797" data-tip="&A797">restore_wksp_from_save</a></span> <span class="comment">; Calculate channel checksum</span></td> </tr> <tr id="addr-A772"> <td class="addr"><a href="#addr-A772">A772</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&10C1">wksp_workspace_checksum</span></span> <span class="comment">; Compare with stored checksum</span></td> </tr> <tr id="addr-A775"> <td class="addr"><a href="#addr-A775">A775</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; Mismatch: corrupt</span></td> </tr> <tr id="addr-A777"> <td class="addr"><a href="#addr-A777">A777</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push 2 dummy bytes for stack frame</span></td> </tr> <tr id="addr-A778"> <td class="addr"><a href="#addr-A778">A778</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Second push</span></td> </tr> <tr id="addr-A779"> <td class="addr"><a href="#addr-A779">A779</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: shift 6 bytes on stack</span></td> </tr> <tr id="addr-A77B"> <td class="addr"><a href="#addr-A77B">A77B</a></td> <td> <span class="opcode">TSX</span> <span class="comment">; Get current stack pointer</span></td> </tr> <tr id="addr-A77C"> <td class="addr"><a href="#addr-A77C">A77C</a></td> <td><span class="label">.restore_workspace_state<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A784">← A784 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0103">brk_error_block_3</span>,x</span> <span class="comment">; Get byte from stack+3</span></td> </tr> <tr id="addr-A77F"> <td class="addr"><a href="#addr-A77F">A77F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0101">brk_error_block_1</span>,x</span> <span class="comment">; Move down to stack+1</span></td> </tr> <tr id="addr-A782"> <td class="addr"><a href="#addr-A782">A782</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A783"> <td class="addr"><a href="#addr-A783">A783</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement counter</span></td> </tr> <tr id="addr-A784"> <td class="addr"><a href="#addr-A784">A784</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A77C" data-tip="&A77C">restore_workspace_state</a></span> <span class="comment">; Loop for 6 bytes</span></td> </tr> <tr id="addr-A786"> <td class="addr"><a href="#addr-A786">A786</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="161 &A1 %10100001">#&a1</span></span> <span class="comment">; Insert return addr low = &A1</span></td> </tr> <tr id="addr-A788"> <td class="addr"><a href="#addr-A788">A788</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0101">brk_error_block_1</span>,x</span> <span class="comment">; Store at stack+1</span></td> </tr> <tr id="addr-A78B"> <td class="addr"><a href="#addr-A78B">A78B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="167 &A7 %10100111">#&a7</span></span> <span class="comment">; Insert return addr high = &A7</span></td> </tr> <tr id="addr-A78D"> <td class="addr"><a href="#addr-A78D">A78D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0102">brk_error_block_2</span>,x</span> <span class="comment">; Store at stack+2 (return to &A7A2)</span></td> </tr> <tr id="addr-A790"> <td class="addr"><a href="#addr-A790">A790</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore X from dummy push</span></td> </tr> <tr id="addr-A791"> <td class="addr"><a href="#addr-A791">A791</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-A792"> <td class="addr"><a href="#addr-A792">A792</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore Y from dummy push</span></td> </tr> <tr id="addr-A793"> <td class="addr"><a href="#addr-A793">A793</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer to Y</span></td> </tr> <tr id="addr-A794"> <td class="addr"><a href="#addr-A794">A794</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore A</span></td> </tr> <tr id="addr-A795"> <td class="addr"><a href="#addr-A795">A795</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-A796"> <td class="addr"><a href="#addr-A796">A796</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (via inserted &A7A2 addr)</span></td> </tr> <tr id="addr-A797"> <td class="addr"><a href="#addr-A797">A797</a></td> <td><span class="label">.restore_wksp_from_save<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A76F">← A76F JSR</a><a href="#addr-A7A8">← A7A8 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="120 &78 %01111000 'x'">#&78</span></span> <span class="comment">; X=&78: sum 120 bytes of channel data</span></td> </tr> <tr id="addr-A799"> <td class="addr"><a href="#addr-A799">A799</a></td> <td> <span class="opcode">TXA</span></td> </tr> <tr id="addr-A79A"> <td class="addr"><a href="#addr-A79A">A79A</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for summation</span></td> </tr> <tr id="addr-A79B"> <td class="addr"><a href="#addr-A79B">A79B</a></td> <td><span class="label">.restore_wksp_byte_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A79F">← A79F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1183">wksp_ch_alloc_pad</span>,x</span> <span class="comment">; Add channel table byte</span></td> </tr> <tr id="addr-A79E"> <td class="addr"><a href="#addr-A79E">A79E</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A79F"> <td class="addr"><a href="#addr-A79F">A79F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A79B" data-tip="&A79B">restore_wksp_byte_loop</a></span> <span class="comment">; Loop for 120 bytes</span></td> </tr> <tr id="addr-A7A1"> <td class="addr"><a href="#addr-A7A1">A7A1</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return checksum in A</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A7A2"> <td colspan="2"><div class="sub-header"> <h3>Restore workspace and load directory</h3> <div class="sub-desc"><p>Restore workspace from saved copy, then load the current directory from disc for the active drive.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A7A2">A7A2</a></td> <td><span class="label">.load_dir_for_drive<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-83F7">← 83F7 JSR</a><a href="#addr-9BF8">← 9BF8 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save all registers</span></td> </tr> <tr id="addr-A7A3"> <td class="addr"><a href="#addr-A7A3">A7A3</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save A</span></td> </tr> <tr id="addr-A7A4"> <td class="addr"><a href="#addr-A7A4">A7A4</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer Y to A</span></td> </tr> <tr id="addr-A7A5"> <td class="addr"><a href="#addr-A7A5">A7A5</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save Y</span></td> </tr> <tr id="addr-A7A6"> <td class="addr"><a href="#addr-A7A6">A7A6</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer X to A</span></td> </tr> <tr id="addr-A7A7"> <td class="addr"><a href="#addr-A7A7">A7A7</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save X</span></td> </tr> <tr id="addr-A7A8"> <td class="addr"><a href="#addr-A7A8">A7A8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A797" data-tip="&A797">restore_wksp_from_save</a></span> <span class="comment">; Calculate channel checksum</span></td> </tr> <tr id="addr-A7AB"> <td class="addr"><a href="#addr-A7AB">A7AB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10C1">wksp_workspace_checksum</span></span> <span class="comment">; Store checksum in workspace</span></td> </tr> <tr id="addr-A7AE"> <td class="addr"><a href="#addr-A7AE">A7AE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear flags</span></td> </tr> <tr id="addr-A7B0"> <td class="addr"><a href="#addr-A7B0">A7B0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D8">wksp_compaction_reported</span></span> <span class="comment">; Clear compaction-reported flag</span></td> </tr> <tr id="addr-A7B3"> <td class="addr"><a href="#addr-A7B3">A7B3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10CE">wksp_error_suppress</span></span> <span class="comment">; Clear error flag</span></td> </tr> <tr id="addr-A7B6"> <td class="addr"><a href="#addr-A7B6">A7B6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D5">wksp_cur_channel</span></span> <span class="comment">; Clear current channel</span></td> </tr> <tr id="addr-A7B9"> <td class="addr"><a href="#addr-A7B9">A7B9</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-A7BA"> <td class="addr"><a href="#addr-A7BA">A7BA</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-A7BB"> <td class="addr"><a href="#addr-A7BB">A7BB</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore Y</span></td> </tr> <tr id="addr-A7BC"> <td class="addr"><a href="#addr-A7BC">A7BC</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer to Y</span></td> </tr> <tr id="addr-A7BD"> <td class="addr"><a href="#addr-A7BD">A7BD</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore A</span></td> </tr> <tr id="addr-A7BE"> <td class="addr"><a href="#addr-A7BE">A7BE</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-A7BF"> <td class="addr"><a href="#addr-A7BF">A7BF</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A7C0"> <td colspan="2"><div class="sub-header"> <h3>Set up disc read for directory load</h3> <div class="sub-desc"><p>Copy a disc operation template to the workspace and set up the sector address for reading a directory from disc.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A7C0">A7C0</a></td> <td><span class="label">.setup_disc_read_for_dir<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A880">← A880 JSR</a><a href="#addr-A936">← A936 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1091">wksp_filename_save</span></span> <span class="comment">; Get saved filename pointer low</span></td> </tr> <tr id="addr-A7C3"> <td class="addr"><a href="#addr-A7C3">A7C3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store in (&B4)</span></td> </tr> <tr id="addr-A7C5"> <td class="addr"><a href="#addr-A7C5">A7C5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1092">wksp_filename_save_hi</span></span> <span class="comment">; Get saved filename pointer high</span></td> </tr> <tr id="addr-A7C8"> <td class="addr"><a href="#addr-A7C8">A7C8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store in (&B5)</span></td> </tr> <tr id="addr-A7CA"> <td class="addr"><a href="#addr-A7CA">A7CA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1094">wksp_entry_save_hi</span></span> <span class="comment">; Get saved dir entry high</span></td> </tr> <tr id="addr-A7CD"> <td class="addr"><a href="#addr-A7CD">A7CD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B7">zp_entry_ptr_hi</span></span> <span class="comment">; Store in (&B7)</span></td> </tr> <tr id="addr-A7CF"> <td class="addr"><a href="#addr-A7CF">A7CF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1093">wksp_entry_save</span></span> <span class="comment">; Get saved dir entry low</span></td> </tr> <tr id="addr-A7D2"> <td class="addr"><a href="#addr-A7D2">A7D2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span></span> <span class="comment">; Store in (&B6)</span></td> </tr> <tr id="addr-A7D4"> <td class="addr"><a href="#addr-A7D4">A7D4</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="11 &0B %00001011 VT">#&0b</span></span> <span class="comment">; X=&0B: copy 12-byte disc op template</span></td> </tr> <tr id="addr-A7D6"> <td class="addr"><a href="#addr-A7D6">A7D6</a></td> <td><span class="label">.copy_disc_op_template_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A7DD">← A7DD BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-8816" data-tip="&8816">disc_op_tpl_padding</a>,x</span> <span class="comment">; Get template byte</span></td> </tr> <tr id="addr-A7D9"> <td class="addr"><a href="#addr-A7D9">A7D9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1014">wksp_disc_op_block</span>,x</span> <span class="comment">; Copy to workspace</span></td> </tr> <tr id="addr-A7DC"> <td class="addr"><a href="#addr-A7DC">A7DC</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A7DD"> <td class="addr"><a href="#addr-A7DD">A7DD</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A7D6" data-tip="&A7D6">copy_disc_op_template_loop</a></span> <span class="comment">; Loop for 12 bytes</span></td> </tr> <tr id="addr-A7DF"> <td class="addr"><a href="#addr-A7DF">A7DF</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4-byte sector address</span></td> </tr> <tr id="addr-A7E1"> <td class="addr"><a href="#addr-A7E1">A7E1</a></td> <td><span class="label">.copy_dir_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A7F0">← A7F0 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&106C">wksp_saved_dir_sector</span>,y</span> <span class="comment">; Get source sector byte</span></td> </tr> <tr id="addr-A7E4"> <td class="addr"><a href="#addr-A7E4">A7E4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Store in CSD sector</span></td> </tr> <tr id="addr-A7E7"> <td class="addr"><a href="#addr-A7E7">A7E7</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0?</span></td> </tr> <tr id="addr-A7E9"> <td class="addr"><a href="#addr-A7E9">A7E9</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A7EE" data-tip="&A7EE">read_dir_from_disc</a></span> <span class="comment">; Yes, skip disc op sector store</span></td> </tr> <tr id="addr-A7EB"> <td class="addr"><a href="#addr-A7EB">A7EB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&101A">wksp_disc_op_command</span>,x</span> <span class="comment">; Store in disc op sector field</span></td> </tr> <tr id="addr-A7EE"> <td class="addr"><a href="#addr-A7EE">A7EE</a></td> <td><span class="label">.read_dir_from_disc<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A7E9">← A7E9 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INX</span> <span class="comment">; Next X</span></td> </tr> <tr id="addr-A7EF"> <td class="addr"><a href="#addr-A7EF">A7EF</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next Y (decreasing)</span></td> </tr> <tr id="addr-A7F0"> <td class="addr"><a href="#addr-A7F0">A7F0</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A7E1" data-tip="&A7E1">copy_dir_sector_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A7F2"> <td class="addr"><a href="#addr-A7F2">A7F2</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8287" data-tip="&8287 – Execute disc command from workspace control block">exec_disc_op_from_wksp</a></span> <span class="comment">; Execute disc read command</span></td> </tr> <tr id="addr-A7F5"> <td class="addr"><a href="#addr-A7F5">A7F5</a></td> <td><span class="label">.setup_fsm_read<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A8D9">← A8D9 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="11 &0B %00001011 VT">#&0b</span></span> <span class="comment">; X=&0B: copy 12-byte disc op template</span></td> </tr> <tr id="addr-A7F7"> <td class="addr"><a href="#addr-A7F7">A7F7</a></td> <td><span class="label">.copy_fsm_template_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A7FE">← A7FE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><a href="#addr-8816" data-tip="&8816">disc_op_tpl_padding</a>,x</span> <span class="comment">; Get template byte</span></td> </tr> <tr id="addr-A7FA"> <td class="addr"><a href="#addr-A7FA">A7FA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1014">wksp_disc_op_block</span>,x</span> <span class="comment">; Copy to workspace</span></td> </tr> <tr id="addr-A7FD"> <td class="addr"><a href="#addr-A7FD">A7FD</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A7FE"> <td class="addr"><a href="#addr-A7FE">A7FE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A7F7" data-tip="&A7F7">copy_fsm_template_loop</a></span> <span class="comment">; Loop for 12 bytes</span></td> </tr> <tr id="addr-A800"> <td class="addr"><a href="#addr-A800">A800</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4-byte sector address</span></td> </tr> <tr id="addr-A802"> <td class="addr"><a href="#addr-A802">A802</a></td> <td><span class="label">.copy_fsm_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A811">← A811 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1070">wksp_new_parent_sector</span>,y</span> <span class="comment">; Get dest sector byte</span></td> </tr> <tr id="addr-A805"> <td class="addr"><a href="#addr-A805">A805</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Store in CSD sector</span></td> </tr> <tr id="addr-A808"> <td class="addr"><a href="#addr-A808">A808</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0?</span></td> </tr> <tr id="addr-A80A"> <td class="addr"><a href="#addr-A80A">A80A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A80F" data-tip="&A80F">read_fsm_from_disc</a></span> <span class="comment">; Yes, skip disc op store</span></td> </tr> <tr id="addr-A80C"> <td class="addr"><a href="#addr-A80C">A80C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&101A">wksp_disc_op_command</span>,x</span> <span class="comment">; Store in disc op sector field</span></td> </tr> <tr id="addr-A80F"> <td class="addr"><a href="#addr-A80F">A80F</a></td> <td><span class="label">.read_fsm_from_disc<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A80A">← A80A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INX</span> <span class="comment">; Next X</span></td> </tr> <tr id="addr-A810"> <td class="addr"><a href="#addr-A810">A810</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next Y (decreasing)</span></td> </tr> <tr id="addr-A811"> <td class="addr"><a href="#addr-A811">A811</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A802" data-tip="&A802">copy_fsm_sector_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A813"> <td class="addr"><a href="#addr-A813">A813</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8287" data-tip="&8287 – Execute disc command from workspace control block">exec_disc_op_from_wksp</a></span> <span class="comment">; Execute disc read command</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A816"> <td colspan="2"><div class="sub-header"> <h3>Load free space map from disc</h3> <div class="sub-desc"><p>Read sectors 0 and 1 from the current drive into the free space map workspace at &0E00-&0FFF. Validates the checksum.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A816">A816</a></td> <td><span class="label">.load_fsm</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="12 &0C %00001100 FF">#&0c</span></span> <span class="comment">; X=&0C: control block offset</span></td> </tr> <tr id="addr-A818"> <td class="addr"><a href="#addr-A818">A818</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="136 &88 %10001000">#&88</span></span> <span class="comment">; Y=&88: control block page</span></td> </tr> <tr id="addr-A81A"> <td class="addr"><a href="#addr-A81A">A81A</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-828B" data-tip="&828B – Execute disc command and check for error">exec_disc_command</a></span> <span class="comment">; Execute disc read command</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A81D"> <td colspan="2"><div class="sub-header"> <h3>*COPY command handler</h3> <div class="sub-desc"><p>Copy a file. The source and destination may be on different drives.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A81D">A81D</a></td> <td><span class="label">.star_copy</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Set up control block pointers</span></td> </tr> <tr id="addr-A81F"> <td class="addr"><a href="#addr-A81F">A81F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store control block pointer low</span></td> </tr> <tr id="addr-A821"> <td class="addr"><a href="#addr-A821">A821</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Control block page = &10</span></td> </tr> <tr id="addr-A823"> <td class="addr"><a href="#addr-A823">A823</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store control block pointer high</span></td> </tr> <tr id="addr-A825"> <td class="addr"><a href="#addr-A825">A825</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="116 &74 %01110100 't'">#&74</span></span> <span class="comment">; Store source name offset</span></td> </tr> <tr id="addr-A827"> <td class="addr"><a href="#addr-A827">A827</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&107F">wksp_copy_name_ptr</span></span> <span class="comment">; Store source name offset</span></td> </tr> <tr id="addr-A82A"> <td class="addr"><a href="#addr-A82A">A82A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Source name page = &10</span></td> </tr> <tr id="addr-A82C"> <td class="addr"><a href="#addr-A82C">A82C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1080">wksp_copy_name_ptr_hi</span></span> <span class="comment">; Store source name page</span></td> </tr> <tr id="addr-A82F"> <td class="addr"><a href="#addr-A82F">A82F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8BB3" data-tip="&8BB3 – Search for non-directory file">search_for_file</a></span> <span class="comment">; Find source file</span></td> </tr> <tr id="addr-A832"> <td class="addr"><a href="#addr-A832">A832</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A837" data-tip="&A837">source_file_found</a></span> <span class="comment">; Found?</span></td> </tr> <tr id="addr-A834"> <td class="addr"><a href="#addr-A834">A834</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8BC8" data-tip="&8BC8 – Generate Not found error">not_found_error</a></span> <span class="comment">; Not found: report error</span></td> </tr> <tr id="addr-A837"> <td class="addr"><a href="#addr-A837">A837</a></td> <td><span class="label">.source_file_found<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A832">← A832 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span></span> <span class="comment">; Save directory entry pointer</span></td> </tr> <tr id="addr-A839"> <td class="addr"><a href="#addr-A839">A839</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1093">wksp_entry_save</span></span> <span class="comment">; Save dir entry pointer low</span></td> </tr> <tr id="addr-A83C"> <td class="addr"><a href="#addr-A83C">A83C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B7">zp_entry_ptr_hi</span></span> <span class="comment">; Get dir entry pointer high</span></td> </tr> <tr id="addr-A83E"> <td class="addr"><a href="#addr-A83E">A83E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1094">wksp_entry_save_hi</span></span> <span class="comment">; Save dir entry pointer high</span></td> </tr> <tr id="addr-A841"> <td class="addr"><a href="#addr-A841">A841</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Save filename pointer</span></td> </tr> <tr id="addr-A843"> <td class="addr"><a href="#addr-A843">A843</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1091">wksp_filename_save</span></span> <span class="comment">; Save filename pointer low</span></td> </tr> <tr id="addr-A846"> <td class="addr"><a href="#addr-A846">A846</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Get filename pointer high</span></td> </tr> <tr id="addr-A848"> <td class="addr"><a href="#addr-A848">A848</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1092">wksp_filename_save_hi</span></span> <span class="comment">; Save filename pointer high</span></td> </tr> <tr id="addr-A84B"> <td class="addr"><a href="#addr-A84B">A84B</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: save current directory sector</span></td> </tr> <tr id="addr-A84D"> <td class="addr"><a href="#addr-A84D">A84D</a></td> <td><span class="label">.save_source_dir_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A854">← A854 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Copy CSD sector to workspace</span></td> </tr> <tr id="addr-A850"> <td class="addr"><a href="#addr-A850">A850</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&106C">wksp_saved_dir_sector</span>,y</span> <span class="comment">; Save CSD sector byte</span></td> </tr> <tr id="addr-A853"> <td class="addr"><a href="#addr-A853">A853</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A854"> <td class="addr"><a href="#addr-A854">A854</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A84D" data-tip="&A84D">save_source_dir_sector_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A856"> <td class="addr"><a href="#addr-A856">A856</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-A859"> <td class="addr"><a href="#addr-A859">A859</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy current dir sector</span></td> </tr> <tr id="addr-A85B"> <td class="addr"><a href="#addr-A85B">A85B</a></td> <td><span class="label">.copy_csd_for_dest_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A862">← A862 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get CSD sector byte</span></td> </tr> <tr id="addr-A85E"> <td class="addr"><a href="#addr-A85E">A85E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,y</span> <span class="comment">; Set as target dir for copy</span></td> </tr> <tr id="addr-A861"> <td class="addr"><a href="#addr-A861">A861</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A862"> <td class="addr"><a href="#addr-A862">A862</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A85B" data-tip="&A85B">copy_csd_for_dest_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A864"> <td class="addr"><a href="#addr-A864">A864</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A365" data-tip="&A365 – Parse second filename from command line">parse_second_filename</a></span> <span class="comment">; Parse destination path</span></td> </tr> <tr id="addr-A867"> <td class="addr"><a href="#addr-A867">A867</a></td> <td><span class="label">.check_dest_terminator</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-871A" data-tip="&871A – Check if character is a filename terminator">check_char_is_terminator</a></span></td> </tr> <tr id="addr-A86A"> <td class="addr"><a href="#addr-A86A">A86A</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A86F" data-tip="&A86F">load_dest_directory</a></span> <span class="comment">; Found destination dir?</span></td> </tr> <tr id="addr-A86C"> <td class="addr"><a href="#addr-A86C">A86C</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8737" data-tip="&8737">bad_name_error</a></span> <span class="comment">; Bad name: invalid destination</span></td> </tr> <tr id="addr-A86F"> <td class="addr"><a href="#addr-A86F">A86F</a></td> <td><span class="label">.load_dest_directory<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A86A">← A86A BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-947F" data-tip="&947F – Parse path and load target directory">parse_path_and_load</a></span> <span class="comment">; Load destination directory</span></td> </tr> <tr id="addr-A872"> <td class="addr"><a href="#addr-A872">A872</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FEA" data-tip="&8FEA – Validate FSM entry structure and checksums">validate_fsm_checksums</a></span> <span class="comment">; Validate FSM before dest dir change</span></td> </tr> <tr id="addr-A875"> <td class="addr"><a href="#addr-A875">A875</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: save dest dir sector</span></td> </tr> <tr id="addr-A877"> <td class="addr"><a href="#addr-A877">A877</a></td> <td><span class="label">.save_dest_dir_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A87E">← A87E BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get dest dir sector byte</span></td> </tr> <tr id="addr-A87A"> <td class="addr"><a href="#addr-A87A">A87A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1070">wksp_new_parent_sector</span>,y</span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-A87D"> <td class="addr"><a href="#addr-A87D">A87D</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A87E"> <td class="addr"><a href="#addr-A87E">A87E</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A877" data-tip="&A877">save_dest_dir_sector_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A880"> <td class="addr"><a href="#addr-A880">A880</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A7C0" data-tip="&A7C0 – Set up disc read for directory load">setup_disc_read_for_dir</a></span> <span class="comment">; Set up disc read for source</span></td> </tr> <tr id="addr-A883"> <td class="addr"><a href="#addr-A883">A883</a></td> <td><span class="label">.scan_source_entries_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A88F">← A88F BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Y=4: check entry access byte</span></td> </tr> <tr id="addr-A885"> <td class="addr"><a href="#addr-A885">A885</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get access byte from entry</span></td> </tr> <tr id="addr-A887"> <td class="addr"><a href="#addr-A887">A887</a></td> <td> <span class="opcode">DEY</span></td> </tr> <tr id="addr-A888"> <td class="addr"><a href="#addr-A888">A888</a></td> <td> <span class="opcode">ORA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; OR with first name byte</span></td> </tr> <tr id="addr-A88A"> <td class="addr"><a href="#addr-A88A">A88A</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A894" data-tip="&A894">copy_file_entry</a></span> <span class="comment">; Bit 7 clear: regular file, copy it</span></td> </tr> <tr id="addr-A88C"> <td class="addr"><a href="#addr-A88C">A88C</a></td> <td><span class="label">.skip_dir_entry_or_done<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A939">← A939 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-895E" data-tip="&895E – Advance to next matching directory entry">advance_dir_entry_ptr</a></span> <span class="comment">; Directory: find next entry</span></td> </tr> <tr id="addr-A88F"> <td class="addr"><a href="#addr-A88F">A88F</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A883" data-tip="&A883">scan_source_entries_loop</a></span> <span class="comment">; More entries: loop</span></td> </tr> <tr id="addr-A891"> <td class="addr"><a href="#addr-A891">A891</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; No more entries: done</span></td> </tr> <tr id="addr-A894"> <td class="addr"><a href="#addr-A894">A894</a></td> <td><span class="label">.copy_file_entry<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A88A">← A88A BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span></span> <span class="comment">; Save source entry pointer</span></td> </tr> <tr id="addr-A896"> <td class="addr"><a href="#addr-A896">A896</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1093">wksp_entry_save</span></span> <span class="comment">; Store entry pointer low</span></td> </tr> <tr id="addr-A899"> <td class="addr"><a href="#addr-A899">A899</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B7">zp_entry_ptr_hi</span></span> <span class="comment">; Get entry pointer high</span></td> </tr> <tr id="addr-A89B"> <td class="addr"><a href="#addr-A89B">A89B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1094">wksp_entry_save_hi</span></span> <span class="comment">; Store entry pointer high</span></td> </tr> <tr id="addr-A89E"> <td class="addr"><a href="#addr-A89E">A89E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8C62" data-tip="&8C62 – Search directory for matching file">search_dir_for_file</a></span> <span class="comment">; Check if file already exists at dest</span></td> </tr> <tr id="addr-A8A1"> <td class="addr"><a href="#addr-A8A1">A8A1</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="22 &16 %00010110 SYN">#&16</span></span> <span class="comment">; Y=&16: get source start sector</span></td> </tr> <tr id="addr-A8A3"> <td class="addr"><a href="#addr-A8A3">A8A3</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get sector low byte</span></td> </tr> <tr id="addr-A8A5"> <td class="addr"><a href="#addr-A8A5">A8A5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A2">wksp_copy_read_sector</span></span> <span class="comment">; Store in load address workspace</span></td> </tr> <tr id="addr-A8A8"> <td class="addr"><a href="#addr-A8A8">A8A8</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-A8A9"> <td class="addr"><a href="#addr-A8A9">A8A9</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get sector mid byte</span></td> </tr> <tr id="addr-A8AB"> <td class="addr"><a href="#addr-A8AB">A8AB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A3">wksp_copy_read_sector_1</span></span> <span class="comment">; Store in load address workspace</span></td> </tr> <tr id="addr-A8AE"> <td class="addr"><a href="#addr-A8AE">A8AE</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-A8AF"> <td class="addr"><a href="#addr-A8AF">A8AF</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get sector high byte</span></td> </tr> <tr id="addr-A8B1"> <td class="addr"><a href="#addr-A8B1">A8B1</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; OR with drive number</span></td> </tr> <tr id="addr-A8B4"> <td class="addr"><a href="#addr-A8B4">A8B4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A4">wksp_copy_read_sector_2</span></span> <span class="comment">; Store in load address workspace</span></td> </tr> <tr id="addr-A8B7"> <td class="addr"><a href="#addr-A8B7">A8B7</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: clear length bytes</span></td> </tr> <tr id="addr-A8B9"> <td class="addr"><a href="#addr-A8B9">A8B9</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4-byte OSFILE params</span></td> </tr> <tr id="addr-A8BB"> <td class="addr"><a href="#addr-A8BB">A8BB</a></td> <td><span class="label">.copy_osfile_params_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A8C6">← A8C6 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1089">wksp_copy_osfile_params</span>,y</span> <span class="comment">; Get source OSFILE param</span></td> </tr> <tr id="addr-A8BE"> <td class="addr"><a href="#addr-A8BE">A8BE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&108D">wksp_copy_dest_params</span>,y</span> <span class="comment">; Copy to dest OSFILE block</span></td> </tr> <tr id="addr-A8C1"> <td class="addr"><a href="#addr-A8C1">A8C1</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer X (=0) for clearing</span></td> </tr> <tr id="addr-A8C2"> <td class="addr"><a href="#addr-A8C2">A8C2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1089">wksp_copy_osfile_params</span>,y</span> <span class="comment">; Clear source param</span></td> </tr> <tr id="addr-A8C5"> <td class="addr"><a href="#addr-A8C5">A8C5</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A8C6"> <td class="addr"><a href="#addr-A8C6">A8C6</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A8BB" data-tip="&A8BB">copy_osfile_params_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-A8C8"> <td class="addr"><a href="#addr-A8C8">A8C8</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: copy 10-byte filename</span></td> </tr> <tr id="addr-A8CA"> <td class="addr"><a href="#addr-A8CA">A8CA</a></td> <td><span class="label">.copy_source_name_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A8D2">← A8D2 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get filename byte from source</span></td> </tr> <tr id="addr-A8CC"> <td class="addr"><a href="#addr-A8CC">A8CC</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7 (access flag)</span></td> </tr> <tr id="addr-A8CE"> <td class="addr"><a href="#addr-A8CE">A8CE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1074">wksp_dest_name</span>,y</span> <span class="comment">; Store in dest name workspace</span></td> </tr> <tr id="addr-A8D1"> <td class="addr"><a href="#addr-A8D1">A8D1</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A8D2"> <td class="addr"><a href="#addr-A8D2">A8D2</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A8CA" data-tip="&A8CA">copy_source_name_loop</a></span> <span class="comment">; Loop for 10 bytes</span></td> </tr> <tr id="addr-A8D4"> <td class="addr"><a href="#addr-A8D4">A8D4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="13 &0D %00001101 CR">#&0d</span></span> <span class="comment">; A=CR: terminate filename</span></td> </tr> <tr id="addr-A8D6"> <td class="addr"><a href="#addr-A8D6">A8D6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&107E">wksp_dest_filename_end</span></span> <span class="comment">; Store terminator</span></td> </tr> <tr id="addr-A8D9"> <td class="addr"><a href="#addr-A8D9">A8D9</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A7F5" data-tip="&A7F5">setup_fsm_read</a></span> <span class="comment">; Set up disc read for source file</span></td> </tr> <tr id="addr-A8DC"> <td class="addr"><a href="#addr-A8DC">A8DC</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8DF3" data-tip="&8DF3 – Copy OSFILE addresses and search for empty entry">copy_addrs_and_find_empty_entry</a></span> <span class="comment">; Check if file is open</span></td> </tr> <tr id="addr-A8DF"> <td class="addr"><a href="#addr-A8DF">A8DF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8E6F" data-tip="&8E6F – Allocate disc space and store in entry">allocate_disc_space_for_file</a></span> <span class="comment">; Allocate space for dest file</span></td> </tr> <tr id="addr-A8E2"> <td class="addr"><a href="#addr-A8E2">A8E2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F52" data-tip="&8F52">write_entry_sector_info</a></span> <span class="comment">; Write dest directory entry</span></td> </tr> <tr id="addr-A8E5"> <td class="addr"><a href="#addr-A8E5">A8E5</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: copy sector addresses</span></td> </tr> <tr id="addr-A8E7"> <td class="addr"><a href="#addr-A8E7">A8E7</a></td> <td><span class="label">.copy_sector_addresses_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A8F4">← A8F4 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103A">wksp_alloc_sector</span>,y</span> <span class="comment">; Get source sector byte</span></td> </tr> <tr id="addr-A8EA"> <td class="addr"><a href="#addr-A8EA">A8EA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A8">wksp_copy_src_sector</span>,y</span> <span class="comment">; Store as read sector</span></td> </tr> <tr id="addr-A8ED"> <td class="addr"><a href="#addr-A8ED">A8ED</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103D">wksp_alloc_size</span>,y</span> <span class="comment">; Get dest sector byte</span></td> </tr> <tr id="addr-A8F0"> <td class="addr"><a href="#addr-A8F0">A8F0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A5">wksp_copy_write_sector</span>,y</span> <span class="comment">; Store as write sector</span></td> </tr> <tr id="addr-A8F3"> <td class="addr"><a href="#addr-A8F3">A8F3</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-A8F4"> <td class="addr"><a href="#addr-A8F4">A8F4</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A8E7" data-tip="&A8E7">copy_sector_addresses_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-A8F6"> <td class="addr"><a href="#addr-A8F6">A8F6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="131 &83 %10000011">#osbyte_read_oshwm</span></span> <span class="comment">; OSBYTE &83: read OSHWM</span></td> </tr> <tr id="addr-A8F8"> <td class="addr"><a href="#addr-A8F8">A8F8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Read top of operating system RAM address (OSHWM)</span></td> </tr> <tr id="addr-A8FB"> <td class="addr"><a href="#addr-A8FB">A8FB</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; X and Y contain the address of OSHWM (low, high)</span></td> </tr> <tr id="addr-A8FE"> <td class="addr"><a href="#addr-A8FE">A8FE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="132 &84 %10000100">#osbyte_read_himem</span></span> <span class="comment">; OSBYTE &84: read HIMEM</span></td> </tr> <tr id="addr-A900"> <td class="addr"><a href="#addr-A900">A900</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Read top of available user RAM (HIMEM)</span></td> </tr> <tr id="addr-A903"> <td class="addr"><a href="#addr-A903">A903</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; X and Y contain the address of HIMEM (low, high)</span></td> </tr> <tr id="addr-A904"> <td class="addr"><a href="#addr-A904">A904</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Calculate buffer size: HIMEM-OSHWM</span></td> </tr> <tr id="addr-A905"> <td class="addr"><a href="#addr-A905">A905</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Subtract OSHWM page</span></td> </tr> <tr id="addr-A908"> <td class="addr"><a href="#addr-A908">A908</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1061">wksp_compact_length</span></span> <span class="comment">; Store buffer size in pages</span></td> </tr> <tr id="addr-A90B"> <td class="addr"><a href="#addr-A90B">A90B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Set bit 3 of ADFS flags</span></td> </tr> <tr id="addr-A90D"> <td class="addr"><a href="#addr-A90D">A90D</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; Indicate copy operation in progress</span></td> </tr> <tr id="addr-A90F"> <td class="addr"><a href="#addr-A90F">A90F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-A911"> <td class="addr"><a href="#addr-A911">A911</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&106F">wksp_drive_number</span></span> <span class="comment">; Get source drive number</span></td> </tr> <tr id="addr-A914"> <td class="addr"><a href="#addr-A914">A914</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&10A4">wksp_copy_read_sector_2</span></span> <span class="comment">; OR into read sector high byte</span></td> </tr> <tr id="addr-A917"> <td class="addr"><a href="#addr-A917">A917</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A4">wksp_copy_read_sector_2</span></span> <span class="comment">; Store source drive+sector</span></td> </tr> <tr id="addr-A91A"> <td class="addr"><a href="#addr-A91A">A91A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1073">wksp_dest_drive</span></span> <span class="comment">; Get dest drive number</span></td> </tr> <tr id="addr-A91D"> <td class="addr"><a href="#addr-A91D">A91D</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&10AA">wksp_copy_src_sector_2</span></span> <span class="comment">; OR into write sector high byte</span></td> </tr> <tr id="addr-A920"> <td class="addr"><a href="#addr-A920">A920</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10AA">wksp_copy_src_sector_2</span></span> <span class="comment">; Store dest drive+sector</span></td> </tr> <tr id="addr-A923"> <td class="addr"><a href="#addr-A923">A923</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Save current drive</span></td> </tr> <tr id="addr-A926"> <td class="addr"><a href="#addr-A926">A926</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-A927"> <td class="addr"><a href="#addr-A927">A927</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Set drive to 0 temporarily</span></td> </tr> <tr id="addr-A929"> <td class="addr"><a href="#addr-A929">A929</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Store temporary drive</span></td> </tr> <tr id="addr-A92C"> <td class="addr"><a href="#addr-A92C">A92C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-96A6" data-tip="&96A6">execute_sector_copy</a></span> <span class="comment">; Execute sector-by-sector copy</span></td> </tr> <tr id="addr-A92F"> <td class="addr"><a href="#addr-A92F">A92F</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore original drive</span></td> </tr> <tr id="addr-A930"> <td class="addr"><a href="#addr-A930">A930</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set as current drive</span></td> </tr> <tr id="addr-A933"> <td class="addr"><a href="#addr-A933">A933</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write modified directory</span></td> </tr> <tr id="addr-A936"> <td class="addr"><a href="#addr-A936">A936</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A7C0" data-tip="&A7C0 – Set up disc read for directory load">setup_disc_read_for_dir</a></span> <span class="comment">; Set up for next source file</span></td> </tr> <tr id="addr-A939"> <td class="addr"><a href="#addr-A939">A939</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A88C" data-tip="&A88C">skip_dir_entry_or_done</a></span> <span class="comment">; Loop to copy next file</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A93C"> <td colspan="2"><div class="sub-header"> <h3>FSC 6: new filing system selected</h3> <div class="sub-desc"><p>Handle the FSC 6 call which notifies ADFS that a new filing system is being selected. Ensures all files are closed and workspace is saved.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A93C">A93C</a></td> <td><span class="label">.fsc6_new_filing_system<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9B1F">← 9B1F JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A70E" data-tip="&A70E – Get workspace address into &BA">get_wksp_addr_ba</a></span> <span class="comment">; Get workspace page address</span></td> </tr> <tr id="addr-A93F"> <td class="addr"><a href="#addr-A93F">A93F</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: store at byte 255</span></td> </tr> <tr id="addr-A941"> <td class="addr"><a href="#addr-A941">A941</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BA">zp_wksp_ptr_lo</span>),y</span> <span class="comment">; Mark workspace as needing save</span></td> </tr> <tr id="addr-A943"> <td class="addr"><a href="#addr-A943">A943</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Check if drive is initialised</span></td> </tr> <tr id="addr-A946"> <td class="addr"><a href="#addr-A946">A946</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Drive = &FF (uninitialised)?</span></td> </tr> <tr id="addr-A947"> <td class="addr"><a href="#addr-A947">A947</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-A95E" data-tip="&A95E">return_33</a></span> <span class="comment">; Yes, nothing more to do</span></td> </tr> <tr id="addr-A949"> <td class="addr"><a href="#addr-A949">A949</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="119 &77 %01110111 'w'">#osbyte_close_spool_exec</span></span> <span class="comment">; OSBYTE &77: close spool/exec files</span></td> </tr> <tr id="addr-A94B"> <td class="addr"><a href="#addr-A94B">A94B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Close any SPOOLed or EXECed files</span></td> </tr> <tr id="addr-A94E"> <td class="addr"><a href="#addr-A94E">A94E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state to disc</span></td> </tr> <tr id="addr-A951"> <td class="addr"><a href="#addr-A951">A951</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: will become 0 after INY</span></td> </tr> <tr id="addr-A953"> <td class="addr"><a href="#addr-A953">A953</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; A=&FF: flag for OSARGS</span></td> </tr> <tr id="addr-A954"> <td class="addr"><a href="#addr-A954">A954</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=0: falls through to osargs_handler</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A955"> <td colspan="2"><div class="sub-header"> <h3>OSARGS handler</h3> <div class="sub-desc"><p>Handle OSARGS calls for reading and writing file arguments (PTR, EXT, allocation) and filing system information.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A955">A955</a></td> <td><span class="label">.osargs_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9574">← 9574 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0? General OSARGS query</span></td> </tr> <tr id="addr-A957"> <td class="addr"><a href="#addr-A957">A957</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A995" data-tip="&A995">osargs_file_specific</a></span> <span class="comment">; Y!=0: file-specific handler</span></td> </tr> <tr id="addr-A959"> <td class="addr"><a href="#addr-A959">A959</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer A to Y (function code)</span></td> </tr> <tr id="addr-A95A"> <td class="addr"><a href="#addr-A95A">A95A</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A95F" data-tip="&A95F">osargs_general_query</a></span> <span class="comment">; A=0? Return FS number</span></td> </tr> <tr id="addr-A95C"> <td class="addr"><a href="#addr-A95C">A95C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; A=8: ADFS filing system number</span></td> </tr> <tr id="addr-A95E"> <td class="addr"><a href="#addr-A95E">A95E</a></td> <td><span class="label">.return_33<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A947">← A947 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (FS number in A)</span></td> </tr> <tr id="addr-A95F"> <td class="addr"><a href="#addr-A95F">A95F</a></td> <td><span class="label">.osargs_general_query<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A95A">← A95A BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save registers for later restore</span></td> </tr> <tr id="addr-A962"> <td class="addr"><a href="#addr-A962">A962</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Save X (zero page pointer)</span></td> </tr> <tr id="addr-A964"> <td class="addr"><a href="#addr-A964">A964</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Y=0 means function was 1</span></td> </tr> <tr id="addr-A965"> <td class="addr"><a href="#addr-A965">A965</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A97C" data-tip="&A97C – Flush all open channel buffers">flush_all_channels</a></span> <span class="comment">; A!=1: check further functions</span></td> </tr> <tr id="addr-A967"> <td class="addr"><a href="#addr-A967">A967</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10D6">wksp_cmd_tail</span></span> <span class="comment">; A=1: return command tail low byte</span></td> </tr> <tr id="addr-A96A"> <td class="addr"><a href="#addr-A96A">A96A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Store in zero page at X+0</span></td> </tr> <tr id="addr-A96C"> <td class="addr"><a href="#addr-A96C">A96C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10D7">wksp_cmd_tail_hi</span></span> <span class="comment">; Command tail high byte</span></td> </tr> <tr id="addr-A96F"> <td class="addr"><a href="#addr-A96F">A96F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Store in zero page at X+1</span></td> </tr> <tr id="addr-A971"> <td class="addr"><a href="#addr-A971">A971</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Y=&FF</span></td> </tr> <tr id="addr-A972"> <td class="addr"><a href="#addr-A972">A972</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Clear X+2 (high bytes)</span></td> </tr> <tr id="addr-A974"> <td class="addr"><a href="#addr-A974">A974</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Clear X+3</span></td> </tr> <tr id="addr-A976"> <td class="addr"><a href="#addr-A976">A976</a></td> <td><span class="label">.return_success<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A992">← A992 JMP</a><a href="#addr-AAC3">← AAC3 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-A978"> <td class="addr"><a href="#addr-A978">A978</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: success</span></td> </tr> <tr id="addr-A97A"> <td class="addr"><a href="#addr-A97A">A97A</a></td> <td> <span class="opcode">TAY</span></td> </tr> <tr id="addr-A97B"> <td class="addr"><a href="#addr-A97B">A97B</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (success)</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-A97C"> <td colspan="2"><div class="sub-header"> <h3>Flush all open channel buffers</h3> <div class="sub-desc"><p>Iterate all channel entries, flushing dirty buffers to disc and clearing state flags. Used by OSARGS A=&FF.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-A97C">A97C</a></td> <td><span class="label">.flush_all_channels<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-96AF">← 96AF JSR</a><a href="#addr-A965">← A965 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; X=&10: scan open channels</span></td> </tr> <tr id="addr-A97E"> <td class="addr"><a href="#addr-A97E">A97E</a></td> <td><span class="label">.flush_channels_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A98A">← A98A BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAF3" data-tip="&AAF3">flush_dirty_channel_buffer</a></span> <span class="comment">; Flush channel buffer if dirty</span></td> </tr> <tr id="addr-A981"> <td class="addr"><a href="#addr-A981">A981</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear workspace entry</span></td> </tr> <tr id="addr-A983"> <td class="addr"><a href="#addr-A983">A983</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Clear channel dirty flag</span></td> </tr> <tr id="addr-A986"> <td class="addr"><a href="#addr-A986">A986</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes (entry size)</span></td> </tr> <tr id="addr-A987"> <td class="addr"><a href="#addr-A987">A987</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes (next channel)</span></td> </tr> <tr id="addr-A988"> <td class="addr"><a href="#addr-A988">A988</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-A989"> <td class="addr"><a href="#addr-A989">A989</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-A98A"> <td class="addr"><a href="#addr-A98A">A98A</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-A97E" data-tip="&A97E">flush_channels_loop</a></span> <span class="comment">; Loop for all entries</span></td> </tr> <tr id="addr-A98C"> <td class="addr"><a href="#addr-A98C">A98C</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span></span> <span class="comment">; Increment flush counter</span></td> </tr> <tr id="addr-A98F"> <td class="addr"><a href="#addr-A98F">A98F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8305" data-tip="&8305 – Wait while files are being ensured">wait_ensuring</a></span></td> </tr> <tr id="addr-A992"> <td class="addr"><a href="#addr-A992">A992</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A976" data-tip="&A976">return_success</a></span> <span class="comment">; Return success</span></td> </tr> <tr id="addr-A995"> <td class="addr"><a href="#addr-A995">A995</a></td> <td><span class="label">.osargs_file_specific<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A957">← A957 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save regs for file-specific OSARGS</span></td> </tr> <tr id="addr-A998"> <td class="addr"><a href="#addr-A998">A998</a></td> <td><span class="label">.set_channel_and_dispatch<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5E1">← B5E1 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Save X (ZP pointer)</span></td> </tr> <tr id="addr-A99A"> <td class="addr"><a href="#addr-A99A">A99A</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save function code on stack</span></td> </tr> <tr id="addr-A99B"> <td class="addr"><a href="#addr-A99B">A99B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACFE" data-tip="&ACFE – Validate and set channel number from Y">check_set_channel_y</a></span></td> </tr> <tr id="addr-A99E"> <td class="addr"><a href="#addr-A99E">A99E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B18C" data-tip="&B18C – Synchronise EXT to PTR if at EOF">sync_ext_to_ptr</a></span> <span class="comment">; Flush channel buffer</span></td> </tr> <tr id="addr-A9A1"> <td class="addr"><a href="#addr-A9A1">A9A1</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore function code</span></td> </tr> <tr id="addr-A9A2"> <td class="addr"><a href="#addr-A9A2">A9A2</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-A9A4"> <td class="addr"><a href="#addr-A9A4">A9A4</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; A still non-zero?</span></td> </tr> <tr id="addr-A9A5"> <td class="addr"><a href="#addr-A9A5">A9A5</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-A9C7" data-tip="&A9C7">check_write_ptr</a></span> <span class="comment">; A=2: skip (A-1!=0 means not A=2)</span></td> </tr> <tr id="addr-A9A7"> <td class="addr"><a href="#addr-A9A7">A9A7</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; A=2: read PTR to user zero page</span></td> </tr> <tr id="addr-A9A9"> <td class="addr"><a href="#addr-A9A9">A9A9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,y</span> <span class="comment">; Get PTR low byte from channel table</span></td> </tr> <tr id="addr-A9AC"> <td class="addr"><a href="#addr-A9AC">A9AC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Store at user's X+0</span></td> </tr> <tr id="addr-A9AE"> <td class="addr"><a href="#addr-A9AE">A9AE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,y</span> <span class="comment">; Get PTR mid-low byte</span></td> </tr> <tr id="addr-A9B1"> <td class="addr"><a href="#addr-A9B1">A9B1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Store PTR mid-low at X+1</span></td> </tr> <tr id="addr-A9B3"> <td class="addr"><a href="#addr-A9B3">A9B3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,y</span> <span class="comment">; Get PTR mid-high byte</span></td> </tr> <tr id="addr-A9B6"> <td class="addr"><a href="#addr-A9B6">A9B6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Store PTR mid-high at X+2</span></td> </tr> <tr id="addr-A9B8"> <td class="addr"><a href="#addr-A9B8">A9B8</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,y</span> <span class="comment">; Get PTR high byte</span></td> </tr> <tr id="addr-A9BB"> <td class="addr"><a href="#addr-A9BB">A9BB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Store PTR high at X+3</span></td> </tr> <tr id="addr-A9BD"> <td class="addr"><a href="#addr-A9BD">A9BD</a></td> <td><span class="label">.return_after_flag_update<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AA00">← AA00 JMP</a><a href="#addr-AA32">← AA32 JMP</a><a href="#addr-AA5F">← AA5F JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B13F" data-tip="&B13F">update_channel_flags_for_ptr</a></span> <span class="comment">; Ensure channel state is consistent</span></td> </tr> <tr id="addr-A9C0"> <td class="addr"><a href="#addr-A9C0">A9C0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: success return</span></td> </tr> <tr id="addr-A9C2"> <td class="addr"><a href="#addr-A9C2">A9C2</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-A9C4"> <td class="addr"><a href="#addr-A9C4">A9C4</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C2">zp_save_y</span></span> <span class="comment">; Restore Y</span></td> </tr> <tr id="addr-A9C6"> <td class="addr"><a href="#addr-A9C6">A9C6</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-A9C7"> <td class="addr"><a href="#addr-A9C7">A9C7</a></td> <td><span class="label">.check_write_ptr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A9A5">← A9A5 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement: A=3 (write PTR)?</span></td> </tr> <tr id="addr-A9C8"> <td class="addr"><a href="#addr-A9C8">A9C8</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AA46" data-tip="&AA46">check_write_ext</a></span> <span class="comment">; No, check A=4</span></td> </tr> <tr id="addr-A9CA"> <td class="addr"><a href="#addr-A9CA">A9CA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,y</span> <span class="comment">; A=3: check file is open for write</span></td> </tr> <tr id="addr-A9CD"> <td class="addr"><a href="#addr-A9CD">A9CD</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AA03" data-tip="&AA03">not_open_for_update</a></span> <span class="comment">; Bit 7 clear: read-only, error</span></td> </tr> <tr id="addr-A9CF"> <td class="addr"><a href="#addr-A9CF">A9CF</a></td> <td><span class="label">.copy_new_ptr_from_user<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AAA3">← AAA3 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; A=3: copy new PTR from user's ZP</span></td> </tr> <tr id="addr-A9D1"> <td class="addr"><a href="#addr-A9D1">A9D1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Get new PTR low byte</span></td> </tr> <tr id="addr-A9D3"> <td class="addr"><a href="#addr-A9D3">A9D3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Store new PTR low in workspace</span></td> </tr> <tr id="addr-A9D6"> <td class="addr"><a href="#addr-A9D6">A9D6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Get new PTR mid-low from user ZP</span></td> </tr> <tr id="addr-A9D8"> <td class="addr"><a href="#addr-A9D8">A9D8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-A9DB"> <td class="addr"><a href="#addr-A9DB">A9DB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Get new PTR mid-high</span></td> </tr> <tr id="addr-A9DD"> <td class="addr"><a href="#addr-A9DD">A9DD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-A9E0"> <td class="addr"><a href="#addr-A9E0">A9E0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Get new PTR high</span></td> </tr> <tr id="addr-A9E2"> <td class="addr"><a href="#addr-A9E2">A9E2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-A9E5"> <td class="addr"><a href="#addr-A9E5">A9E5</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AE59" data-tip="&AE59">check_ptr_within_allocation</a></span> <span class="comment">; Validate and apply new PTR</span></td> </tr> <tr id="addr-A9E8"> <td class="addr"><a href="#addr-A9E8">A9E8</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Store new PTR in channel table</span></td> </tr> <tr id="addr-A9EA"> <td class="addr"><a href="#addr-A9EA">A9EA</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-A9EC"> <td class="addr"><a href="#addr-A9EC">A9EC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Get validated PTR low from user ZP</span></td> </tr> <tr id="addr-A9EE"> <td class="addr"><a href="#addr-A9EE">A9EE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,y</span> <span class="comment">; Set PTR low byte</span></td> </tr> <tr id="addr-A9F1"> <td class="addr"><a href="#addr-A9F1">A9F1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Get PTR mid-low</span></td> </tr> <tr id="addr-A9F3"> <td class="addr"><a href="#addr-A9F3">A9F3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,y</span> <span class="comment">; Set PTR mid-low byte</span></td> </tr> <tr id="addr-A9F6"> <td class="addr"><a href="#addr-A9F6">A9F6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Get PTR mid-high</span></td> </tr> <tr id="addr-A9F8"> <td class="addr"><a href="#addr-A9F8">A9F8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,y</span> <span class="comment">; Set PTR mid-high byte</span></td> </tr> <tr id="addr-A9FB"> <td class="addr"><a href="#addr-A9FB">A9FB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Get PTR high</span></td> </tr> <tr id="addr-A9FD"> <td class="addr"><a href="#addr-A9FD">A9FD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,y</span> <span class="comment">; Set PTR high byte</span></td> </tr> <tr id="addr-AA00"> <td class="addr"><a href="#addr-AA00">AA00</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A9BD" data-tip="&A9BD">return_after_flag_update</a></span> <span class="comment">; Jump to success return</span></td> </tr> <tr id="addr-AA03"> <td class="addr"><a href="#addr-AA03">AA03</a></td> <td><span class="label">.not_open_for_update<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A9CD">← A9CD BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; A=3: check new PTR <= EXT</span></td> </tr> <tr id="addr-AA05"> <td class="addr"><a href="#addr-AA05">AA05</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index for EXT compare</span></td> </tr> <tr id="addr-AA07"> <td class="addr"><a href="#addr-AA07">AA07</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Subtract new PTR from EXT</span></td> </tr> <tr id="addr-AA08"> <td class="addr"><a href="#addr-AA08">AA08</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,y</span> <span class="comment">; Get EXT low byte</span></td> </tr> <tr id="addr-AA0B"> <td class="addr"><a href="#addr-AA0B">AA0B</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Subtract new PTR low</span></td> </tr> <tr id="addr-AA0D"> <td class="addr"><a href="#addr-AA0D">AA0D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,y</span> <span class="comment">; Get EXT mid-low</span></td> </tr> <tr id="addr-AA10"> <td class="addr"><a href="#addr-AA10">AA10</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Subtract new PTR mid-low</span></td> </tr> <tr id="addr-AA12"> <td class="addr"><a href="#addr-AA12">AA12</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,y</span> <span class="comment">; Get EXT mid-high</span></td> </tr> <tr id="addr-AA15"> <td class="addr"><a href="#addr-AA15">AA15</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Subtract new PTR mid-high</span></td> </tr> <tr id="addr-AA17"> <td class="addr"><a href="#addr-AA17">AA17</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,y</span> <span class="comment">; Get EXT high</span></td> </tr> <tr id="addr-AA1A"> <td class="addr"><a href="#addr-AA1A">AA1A</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Subtract new PTR high</span></td> </tr> <tr id="addr-AA1C"> <td class="addr"><a href="#addr-AA1C">AA1C</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AA35" data-tip="&AA35">check_read_allocation</a></span> <span class="comment">; New PTR > EXT: error</span></td> </tr> <tr id="addr-AA1E"> <td class="addr"><a href="#addr-AA1E">AA1E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; New PTR <= EXT: set PTR</span></td> </tr> <tr id="addr-AA20"> <td class="addr"><a href="#addr-AA20">AA20</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,y</span> <span class="comment">; Set new PTR low byte</span></td> </tr> <tr id="addr-AA23"> <td class="addr"><a href="#addr-AA23">AA23</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Get mid-low from user ZP</span></td> </tr> <tr id="addr-AA25"> <td class="addr"><a href="#addr-AA25">AA25</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,y</span> <span class="comment">; Set PTR mid-low</span></td> </tr> <tr id="addr-AA28"> <td class="addr"><a href="#addr-AA28">AA28</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Get mid-high from user ZP</span></td> </tr> <tr id="addr-AA2A"> <td class="addr"><a href="#addr-AA2A">AA2A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,y</span> <span class="comment">; Set PTR mid-high</span></td> </tr> <tr id="addr-AA2D"> <td class="addr"><a href="#addr-AA2D">AA2D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Get high from user ZP</span></td> </tr> <tr id="addr-AA2F"> <td class="addr"><a href="#addr-AA2F">AA2F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,y</span> <span class="comment">; Set PTR high</span></td> </tr> <tr id="addr-AA32"> <td class="addr"><a href="#addr-AA32">AA32</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A9BD" data-tip="&A9BD">return_after_flag_update</a></span> <span class="comment">; Jump to success return</span></td> </tr> <tr id="addr-AA35"> <td class="addr"><a href="#addr-AA35">AA35</a></td> <td><span class="label">.check_read_allocation<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AA1C">← AA1C BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-AA38"> <td class="addr"><a href="#addr-AA38">AA38</a></td> <td> <span class="directive">EQUB</span> <span data-tip="183 &B7 %10110111">&B7</span> <span class="comment">; Error &B7: Outside file</span></td> </tr> <tr id="addr-AA39"> <td class="addr"><a href="#addr-AA39">AA39</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Outside file"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-AA46"> <td class="addr"><a href="#addr-AA46">AA46</a></td> <td><span class="label">.check_write_ext<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-A9C8">← A9C8 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement: A=4 (read EXT)?</span></td> </tr> <tr id="addr-AA47"> <td class="addr"><a href="#addr-AA47">AA47</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AA62" data-tip="&AA62">read_ext_value</a></span> <span class="comment">; No, check A=5</span></td> </tr> <tr id="addr-AA49"> <td class="addr"><a href="#addr-AA49">AA49</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; A=4: read EXT to user zero page</span></td> </tr> <tr id="addr-AA4B"> <td class="addr"><a href="#addr-AA4B">AA4B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,y</span> <span class="comment">; Get EXT low byte</span></td> </tr> <tr id="addr-AA4E"> <td class="addr"><a href="#addr-AA4E">AA4E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Store EXT low at user X+0</span></td> </tr> <tr id="addr-AA50"> <td class="addr"><a href="#addr-AA50">AA50</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,y</span> <span class="comment">; Get EXT mid-low</span></td> </tr> <tr id="addr-AA53"> <td class="addr"><a href="#addr-AA53">AA53</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Store at user X+1</span></td> </tr> <tr id="addr-AA55"> <td class="addr"><a href="#addr-AA55">AA55</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,y</span> <span class="comment">; Get EXT mid-high</span></td> </tr> <tr id="addr-AA58"> <td class="addr"><a href="#addr-AA58">AA58</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Store at user X+2</span></td> </tr> <tr id="addr-AA5A"> <td class="addr"><a href="#addr-AA5A">AA5A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,y</span> <span class="comment">; Get EXT high</span></td> </tr> <tr id="addr-AA5D"> <td class="addr"><a href="#addr-AA5D">AA5D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Store at user X+3</span></td> </tr> <tr id="addr-AA5F"> <td class="addr"><a href="#addr-AA5F">AA5F</a></td> <td><span class="label">.read_allocation_size<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AAA1">← AAA1 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A9BD" data-tip="&A9BD">return_after_flag_update</a></span> <span class="comment">; Jump to success return</span></td> </tr> <tr id="addr-AA62"> <td class="addr"><a href="#addr-AA62">AA62</a></td> <td><span class="label">.read_ext_value<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AA47">← AA47 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement: A=5 (write EXT)?</span></td> </tr> <tr id="addr-AA63"> <td class="addr"><a href="#addr-AA63">AA63</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AAA6" data-tip="&AAA6 – Flush buffers and set file pointer">validate_and_set_ptr</a></span> <span class="comment">; No, handle ensure</span></td> </tr> <tr id="addr-AA65"> <td class="addr"><a href="#addr-AA65">AA65</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; A=5: check file is open for write</span></td> </tr> <tr id="addr-AA67"> <td class="addr"><a href="#addr-AA67">AA67</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,y</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-AA6A"> <td class="addr"><a href="#addr-AA6A">AA6A</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-AA6F" data-tip="&AA6F">write_new_ext</a></span> <span class="comment">; Bit 7 set: writable, proceed</span></td> </tr> <tr id="addr-AA6C"> <td class="addr"><a href="#addr-AA6C">AA6C</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B09D" data-tip="&B09D">not_open_for_update_error</a></span> <span class="comment">; Not writable: error</span></td> </tr> <tr id="addr-AA6F"> <td class="addr"><a href="#addr-AA6F">AA6F</a></td> <td><span class="label">.write_new_ext<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AA6A">← AA6A BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Copy new EXT from user's ZP</span></td> </tr> <tr id="addr-AA71"> <td class="addr"><a href="#addr-AA71">AA71</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Store new EXT low in workspace</span></td> </tr> <tr id="addr-AA74"> <td class="addr"><a href="#addr-AA74">AA74</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Get new EXT mid-low</span></td> </tr> <tr id="addr-AA76"> <td class="addr"><a href="#addr-AA76">AA76</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-AA79"> <td class="addr"><a href="#addr-AA79">AA79</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Get new EXT mid-high</span></td> </tr> <tr id="addr-AA7B"> <td class="addr"><a href="#addr-AA7B">AA7B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-AA7E"> <td class="addr"><a href="#addr-AA7E">AA7E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Get new EXT high</span></td> </tr> <tr id="addr-AA80"> <td class="addr"><a href="#addr-AA80">AA80</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-AA83"> <td class="addr"><a href="#addr-AA83">AA83</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AE59" data-tip="&AE59">check_ptr_within_allocation</a></span> <span class="comment">; Validate and apply new EXT</span></td> </tr> <tr id="addr-AA86"> <td class="addr"><a href="#addr-AA86">AA86</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-AA88"> <td class="addr"><a href="#addr-AA88">AA88</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AA8A"> <td class="addr"><a href="#addr-AA8A">AA8A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0000">zp_user_ptr_0</span>,x</span> <span class="comment">; Get validated EXT low</span></td> </tr> <tr id="addr-AA8C"> <td class="addr"><a href="#addr-AA8C">AA8C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,y</span> <span class="comment">; Set channel EXT low</span></td> </tr> <tr id="addr-AA8F"> <td class="addr"><a href="#addr-AA8F">AA8F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0001">zp_user_ptr_1</span>,x</span> <span class="comment">; Get EXT mid-low</span></td> </tr> <tr id="addr-AA91"> <td class="addr"><a href="#addr-AA91">AA91</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,y</span> <span class="comment">; Set channel EXT mid-low</span></td> </tr> <tr id="addr-AA94"> <td class="addr"><a href="#addr-AA94">AA94</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0002">zp_user_ptr_2</span>,x</span> <span class="comment">; Get EXT mid-high</span></td> </tr> <tr id="addr-AA96"> <td class="addr"><a href="#addr-AA96">AA96</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,y</span> <span class="comment">; Set channel EXT mid-high</span></td> </tr> <tr id="addr-AA99"> <td class="addr"><a href="#addr-AA99">AA99</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0003">zp_user_ptr_3</span>,x</span> <span class="comment">; Get EXT high</span></td> </tr> <tr id="addr-AA9B"> <td class="addr"><a href="#addr-AA9B">AA9B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,y</span> <span class="comment">; Set channel EXT high</span></td> </tr> <tr id="addr-AA9E"> <td class="addr"><a href="#addr-AA9E">AA9E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AD16" data-tip="&AD16 – Compare file EXT to PTR">compare_ext_to_ptr</a></span></td> </tr> <tr id="addr-AAA1"> <td class="addr"><a href="#addr-AAA1">AAA1</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AA5F" data-tip="&AA5F">read_allocation_size</a></span> <span class="comment">; EXT >= current: just update table</span></td> </tr> <tr id="addr-AAA3"> <td class="addr"><a href="#addr-AAA3">AAA3</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A9CF" data-tip="&A9CF">copy_new_ptr_from_user</a></span> <span class="comment">; EXT < current: also update PTR</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AAA6"> <td colspan="2"><div class="sub-header"> <h3>Flush buffers and set file pointer</h3> <div class="sub-desc"><p>Scan the ensure table for entries matching the current channel and flush any dirty buffers before updating PTR.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AAA6">AAA6</a></td> <td><span class="label">.validate_and_set_ptr<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AA63">← AA63 BNE</a><a href="#addr-B3E4">← B3E4 JSR</a><a href="#addr-B7CF">← B7CF JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; X=&10: scan ensure table</span></td> </tr> <tr id="addr-AAA8"> <td class="addr"><a href="#addr-AAA8">AAA8</a></td> <td><span class="label">.copy_ptr_to_channel_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AAC1">← AAC1 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get ensure table entry</span></td> </tr> <tr id="addr-AAAB"> <td class="addr"><a href="#addr-AAAB">AAAB</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Shift right to get channel index</span></td> </tr> <tr id="addr-AAAC"> <td class="addr"><a href="#addr-AAAC">AAAC</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="15 &0F %00001111 SI">#&0f</span></span> <span class="comment">; Mask to 4-bit channel number</span></td> </tr> <tr id="addr-AAAE"> <td class="addr"><a href="#addr-AAAE">AAAE</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; This channel's entry?</span></td> </tr> <tr id="addr-AAB0"> <td class="addr"><a href="#addr-AAB0">AAB0</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AABD" data-tip="&AABD">set_ptr_complete</a></span> <span class="comment">; No, skip to next entry</span></td> </tr> <tr id="addr-AAB2"> <td class="addr"><a href="#addr-AAB2">AAB2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAF3" data-tip="&AAF3">flush_dirty_channel_buffer</a></span> <span class="comment">; Flush this entry's buffer</span></td> </tr> <tr id="addr-AAB5"> <td class="addr"><a href="#addr-AAB5">AAB5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get ensure table entry again</span></td> </tr> <tr id="addr-AAB8"> <td class="addr"><a href="#addr-AAB8">AAB8</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Keep only bit 0 (dirty flag)</span></td> </tr> <tr id="addr-AABA"> <td class="addr"><a href="#addr-AABA">AABA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Clear other bits</span></td> </tr> <tr id="addr-AABD"> <td class="addr"><a href="#addr-AABD">AABD</a></td> <td><span class="label">.set_ptr_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AAB0">← AAB0 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes</span></td> </tr> <tr id="addr-AABE"> <td class="addr"><a href="#addr-AABE">AABE</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AABF"> <td class="addr"><a href="#addr-AABF">AABF</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AAC0"> <td class="addr"><a href="#addr-AAC0">AAC0</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AAC1"> <td class="addr"><a href="#addr-AAC1">AAC1</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AAA8" data-tip="&AAA8">copy_ptr_to_channel_loop</a></span> <span class="comment">; Loop for all ensure entries</span></td> </tr> <tr id="addr-AAC3"> <td class="addr"><a href="#addr-AAC3">AAC3</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A976" data-tip="&A976">return_success</a></span> <span class="comment">; Return success</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AAC6"> <td colspan="2"><div class="sub-header"> <h3>Hard drive single sector for BGET/BPUT</h3> <div class="sub-desc"><p>Read or write a single sector via the SCSI interface for byte-level file access (BGET/BPUT channel operations).</p> </div> <div class="sub-registers"><table> <tr><th rowspan="2">On Entry</th><td>A</td><td>SCSI command byte (&08=read, &0A=write)</td></tr> <tr><td>X</td><td>channel buffer table offset</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AAC6">AAC6</a></td> <td><span class="label">.hd_command_bget_bput_sector<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AB4F">← AB4F JSR</a><a href="#addr-ACB4">← ACB4 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PHA</span> <span class="comment">; Wait if files being ensured</span></td> </tr> <tr id="addr-AAC7"> <td class="addr"><a href="#addr-AAC7">AAC7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8305" data-tip="&8305 – Wait while files are being ensured">wait_ensuring</a></span></td> </tr> <tr id="addr-AACA"> <td class="addr"><a href="#addr-AACA">AACA</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8067" data-tip="&8067">scsi_start_command2</a></span> <span class="comment">; Start SCSI command phase (Y in cmd)</span></td> </tr> <tr id="addr-AACD"> <td class="addr"><a href="#addr-AACD">AACD</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore command byte from stack</span></td> </tr> <tr id="addr-AACE"> <td class="addr"><a href="#addr-AACE">AACE</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-82FB" data-tip="&82FB – Send one byte during SCSI command phase">scsi_send_cmd_byte</a></span></td> </tr> <tr id="addr-AAD1"> <td class="addr"><a href="#addr-AAD1">AAD1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Get drive+LUN from channel block</span></td> </tr> <tr id="addr-AAD4"> <td class="addr"><a href="#addr-AAD4">AAD4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1133">wksp_current_drive_hi</span></span> <span class="comment">; Save as current drive info</span></td> </tr> <tr id="addr-AAD7"> <td class="addr"><a href="#addr-AAD7">AAD7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-82FB" data-tip="&82FB – Send one byte during SCSI command phase">scsi_send_cmd_byte</a></span></td> </tr> <tr id="addr-AADA"> <td class="addr"><a href="#addr-AADA">AADA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Get sector address high</span></td> </tr> <tr id="addr-AADD"> <td class="addr"><a href="#addr-AADD">AADD</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-82FB" data-tip="&82FB – Send one byte during SCSI command phase">scsi_send_cmd_byte</a></span></td> </tr> <tr id="addr-AAE0"> <td class="addr"><a href="#addr-AAE0">AAE0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Get sector address mid</span></td> </tr> <tr id="addr-AAE3"> <td class="addr"><a href="#addr-AAE3">AAE3</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-82FB" data-tip="&82FB – Send one byte during SCSI command phase">scsi_send_cmd_byte</a></span></td> </tr> <tr id="addr-AAE6"> <td class="addr"><a href="#addr-AAE6">AAE6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Sector count = 1</span></td> </tr> <tr id="addr-AAE8"> <td class="addr"><a href="#addr-AAE8">AAE8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-82FB" data-tip="&82FB – Send one byte during SCSI command phase">scsi_send_cmd_byte</a></span></td> </tr> <tr id="addr-AAEB"> <td class="addr"><a href="#addr-AAEB">AAEB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Control byte = 0</span></td> </tr> <tr id="addr-AAED"> <td class="addr"><a href="#addr-AAED">AAED</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-82FB" data-tip="&82FB – Send one byte during SCSI command phase">scsi_send_cmd_byte</a></span> <span class="comment">; Send last command byte and return</span></td> </tr> <tr id="addr-AAF0"> <td class="addr"><a href="#addr-AAF0">AAF0</a></td> <td><span class="label">.calc_channel_buffer_page<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC5F">← AC5F JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACD7" data-tip="&ACD7 – Calculate buffer page from channel offset">calc_buffer_page_from_offset</a></span> <span class="comment">; Calculate buffer page from channel</span></td> </tr> <tr id="addr-AAF3"> <td class="addr"><a href="#addr-AAF3">AAF3</a></td> <td><span class="label">.flush_dirty_channel_buffer<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-A97E">← A97E JSR</a><a href="#addr-AAB2">← AAB2 JSR</a><a href="#addr-B035">← B035 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABA5" data-tip="&ABA5">ensure_channel_buffer</a></span> <span class="comment">; Ensure channel buffer is allocated</span></td> </tr> <tr id="addr-AAF6"> <td class="addr"><a href="#addr-AAF6">AAF6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state byte</span></td> </tr> <tr id="addr-AAF9"> <td class="addr"><a href="#addr-AAF9">AAF9</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; State >= &C0 (dirty write)?</span></td> </tr> <tr id="addr-AAFB"> <td class="addr"><a href="#addr-AAFB">AAFB</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AB77" data-tip="&AB77">return_34</a></span> <span class="comment">; No: buffer clean, return</span></td> </tr> <tr id="addr-AAFD"> <td class="addr"><a href="#addr-AAFD">AAFD</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer X to A</span></td> </tr> <tr id="addr-AAFE"> <td class="addr"><a href="#addr-AAFE">AAFE</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Divide by 4 for channel number</span></td> </tr> <tr id="addr-AAFF"> <td class="addr"><a href="#addr-AAFF">AAFF</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Second shift</span></td> </tr> <tr id="addr-AB00"> <td class="addr"><a href="#addr-AB00">AB00</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="23 &17 %00010111 ETB">#&17</span></span> <span class="comment">; Add &17 for buffer page base</span></td> </tr> <tr id="addr-AB02"> <td class="addr"><a href="#addr-AB02">AB02</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BD">zp_buf_src_hi</span></span> <span class="comment">; Store as buffer high byte</span></td> </tr> <tr id="addr-AB04"> <td class="addr"><a href="#addr-AB04">AB04</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Buffer low byte = 0</span></td> </tr> <tr id="addr-AB06"> <td class="addr"><a href="#addr-AB06">AB06</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BC">zp_buf_src_lo</span></span> <span class="comment">; Store buffer low byte</span></td> </tr> <tr id="addr-AB08"> <td class="addr"><a href="#addr-AB08">AB08</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state</span></td> </tr> <tr id="addr-AB0B"> <td class="addr"><a href="#addr-AB0B">AB0B</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="191 &BF %10111111">#&bf</span></span> <span class="comment">; Clear bit 6 (dirty flag)</span></td> </tr> <tr id="addr-AB0D"> <td class="addr"><a href="#addr-AB0D">AB0D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Store cleared state</span></td> </tr> <tr id="addr-AB10"> <td class="addr"><a href="#addr-AB10">AB10</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="30 &1E %00011110 RS">#&1e</span></span> <span class="comment">; Isolate channel number bits</span></td> </tr> <tr id="addr-AB12"> <td class="addr"><a href="#addr-AB12">AB12</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Shift right to get channel index</span></td> </tr> <tr id="addr-AB13"> <td class="addr"><a href="#addr-AB13">AB13</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; OR with &30 to get file handle</span></td> </tr> <tr id="addr-AB15"> <td class="addr"><a href="#addr-AB15">AB15</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D4">wksp_err_handle</span></span> <span class="comment">; Store file handle for errors</span></td> </tr> <tr id="addr-AB18"> <td class="addr"><a href="#addr-AB18">AB18</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Get sector address low</span></td> </tr> <tr id="addr-AB1B"> <td class="addr"><a href="#addr-AB1B">AB1B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D0">wksp_err_sector</span></span> <span class="comment">; Store in error sector workspace</span></td> </tr> <tr id="addr-AB1E"> <td class="addr"><a href="#addr-AB1E">AB1E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Get sector address mid</span></td> </tr> <tr id="addr-AB21"> <td class="addr"><a href="#addr-AB21">AB21</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D1">wksp_err_sector_mid</span></span> <span class="comment">; Store in error sector mid</span></td> </tr> <tr id="addr-AB24"> <td class="addr"><a href="#addr-AB24">AB24</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Get drive+sector high</span></td> </tr> <tr id="addr-AB27"> <td class="addr"><a href="#addr-AB27">AB27</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D2">wksp_err_sector_hi</span></span> <span class="comment">; Store in error sector high</span></td> </tr> <tr id="addr-AB2A"> <td class="addr"><a href="#addr-AB2A">AB2A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B51C" data-tip="&B51C – Set current drive from channel's drive">set_drive_from_channel</a></span> <span class="comment">; Flush channel if dirty</span></td> </tr> <tr id="addr-AB2D"> <td class="addr"><a href="#addr-AB2D">AB2D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8080" data-tip="&8080 – Set retry count for disc operation">command_set_retries</a></span></td> </tr> <tr id="addr-AB30"> <td class="addr"><a href="#addr-AB30">AB30</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C1">zp_name_ptr_hi</span></span> <span class="comment">; Save channel index</span></td> </tr> <tr id="addr-AB32"> <td class="addr"><a href="#addr-AB32">AB32</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Check for hard drive</span></td> </tr> <tr id="addr-AB34"> <td class="addr"><a href="#addr-AB34">AB34</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Bit 5: hard drive present?</span></td> </tr> <tr id="addr-AB36"> <td class="addr"><a href="#addr-AB36">AB36</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AB3D" data-tip="&AB3D">write_dirty_sector_to_disc</a></span> <span class="comment">; No HD: use floppy</span></td> </tr> <tr id="addr-AB38"> <td class="addr"><a href="#addr-AB38">AB38</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Get drive number from channel</span></td> </tr> <tr id="addr-AB3B"> <td class="addr"><a href="#addr-AB3B">AB3B</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AB4B" data-tip="&AB4B">hd_bput_write_sector</a></span> <span class="comment">; Bit 7 clear: use SCSI hard drive</span></td> </tr> <tr id="addr-AB3D"> <td class="addr"><a href="#addr-AB3D">AB3D</a></td> <td><span class="label">.write_dirty_sector_to_disc<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AB36">← AB36 BEQ</a><a href="#addr-AB46">← AB46 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C1">zp_name_ptr_hi</span></span> <span class="comment">; Restore channel index for floppy</span></td> </tr> <tr id="addr-AB3F"> <td class="addr"><a href="#addr-AB3F">AB3F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BA06" data-tip="&BA06">exec_floppy_write_bput_sector_ind</a></span> <span class="comment">; Execute floppy write sector</span></td> </tr> <tr id="addr-AB42"> <td class="addr"><a href="#addr-AB42">AB42</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AB75" data-tip="&AB75">write_buffer_to_scsi_loop</a></span> <span class="comment">; Success? Done</span></td> </tr> <tr id="addr-AB44"> <td class="addr"><a href="#addr-AB44">AB44</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&00CE">zp_retry_count</span></span> <span class="comment">; Decrement retry counter</span></td> </tr> <tr id="addr-AB46"> <td class="addr"><a href="#addr-AB46">AB46</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AB3D" data-tip="&AB3D">write_dirty_sector_to_disc</a></span> <span class="comment">; More retries: try again</span></td> </tr> <tr id="addr-AB48"> <td class="addr"><a href="#addr-AB48">AB48</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-829A" data-tip="&829A – Generate a BRK error">generate_error</a></span></td> </tr> <tr id="addr-AB4B"> <td class="addr"><a href="#addr-AB4B">AB4B</a></td> <td><span class="label">.hd_bput_write_sector<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AB3B">← AB3B BPL</a><a href="#addr-AB5E">← AB5E BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C1">zp_name_ptr_hi</span></span> <span class="comment">; Restore channel index for SCSI</span></td> </tr> <tr id="addr-AB4D"> <td class="addr"><a href="#addr-AB4D">AB4D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; A=&0A: SCSI write command</span></td> </tr> <tr id="addr-AB4F"> <td class="addr"><a href="#addr-AB4F">AB4F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAC6" data-tip="&AAC6 – Hard drive single sector for BGET/BPUT">hd_command_bget_bput_sector</a></span></td> </tr> <tr id="addr-AB52"> <td class="addr"><a href="#addr-AB52">AB52</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: data transfer index</span></td> </tr> <tr id="addr-AB54"> <td class="addr"><a href="#addr-AB54">AB54</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-830F" data-tip="&830F – Wait for SCSI REQ signal">scsi_wait_for_req</a></span></td> </tr> <tr id="addr-AB57"> <td class="addr"><a href="#addr-AB57">AB57</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AB63" data-tip="&AB63 – Write 256 bytes to SCSI bus">scsi_write_page</a></span> <span class="comment">; Status OK: continue</span></td> </tr> <tr id="addr-AB59"> <td class="addr"><a href="#addr-AB59">AB59</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-818A" data-tip="&818A – Complete SCSI command and read status">command_done</a></span></td> </tr> <tr id="addr-AB5C"> <td class="addr"><a href="#addr-AB5C">AB5C</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&00CE">zp_retry_count</span></span> <span class="comment">; Decrement retry counter</span></td> </tr> <tr id="addr-AB5E"> <td class="addr"><a href="#addr-AB5E">AB5E</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AB4B" data-tip="&AB4B">hd_bput_write_sector</a></span> <span class="comment">; More retries: try write again</span></td> </tr> <tr id="addr-AB60"> <td class="addr"><a href="#addr-AB60">AB60</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-829A" data-tip="&829A – Generate a BRK error">generate_error</a></span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AB63"> <td colspan="2"><div class="sub-header"> <h3>Write 256 bytes to SCSI bus</h3> <div class="sub-desc"><p>Transfer a page from (zp_buf_src) to the SCSI data register, then set the ensuring flag.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AB63">AB63</a></td> <td><span class="label">.scsi_write_page<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AB57">← AB57 BPL</a><a href="#addr-AB69">← AB69 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00BC">zp_buf_src_lo</span>),y</span> <span class="comment">; Get byte from buffer</span></td> </tr> <tr id="addr-AB65"> <td class="addr"><a href="#addr-AB65">AB65</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FC40">fred_hard_drive_0</span></span> <span class="comment">; Write to SCSI data bus</span></td> </tr> <tr id="addr-AB68"> <td class="addr"><a href="#addr-AB68">AB68</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-AB69"> <td class="addr"><a href="#addr-AB69">AB69</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AB63" data-tip="&AB63 – Write 256 bytes to SCSI bus">scsi_write_page</a></span> <span class="comment">; Loop for 256 bytes</span></td> </tr> <tr id="addr-AB6B"> <td class="addr"><a href="#addr-AB6B">AB6B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Set ensuring flag</span></td> </tr> <tr id="addr-AB6D"> <td class="addr"><a href="#addr-AB6D">AB6D</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; OR into ADFS flags</span></td> </tr> <tr id="addr-AB6F"> <td class="addr"><a href="#addr-AB6F">AB6F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-AB71"> <td class="addr"><a href="#addr-AB71">AB71</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Y=&FF: disable SCSI IRQ</span></td> </tr> <tr id="addr-AB72"> <td class="addr"><a href="#addr-AB72">AB72</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&FC43">fred_hard_drive_3</span></span> <span class="comment">; Write to SCSI IRQ enable register</span></td> </tr> <tr id="addr-AB75"> <td class="addr"><a href="#addr-AB75">AB75</a></td> <td><span class="label">.write_buffer_to_scsi_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AB42">← AB42 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C1">zp_name_ptr_hi</span></span> <span class="comment">; Restore channel index</span></td> </tr> <tr id="addr-AB77"> <td class="addr"><a href="#addr-AB77">AB77</a></td> <td><span class="label">.return_34<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AAFB">← AAFB BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (success)</span></td> </tr> <tr id="addr-AB78"> <td class="addr"><a href="#addr-AB78">AB78</a></td> <td><span class="label">.svc5_irq</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Get ADFS flags</span></td> </tr> <tr id="addr-AB7A"> <td class="addr"><a href="#addr-AB7A">AB7A</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Check ensuring + HD bits</span></td> </tr> <tr id="addr-AB7C"> <td class="addr"><a href="#addr-AB7C">AB7C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Both set?</span></td> </tr> <tr id="addr-AB7E"> <td class="addr"><a href="#addr-AB7E">AB7E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AB87" data-tip="&AB87">advance_write_page</a></span> <span class="comment">; No: not our interrupt</span></td> </tr> <tr id="addr-AB80"> <td class="addr"><a href="#addr-AB80">AB80</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8056" data-tip="&8056 – Read SCSI status with settling">scsi_get_status</a></span> <span class="comment">; Read SCSI status</span></td> </tr> <tr id="addr-AB83"> <td class="addr"><a href="#addr-AB83">AB83</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="242 &F2 %11110010">#&f2</span></span> <span class="comment">; Status = &F2 (completion)?</span></td> </tr> <tr id="addr-AB85"> <td class="addr"><a href="#addr-AB85">AB85</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AB8A" data-tip="&AB8A">write_complete</a></span> <span class="comment">; Yes: handle SCSI completion</span></td> </tr> <tr id="addr-AB87"> <td class="addr"><a href="#addr-AB87">AB87</a></td> <td><span class="label">.advance_write_page<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AB7E">← AB7E BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Not ours: A=5 (not claimed)</span></td> </tr> <tr id="addr-AB89"> <td class="addr"><a href="#addr-AB89">AB89</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-AB8A"> <td class="addr"><a href="#addr-AB8A">AB8A</a></td> <td><span class="label">.write_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AB85">← AB85 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Save Y</span></td> </tr> <tr id="addr-AB8B"> <td class="addr"><a href="#addr-AB8B">AB8B</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-AB8C"> <td class="addr"><a href="#addr-AB8C">AB8C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear SCSI IRQ</span></td> </tr> <tr id="addr-AB8E"> <td class="addr"><a href="#addr-AB8E">AB8E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FC43">fred_hard_drive_3</span></span> <span class="comment">; Write to SCSI IRQ enable</span></td> </tr> <tr id="addr-AB91"> <td class="addr"><a href="#addr-AB91">AB91</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Clear ensuring flag (bit 0)</span></td> </tr> <tr id="addr-AB93"> <td class="addr"><a href="#addr-AB93">AB93</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry</span></td> </tr> <tr id="addr-AB94"> <td class="addr"><a href="#addr-AB94">AB94</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Restore bit 0 cleared</span></td> </tr> <tr id="addr-AB96"> <td class="addr"><a href="#addr-AB96">AB96</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FC40">fred_hard_drive_0</span></span> <span class="comment">; Read SCSI status byte</span></td> </tr> <tr id="addr-AB99"> <td class="addr"><a href="#addr-AB99">AB99</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-830F" data-tip="&830F – Wait for SCSI REQ signal">scsi_wait_for_req</a></span> <span class="comment">; Wait for message phase</span></td> </tr> <tr id="addr-AB9C"> <td class="addr"><a href="#addr-AB9C">AB9C</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&FC40">fred_hard_drive_0</span></span> <span class="comment">; OR with final status byte</span></td> </tr> <tr id="addr-AB9F"> <td class="addr"><a href="#addr-AB9F">AB9F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1131">wksp_scsi_status</span></span> <span class="comment">; Store combined status</span></td> </tr> <tr id="addr-ABA2"> <td class="addr"><a href="#addr-ABA2">ABA2</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-9D63" data-tip="&9D63">set_result_error_code</a></span> <span class="comment">; Return to service dispatcher</span></td> </tr> <tr id="addr-ABA5"> <td class="addr"><a href="#addr-ABA5">ABA5</a></td> <td><span class="label">.ensure_channel_buffer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AAF3">← AAF3 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1131">wksp_scsi_status</span></span> <span class="comment">; Check for pending data lost error</span></td> </tr> <tr id="addr-ABA8"> <td class="addr"><a href="#addr-ABA8">ABA8</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-ABD7" data-tip="&ABD7">return_35</a></span> <span class="comment">; Zero: no error, return</span></td> </tr> <tr id="addr-ABAA"> <td class="addr"><a href="#addr-ABAA">ABAA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear pending error</span></td> </tr> <tr id="addr-ABAC"> <td class="addr"><a href="#addr-ABAC">ABAC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1131">wksp_scsi_status</span></span> <span class="comment">; Clear error status</span></td> </tr> <tr id="addr-ABAF"> <td class="addr"><a href="#addr-ABAF">ABAF</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&10D4">wksp_err_handle</span></span> <span class="comment">; Get file handle for error message</span></td> </tr> <tr id="addr-ABB2"> <td class="addr"><a href="#addr-ABB2">ABB2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8353" data-tip="&8353 – Generate error with suffix control in X">generate_error_suffix_x</a></span></td> </tr> <tr id="addr-ABB5"> <td class="addr"><a href="#addr-ABB5">ABB5</a></td> <td> <span class="directive">EQUB</span> <span data-tip="202 &CA %11001010">&CA</span> <span class="comment">; Error &CA: Data lost</span></td> </tr> <tr id="addr-ABB6"> <td class="addr"><a href="#addr-ABB6">ABB6</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Data lost, channel"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-ABC9"> <td class="addr"><a href="#addr-ABC9">ABC9</a></td> <td><span class="label">.calc_buffer_address<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AC05">← AC05 JSR</a><a href="#addr-AC89">← AC89 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TXA</span> <span class="comment">; Save X (channel index)</span></td> </tr> <tr id="addr-ABCA"> <td class="addr"><a href="#addr-ABCA">ABCA</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&10A1">wksp_ch_buf_sector_1</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-ABCD"> <td class="addr"><a href="#addr-ABCD">ABCD</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Divide by 4 for channel number</span></td> </tr> <tr id="addr-ABCE"> <td class="addr"><a href="#addr-ABCE">ABCE</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Second shift</span></td> </tr> <tr id="addr-ABCF"> <td class="addr"><a href="#addr-ABCF">ABCF</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="23 &17 %00010111 ETB">#&17</span></span> <span class="comment">; Add &17 for buffer page base</span></td> </tr> <tr id="addr-ABD1"> <td class="addr"><a href="#addr-ABD1">ABD1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BF">zp_buf_dest_hi</span></span> <span class="comment">; Store as buffer high byte</span></td> </tr> <tr id="addr-ABD3"> <td class="addr"><a href="#addr-ABD3">ABD3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Buffer low = 0 (page-aligned)</span></td> </tr> <tr id="addr-ABD5"> <td class="addr"><a href="#addr-ABD5">ABD5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span></span> <span class="comment">; Store buffer low</span></td> </tr> <tr id="addr-ABD7"> <td class="addr"><a href="#addr-ABD7">ABD7</a></td> <td><span class="label">.return_35<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ABA8">← ABA8 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-ABD8"> <td colspan="2"><div class="sub-header"> <h3>Find or allocate a buffer for a sector</h3> <div class="sub-desc"><p>Scan channel buffer table for a buffer matching the target sector. If not found, evict the oldest buffer for reuse.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>A</td><td>buffer mode (&40=read, &C0=write)</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>corrupted</td></tr> <tr><td>X</td><td>buffer table offset for slot</td></tr> <tr><td>Y</td><td>corrupted</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-ABD8">ABD8</a></td> <td><span class="label">.find_buffer_for_sector<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-ADAD">← ADAD JSR</a><a href="#addr-AFAF">← AFAF JSR</a><a href="#addr-B01F">← B01F JSR</a><a href="#addr-B10E">← B10E JSR</a><a href="#addr-B6EE">← B6EE JSR</a><a href="#addr-B811">← B811 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; X=&10: start of channel table</span></td> </tr> <tr id="addr-ABDA"> <td class="addr"><a href="#addr-ABDA">ABDA</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&1095">wksp_osgbpb_end_ptr</span></span> <span class="comment">; Store as initial best match</span></td> </tr> <tr id="addr-ABDD"> <td class="addr"><a href="#addr-ABDD">ABDD</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer A to Y (mode flag)</span></td> </tr> <tr id="addr-ABDE"> <td class="addr"><a href="#addr-ABDE">ABDE</a></td> <td><span class="label">.scan_channel_buffers<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC68">← AC68 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state entry</span></td> </tr> <tr id="addr-ABE1"> <td class="addr"><a href="#addr-ABE1">ABE1</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Check bit 0 (dirty flag)</span></td> </tr> <tr id="addr-ABE3"> <td class="addr"><a href="#addr-ABE3">ABE3</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-ABE8" data-tip="&ABE8">buffer_sector_match</a></span> <span class="comment">; Not dirty: skip</span></td> </tr> <tr id="addr-ABE5"> <td class="addr"><a href="#addr-ABE5">ABE5</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&1095">wksp_osgbpb_end_ptr</span></span> <span class="comment">; Update best dirty channel index</span></td> </tr> <tr id="addr-ABE8"> <td class="addr"><a href="#addr-ABE8">ABE8</a></td> <td><span class="label">.buffer_sector_match<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ABE3">← ABE3 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state</span></td> </tr> <tr id="addr-ABEB"> <td class="addr"><a href="#addr-ABEB">ABEB</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AC62" data-tip="&AC62 – Read a single sector via SCSI">read_single_hd_sector</a></span> <span class="comment">; Bit 7 clear: channel not active</span></td> </tr> <tr id="addr-ABED"> <td class="addr"><a href="#addr-ABED">ABED</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Get channel sector low</span></td> </tr> <tr id="addr-ABF0"> <td class="addr"><a href="#addr-ABF0">ABF0</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Compare with target sector low</span></td> </tr> <tr id="addr-ABF3"> <td class="addr"><a href="#addr-ABF3">ABF3</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AC62" data-tip="&AC62 – Read a single sector via SCSI">read_single_hd_sector</a></span> <span class="comment">; No match: try next channel</span></td> </tr> <tr id="addr-ABF5"> <td class="addr"><a href="#addr-ABF5">ABF5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Get channel sector mid</span></td> </tr> <tr id="addr-ABF8"> <td class="addr"><a href="#addr-ABF8">ABF8</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Compare with target sector mid</span></td> </tr> <tr id="addr-ABFB"> <td class="addr"><a href="#addr-ABFB">ABFB</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AC62" data-tip="&AC62 – Read a single sector via SCSI">read_single_hd_sector</a></span> <span class="comment">; No match: try next channel</span></td> </tr> <tr id="addr-ABFD"> <td class="addr"><a href="#addr-ABFD">ABFD</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Get channel drive+sector high</span></td> </tr> <tr id="addr-AC00"> <td class="addr"><a href="#addr-AC00">AC00</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Compare with target high</span></td> </tr> <tr id="addr-AC03"> <td class="addr"><a href="#addr-AC03">AC03</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AC62" data-tip="&AC62 – Read a single sector via SCSI">read_single_hd_sector</a></span> <span class="comment">; No match: try next channel</span></td> </tr> <tr id="addr-AC05"> <td class="addr"><a href="#addr-AC05">AC05</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABC9" data-tip="&ABC9">calc_buffer_address</a></span> <span class="comment">; Match: set up buffer address</span></td> </tr> <tr id="addr-AC08"> <td class="addr"><a href="#addr-AC08">AC08</a></td> <td><span class="label">.allocate_new_buffer_slot<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACD4">← ACD4 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer mode flag to A</span></td> </tr> <tr id="addr-AC09"> <td class="addr"><a href="#addr-AC09">AC09</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Shift right for direction bit</span></td> </tr> <tr id="addr-AC0A"> <td class="addr"><a href="#addr-AC0A">AC0A</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; Isolate bit 6 (read/write)</span></td> </tr> <tr id="addr-AC0C"> <td class="addr"><a href="#addr-AC0C">AC0C</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Merge with channel state</span></td> </tr> <tr id="addr-AC0F"> <td class="addr"><a href="#addr-AC0F">AC0F</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate state bits</span></td> </tr> <tr id="addr-AC10"> <td class="addr"><a href="#addr-AC10">AC10</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="224 &E0 %11100000">#&e0</span></span> <span class="comment">; Keep top 3 bits</span></td> </tr> <tr id="addr-AC12"> <td class="addr"><a href="#addr-AC12">AC12</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; OR in channel index</span></td> </tr> <tr id="addr-AC14"> <td class="addr"><a href="#addr-AC14">AC14</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save flags</span></td> </tr> <tr id="addr-AC15"> <td class="addr"><a href="#addr-AC15">AC15</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for rotate</span></td> </tr> <tr id="addr-AC16"> <td class="addr"><a href="#addr-AC16">AC16</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Shift left to final position</span></td> </tr> <tr id="addr-AC17"> <td class="addr"><a href="#addr-AC17">AC17</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Store updated channel state</span></td> </tr> <tr id="addr-AC1A"> <td class="addr"><a href="#addr-AC1A">AC1A</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-AC1B"> <td class="addr"><a href="#addr-AC1B">AC1B</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AC3B" data-tip="&AC3B">evict_oldest_buffer</a></span> <span class="comment">; C=0: read operation, skip write</span></td> </tr> <tr id="addr-AC1D"> <td class="addr"><a href="#addr-AC1D">AC1D</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: scan for empty ensure slot</span></td> </tr> <tr id="addr-AC1F"> <td class="addr"><a href="#addr-AC1F">AC1F</a></td> <td><span class="label">.find_free_slot_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC2F">← AC2F BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,y</span> <span class="comment">; Get ensure table entry</span></td> </tr> <tr id="addr-AC22"> <td class="addr"><a href="#addr-AC22">AC22</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AC2B" data-tip="&AC2B">use_free_slot</a></span> <span class="comment">; Non-zero: slot in use</span></td> </tr> <tr id="addr-AC24"> <td class="addr"><a href="#addr-AC24">AC24</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: mark slot as dirty</span></td> </tr> <tr id="addr-AC26"> <td class="addr"><a href="#addr-AC26">AC26</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,y</span> <span class="comment">; Store in ensure table</span></td> </tr> <tr id="addr-AC29"> <td class="addr"><a href="#addr-AC29">AC29</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AC5F" data-tip="&AC5F">load_sector_to_buffer</a></span></td> </tr> <tr id="addr-AC2B"> <td class="addr"><a href="#addr-AC2B">AC2B</a></td> <td><span class="label">.use_free_slot<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC22">← AC22 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEY</span> <span class="comment">; Step back 4 bytes</span></td> </tr> <tr id="addr-AC2C"> <td class="addr"><a href="#addr-AC2C">AC2C</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AC2D"> <td class="addr"><a href="#addr-AC2D">AC2D</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AC2E"> <td class="addr"><a href="#addr-AC2E">AC2E</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AC2F"> <td class="addr"><a href="#addr-AC2F">AC2F</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AC1F" data-tip="&AC1F">find_free_slot_loop</a></span> <span class="comment">; Loop for all ensure entries</span></td> </tr> <tr id="addr-AC31"> <td class="addr"><a href="#addr-AC31">AC31</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACF5" data-tip="&ACF5">convert_handle_to_offset</a></span> <span class="comment">; Flush oldest dirty buffer</span></td> </tr> <tr id="addr-AC34"> <td class="addr"><a href="#addr-AC34">AC34</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Clear bit 0 of channel state</span></td> </tr> <tr id="addr-AC37"> <td class="addr"><a href="#addr-AC37">AC37</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-AC38"> <td class="addr"><a href="#addr-AC38">AC38</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Set bit 0 (mark as dirty)</span></td> </tr> <tr id="addr-AC3B"> <td class="addr"><a href="#addr-AC3B">AC3B</a></td> <td><span class="label">.evict_oldest_buffer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC1B">← AC1B BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INX</span> <span class="comment">; Advance X past current entry</span></td> </tr> <tr id="addr-AC3C"> <td class="addr"><a href="#addr-AC3C">AC3C</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Continue advancing</span></td> </tr> <tr id="addr-AC3D"> <td class="addr"><a href="#addr-AC3D">AC3D</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Continue advancing</span></td> </tr> <tr id="addr-AC3E"> <td class="addr"><a href="#addr-AC3E">AC3E</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Continue advancing</span></td> </tr> <tr id="addr-AC3F"> <td class="addr"><a href="#addr-AC3F">AC3F</a></td> <td> <span class="opcode">CPX</span> <span class="operand"><span class="imm" data-tip="17 &11 %00010001 DC1">#&11</span></span> <span class="comment">; Past end of table (&11)?</span></td> </tr> <tr id="addr-AC41"> <td class="addr"><a href="#addr-AC41">AC41</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AC45" data-tip="&AC45">evict_check_dirty</a></span> <span class="comment">; No: continue</span></td> </tr> <tr id="addr-AC43"> <td class="addr"><a href="#addr-AC43">AC43</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Wrap to start: X=0</span></td> </tr> <tr id="addr-AC45"> <td class="addr"><a href="#addr-AC45">AC45</a></td> <td><span class="label">.evict_check_dirty<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC41">← AC41 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state at new position</span></td> </tr> <tr id="addr-AC48"> <td class="addr"><a href="#addr-AC48">AC48</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Shift right to check state</span></td> </tr> <tr id="addr-AC49"> <td class="addr"><a href="#addr-AC49">AC49</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AC5F" data-tip="&AC5F">load_sector_to_buffer</a></span> <span class="comment">; Empty slot: use it</span></td> </tr> <tr id="addr-AC4B"> <td class="addr"><a href="#addr-AC4B">AC4B</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AC5F" data-tip="&AC5F">load_sector_to_buffer</a></span> <span class="comment">; C=0: clean buffer, reuse it</span></td> </tr> <tr id="addr-AC4D"> <td class="addr"><a href="#addr-AC4D">AC4D</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for rotate back</span></td> </tr> <tr id="addr-AC4E"> <td class="addr"><a href="#addr-AC4E">AC4E</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Restore state bits</span></td> </tr> <tr id="addr-AC4F"> <td class="addr"><a href="#addr-AC4F">AC4F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Store updated state</span></td> </tr> <tr id="addr-AC52"> <td class="addr"><a href="#addr-AC52">AC52</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACF5" data-tip="&ACF5">convert_handle_to_offset</a></span> <span class="comment">; Flush this buffer to disc</span></td> </tr> <tr id="addr-AC55"> <td class="addr"><a href="#addr-AC55">AC55</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACF5" data-tip="&ACF5">convert_handle_to_offset</a></span> <span class="comment">; Flush again (ensure completion)</span></td> </tr> <tr id="addr-AC58"> <td class="addr"><a href="#addr-AC58">AC58</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Clear dirty bit</span></td> </tr> <tr id="addr-AC5B"> <td class="addr"><a href="#addr-AC5B">AC5B</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-AC5C"> <td class="addr"><a href="#addr-AC5C">AC5C</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Set dirty bit (will be written)</span></td> </tr> <tr id="addr-AC5F"> <td class="addr"><a href="#addr-AC5F">AC5F</a></td> <td><span class="label">.load_sector_to_buffer<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AC29">← AC29 BNE</a><a href="#addr-AC49">← AC49 BEQ</a><a href="#addr-AC4B">← AC4B BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-AAF0" data-tip="&AAF0">calc_channel_buffer_page</a></span> <span class="comment">; Jump to buffer fill</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AC62"> <td colspan="2"><div class="sub-header"> <h3>Read a single sector via SCSI</h3> <div class="sub-desc"><p>Issue a single-sector read command and transfer 256 bytes from the SCSI data register into the buffer.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AC62">AC62</a></td> <td><span class="label">.read_single_hd_sector<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-ABEB">← ABEB BPL</a><a href="#addr-ABF3">← ABF3 BNE</a><a href="#addr-ABFB">← ABFB BNE</a><a href="#addr-AC03">← AC03 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes to prev entry</span></td> </tr> <tr id="addr-AC63"> <td class="addr"><a href="#addr-AC63">AC63</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AC64"> <td class="addr"><a href="#addr-AC64">AC64</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AC65"> <td class="addr"><a href="#addr-AC65">AC65</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-AC66"> <td class="addr"><a href="#addr-AC66">AC66</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-AC6B" data-tip="&AC6B">wait_read_data_phase</a></span> <span class="comment">; Past start: no match found</span></td> </tr> <tr id="addr-AC68"> <td class="addr"><a href="#addr-AC68">AC68</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-ABDE" data-tip="&ABDE">scan_channel_buffers</a></span> <span class="comment">; Continue scanning from top</span></td> </tr> <tr id="addr-AC6B"> <td class="addr"><a href="#addr-AC6B">AC6B</a></td> <td><span class="label">.wait_read_data_phase<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC66">← AC66 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&1095">wksp_osgbpb_end_ptr</span></span> <span class="comment">; Get best dirty channel index</span></td> </tr> <tr id="addr-AC6E"> <td class="addr"><a href="#addr-AC6E">AC6E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Get target sector low</span></td> </tr> <tr id="addr-AC71"> <td class="addr"><a href="#addr-AC71">AC71</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Store as channel sector low</span></td> </tr> <tr id="addr-AC74"> <td class="addr"><a href="#addr-AC74">AC74</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D0">wksp_err_sector</span></span> <span class="comment">; Also store in error workspace</span></td> </tr> <tr id="addr-AC77"> <td class="addr"><a href="#addr-AC77">AC77</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Get target sector mid</span></td> </tr> <tr id="addr-AC7A"> <td class="addr"><a href="#addr-AC7A">AC7A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Store as channel sector mid</span></td> </tr> <tr id="addr-AC7D"> <td class="addr"><a href="#addr-AC7D">AC7D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D1">wksp_err_sector_mid</span></span> <span class="comment">; Store in error workspace</span></td> </tr> <tr id="addr-AC80"> <td class="addr"><a href="#addr-AC80">AC80</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Get target drive+sector high</span></td> </tr> <tr id="addr-AC83"> <td class="addr"><a href="#addr-AC83">AC83</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Store as channel drive+sector</span></td> </tr> <tr id="addr-AC86"> <td class="addr"><a href="#addr-AC86">AC86</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10D2">wksp_err_sector_hi</span></span> <span class="comment">; Store in error workspace</span></td> </tr> <tr id="addr-AC89"> <td class="addr"><a href="#addr-AC89">AC89</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABC9" data-tip="&ABC9">calc_buffer_address</a></span> <span class="comment">; Calculate buffer page for channel</span></td> </tr> <tr id="addr-AC8C"> <td class="addr"><a href="#addr-AC8C">AC8C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Get drive+sector high for read</span></td> </tr> <tr id="addr-AC8F"> <td class="addr"><a href="#addr-AC8F">AC8F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B51C" data-tip="&B51C – Set current drive from channel's drive">set_drive_from_channel</a></span> <span class="comment">; Set up disc read control block</span></td> </tr> <tr id="addr-AC92"> <td class="addr"><a href="#addr-AC92">AC92</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Save Y (buffer high)</span></td> </tr> <tr id="addr-AC94"> <td class="addr"><a href="#addr-AC94">AC94</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Save X (buffer low)</span></td> </tr> <tr id="addr-AC96"> <td class="addr"><a href="#addr-AC96">AC96</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8080" data-tip="&8080 – Set retry count for disc operation">command_set_retries</a></span></td> </tr> <tr id="addr-AC99"> <td class="addr"><a href="#addr-AC99">AC99</a></td> <td><span class="label">.read_scsi_to_buffer_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACAD">← ACAD BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Restore buffer pointer</span></td> </tr> <tr id="addr-AC9B"> <td class="addr"><a href="#addr-AC9B">AC9B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Check for hard drive</span></td> </tr> <tr id="addr-AC9D"> <td class="addr"><a href="#addr-AC9D">AC9D</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Bit 5: hard drive present?</span></td> </tr> <tr id="addr-AC9F"> <td class="addr"><a href="#addr-AC9F">AC9F</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-ACA6" data-tip="&ACA6">advance_read_page</a></span> <span class="comment">; No: use floppy</span></td> </tr> <tr id="addr-ACA1"> <td class="addr"><a href="#addr-ACA1">ACA1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Get drive from channel</span></td> </tr> <tr id="addr-ACA4"> <td class="addr"><a href="#addr-ACA4">ACA4</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-ACB2" data-tip="&ACB2">hd_bget_read_sector</a></span> <span class="comment">; Bit 7 clear: use SCSI</span></td> </tr> <tr id="addr-ACA6"> <td class="addr"><a href="#addr-ACA6">ACA6</a></td> <td><span class="label">.advance_read_page<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AC9F">← AC9F BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BA09" data-tip="&BA09">exec_floppy_read_bput_sector_ind</a></span> <span class="comment">; Floppy: read sector to buffer</span></td> </tr> <tr id="addr-ACA9"> <td class="addr"><a href="#addr-ACA9">ACA9</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-ACCB" data-tip="&ACCB">check_read_error</a></span> <span class="comment">; Success? Done</span></td> </tr> <tr id="addr-ACAB"> <td class="addr"><a href="#addr-ACAB">ACAB</a></td> <td><span class="label">.read_hd_256_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACC9">← ACC9 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&00CE">zp_retry_count</span></span> <span class="comment">; Decrement retry counter</span></td> </tr> <tr id="addr-ACAD"> <td class="addr"><a href="#addr-ACAD">ACAD</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AC99" data-tip="&AC99">read_scsi_to_buffer_loop</a></span> <span class="comment">; More retries: try again</span></td> </tr> <tr id="addr-ACAF"> <td class="addr"><a href="#addr-ACAF">ACAF</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-829A" data-tip="&829A – Generate a BRK error">generate_error</a></span></td> </tr> <tr id="addr-ACB2"> <td class="addr"><a href="#addr-ACB2">ACB2</a></td> <td><span class="label">.hd_bget_read_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACA4">← ACA4 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; SCSI: read command = 8</span></td> </tr> <tr id="addr-ACB4"> <td class="addr"><a href="#addr-ACB4">ACB4</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAC6" data-tip="&AAC6 – Hard drive single sector for BGET/BPUT">hd_command_bget_bput_sector</a></span></td> </tr> <tr id="addr-ACB7"> <td class="addr"><a href="#addr-ACB7">ACB7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-830F" data-tip="&830F – Wait for SCSI REQ signal">scsi_wait_for_req</a></span></td> </tr> <tr id="addr-ACBA"> <td class="addr"><a href="#addr-ACBA">ACBA</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-ACC6" data-tip="&ACC6">store_read_result</a></span> <span class="comment">; Status phase: read complete</span></td> </tr> <tr id="addr-ACBC"> <td class="addr"><a href="#addr-ACBC">ACBC</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: read data byte index</span></td> </tr> <tr id="addr-ACBE"> <td class="addr"><a href="#addr-ACBE">ACBE</a></td> <td><span class="label">.read_complete_check<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACC4">← ACC4 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FC40">fred_hard_drive_0</span></span> <span class="comment">; Read byte from SCSI data bus</span></td> </tr> <tr id="addr-ACC1"> <td class="addr"><a href="#addr-ACC1">ACC1</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Store in buffer</span></td> </tr> <tr id="addr-ACC3"> <td class="addr"><a href="#addr-ACC3">ACC3</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-ACC4"> <td class="addr"><a href="#addr-ACC4">ACC4</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-ACBE" data-tip="&ACBE">read_complete_check</a></span> <span class="comment">; Loop for 256 bytes</span></td> </tr> <tr id="addr-ACC6"> <td class="addr"><a href="#addr-ACC6">ACC6</a></td> <td><span class="label">.store_read_result<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACBA">← ACBA BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-818A" data-tip="&818A – Complete SCSI command and read status">command_done</a></span></td> </tr> <tr id="addr-ACC9"> <td class="addr"><a href="#addr-ACC9">ACC9</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-ACAB" data-tip="&ACAB">read_hd_256_complete</a></span> <span class="comment">; Error: retry</span></td> </tr> <tr id="addr-ACCB"> <td class="addr"><a href="#addr-ACCB">ACCB</a></td> <td><span class="label">.check_read_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACA9">← ACA9 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Restore buffer pointer X</span></td> </tr> <tr id="addr-ACCD"> <td class="addr"><a href="#addr-ACCD">ACCD</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Restore buffer pointer Y</span></td> </tr> <tr id="addr-ACCF"> <td class="addr"><a href="#addr-ACCF">ACCF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="129 &81 %10000001">#&81</span></span> <span class="comment">; A=&81: buffer valid + dirty</span></td> </tr> <tr id="addr-ACD1"> <td class="addr"><a href="#addr-ACD1">ACD1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Store as channel state</span></td> </tr> <tr id="addr-ACD4"> <td class="addr"><a href="#addr-ACD4">ACD4</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-AC08" data-tip="&AC08">allocate_new_buffer_slot</a></span> <span class="comment">; Jump to set up buffer access</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-ACD7"> <td colspan="2"><div class="sub-header"> <h3>Calculate buffer page from channel offset</h3> <div class="sub-desc"><p>Divide the channel offset by 4 and add the buffer base page (&17) to compute the buffer memory page.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-ACD7">ACD7</a></td> <td><span class="label">.calc_buffer_page_from_offset<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-8B33">← 8B33 JSR</a><a href="#addr-AAF0">← AAF0 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; X=&10: start of channel table</span></td> </tr> <tr id="addr-ACD9"> <td class="addr"><a href="#addr-ACD9">ACD9</a></td> <td><span class="label">.step_channel_offset_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACE4">← ACE4 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Get channel state</span></td> </tr> <tr id="addr-ACDC"> <td class="addr"><a href="#addr-ACDC">ACDC</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Bit 0: dirty flag</span></td> </tr> <tr id="addr-ACDE"> <td class="addr"><a href="#addr-ACDE">ACDE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD15" data-tip="&AD15">return_36</a></span> <span class="comment">; Not dirty: done scanning</span></td> </tr> <tr id="addr-ACE0"> <td class="addr"><a href="#addr-ACE0">ACE0</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes</span></td> </tr> <tr id="addr-ACE1"> <td class="addr"><a href="#addr-ACE1">ACE1</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-ACE2"> <td class="addr"><a href="#addr-ACE2">ACE2</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-ACE3"> <td class="addr"><a href="#addr-ACE3">ACE3</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-ACE4"> <td class="addr"><a href="#addr-ACE4">ACE4</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-ACD9" data-tip="&ACD9">step_channel_offset_loop</a></span> <span class="comment">; Loop for all entries</span></td> </tr> <tr id="addr-ACE6"> <td class="addr"><a href="#addr-ACE6">ACE6</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; No dirty buffers: workspace error</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-ACE9"> <td colspan="2"><div class="sub-header"> <h3>Step through ensure table entries</h3> <div class="sub-desc"><p>Step backward through the ensure table checking for entries associated with the current channel.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-ACE9">ACE9</a></td> <td><span class="label">.step_ensure_offset_loop<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AD05">← AD05 BCS</a><a href="#addr-AD0B">← AD0B BCC</a><a href="#addr-AD13">← AD13 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-ACEC"> <td class="addr"><a href="#addr-ACEC">ACEC</a></td> <td> <span class="directive">EQUB</span> <span data-tip="222 &DE %11011110">&DE</span> <span class="comment">; Error &DE: Channel</span></td> </tr> <tr id="addr-ACED"> <td class="addr"><a href="#addr-ACED">ACED</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Channel"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-ACF5"> <td class="addr"><a href="#addr-ACF5">ACF5</a></td> <td><span class="label">.convert_handle_to_offset<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AC31">← AC31 JSR</a><a href="#addr-AC52">← AC52 JSR</a><a href="#addr-AC55">← AC55 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Step back 4 bytes</span></td> </tr> <tr id="addr-ACF6"> <td class="addr"><a href="#addr-ACF6">ACF6</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-ACF7"> <td class="addr"><a href="#addr-ACF7">ACF7</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-ACF8"> <td class="addr"><a href="#addr-ACF8">ACF8</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue stepping</span></td> </tr> <tr id="addr-ACF9"> <td class="addr"><a href="#addr-ACF9">ACF9</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-ACFD" data-tip="&ACFD">return_37</a></span> <span class="comment">; Still in range: return</span></td> </tr> <tr id="addr-ACFB"> <td class="addr"><a href="#addr-ACFB">ACFB</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Wrap: X=&10 (back to end)</span></td> </tr> <tr id="addr-ACFD"> <td class="addr"><a href="#addr-ACFD">ACFD</a></td> <td><span class="label">.return_37<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACF9">← ACF9 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-ACFE"> <td colspan="2"><div class="sub-header"> <h3>Validate and set channel number from Y</h3> <div class="sub-desc"><p>Check that Y contains a valid file handle and set the channel offset workspace variable.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>Y</td><td>file handle (&30-&39)</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>channel flags from wksp_ch_flags</td></tr> <tr><td>X</td><td>channel offset</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-ACFE">ACFE</a></td> <td><span class="label">.check_set_channel_y<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-A99B">← A99B JSR</a><a href="#addr-AD3C">← AD3C JSR</a><a href="#addr-AD65">← AD65 JSR</a><a href="#addr-B092">← B092 JSR</a><a href="#addr-B3B3">← B3B3 JSR</a><a href="#addr-B5AC">← B5AC JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00C2">zp_save_y</span></span> <span class="comment">; Save file handle</span></td> </tr> <tr id="addr-AD00"> <td class="addr"><a href="#addr-AD00">AD00</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10D5">wksp_cur_channel</span></span> <span class="comment">; Store as current channel for errors</span></td> </tr> <tr id="addr-AD03"> <td class="addr"><a href="#addr-AD03">AD03</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="58 &3A %00111010 ':'">#&3a</span></span> <span class="comment">; Handle >= &3A?</span></td> </tr> <tr id="addr-AD05"> <td class="addr"><a href="#addr-AD05">AD05</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-ACE9" data-tip="&ACE9 – Step through ensure table entries">step_ensure_offset_loop</a></span> <span class="comment">; Yes, invalid handle</span></td> </tr> <tr id="addr-AD07"> <td class="addr"><a href="#addr-AD07">AD07</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer handle to A</span></td> </tr> <tr id="addr-AD08"> <td class="addr"><a href="#addr-AD08">AD08</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-AD09"> <td class="addr"><a href="#addr-AD09">AD09</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Subtract &30 to get channel index</span></td> </tr> <tr id="addr-AD0B"> <td class="addr"><a href="#addr-AD0B">AD0B</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-ACE9" data-tip="&ACE9 – Step through ensure table entries">step_ensure_offset_loop</a></span> <span class="comment">; Handle < &30? Invalid</span></td> </tr> <tr id="addr-AD0D"> <td class="addr"><a href="#addr-AD0D">AD0D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Store channel index offset</span></td> </tr> <tr id="addr-AD0F"> <td class="addr"><a href="#addr-AD0F">AD0F</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X for table lookup</span></td> </tr> <tr id="addr-AD10"> <td class="addr"><a href="#addr-AD10">AD10</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Read channel flags</span></td> </tr> <tr id="addr-AD13"> <td class="addr"><a href="#addr-AD13">AD13</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-ACE9" data-tip="&ACE9 – Step through ensure table entries">step_ensure_offset_loop</a></span> <span class="comment">; Zero = channel not open</span></td> </tr> <tr id="addr-AD15"> <td class="addr"><a href="#addr-AD15">AD15</a></td> <td><span class="label">.return_36<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACDE">← ACDE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AD16"> <td colspan="2"><div class="sub-header"> <h3>Compare file EXT to PTR</h3> <div class="sub-desc"><p>Compare the file extent (EXT) with the current pointer (PTR) for the channel in the workspace.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="3">On Exit</th><td>A</td><td>last compared EXT byte (C clear if at EOF)</td></tr> <tr><td>X</td><td>channel offset</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AD16">AD16</a></td> <td><span class="label">.compare_ext_to_ptr<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-AA9E">← AA9E JSR</a><a href="#addr-AD48">← AD48 JSR</a><a href="#addr-AD6F">← AD6F JSR</a><a href="#addr-B644">← B644 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AD18"> <td class="addr"><a href="#addr-AD18">AD18</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Compare EXT high byte</span></td> </tr> <tr id="addr-AD1B"> <td class="addr"><a href="#addr-AD1B">AD1B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; With PTR high byte</span></td> </tr> <tr id="addr-AD1E"> <td class="addr"><a href="#addr-AD1E">AD1E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD39" data-tip="&AD39">return_38</a></span> <span class="comment">; Different: not at EOF (C set)</span></td> </tr> <tr id="addr-AD20"> <td class="addr"><a href="#addr-AD20">AD20</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Compare EXT mid-high byte</span></td> </tr> <tr id="addr-AD23"> <td class="addr"><a href="#addr-AD23">AD23</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; With PTR mid-high byte</span></td> </tr> <tr id="addr-AD26"> <td class="addr"><a href="#addr-AD26">AD26</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD39" data-tip="&AD39">return_38</a></span> <span class="comment">; Different: not at EOF</span></td> </tr> <tr id="addr-AD28"> <td class="addr"><a href="#addr-AD28">AD28</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Compare EXT mid-low byte</span></td> </tr> <tr id="addr-AD2B"> <td class="addr"><a href="#addr-AD2B">AD2B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; With PTR mid-low byte</span></td> </tr> <tr id="addr-AD2E"> <td class="addr"><a href="#addr-AD2E">AD2E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD39" data-tip="&AD39">return_38</a></span> <span class="comment">; Different: not at EOF</span></td> </tr> <tr id="addr-AD30"> <td class="addr"><a href="#addr-AD30">AD30</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Compare EXT low byte</span></td> </tr> <tr id="addr-AD33"> <td class="addr"><a href="#addr-AD33">AD33</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; With PTR low byte</span></td> </tr> <tr id="addr-AD36"> <td class="addr"><a href="#addr-AD36">AD36</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD39" data-tip="&AD39">return_38</a></span> <span class="comment">; Different: not at EOF</span></td> </tr> <tr id="addr-AD38"> <td class="addr"><a href="#addr-AD38">AD38</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; All equal: C=0, at EOF</span></td> </tr> <tr id="addr-AD39"> <td class="addr"><a href="#addr-AD39">AD39</a></td> <td><span class="label">.return_38<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-AD1E">← AD1E BNE</a><a href="#addr-AD26">← AD26 BNE</a><a href="#addr-AD2E">← AD2E BNE</a><a href="#addr-AD36">← AD36 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (EXT == PTR: C clear)</span></td> </tr> <tr id="addr-AD3A"> <td class="addr"><a href="#addr-AD3A">AD3A</a></td> <td><span class="label">.check_eof_for_handle</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Get file handle from (&B4)</span></td> </tr> <tr id="addr-AD3C"> <td class="addr"><a href="#addr-AD3C">AD3C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACFE" data-tip="&ACFE – Validate and set channel number from Y">check_set_channel_y</a></span></td> </tr> <tr id="addr-AD3F"> <td class="addr"><a href="#addr-AD3F">AD3F</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate flags bit 0 into carry</span></td> </tr> <tr id="addr-AD40"> <td class="addr"><a href="#addr-AD40">AD40</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AD4B" data-tip="&AD4B">return_eof_status</a></span> <span class="comment">; Carry set: skip flush</span></td> </tr> <tr id="addr-AD42"> <td class="addr"><a href="#addr-AD42">AD42</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Ensure workspace is valid</span></td> </tr> <tr id="addr-AD45"> <td class="addr"><a href="#addr-AD45">AD45</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B18C" data-tip="&B18C – Synchronise EXT to PTR if at EOF">sync_ext_to_ptr</a></span> <span class="comment">; Flush channel buffer if dirty</span></td> </tr> <tr id="addr-AD48"> <td class="addr"><a href="#addr-AD48">AD48</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AD16" data-tip="&AD16 – Compare file EXT to PTR">compare_ext_to_ptr</a></span></td> </tr> <tr id="addr-AD4B"> <td class="addr"><a href="#addr-AD4B">AD4B</a></td> <td><span class="label">.return_eof_status<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AD40">← AD40 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: PTR == EXT result</span></td> </tr> <tr id="addr-AD4D"> <td class="addr"><a href="#addr-AD4D">AD4D</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AD50" data-tip="&AD50">return_eof_result</a></span> <span class="comment">; Carry set: PTR == EXT</span></td> </tr> <tr id="addr-AD4F"> <td class="addr"><a href="#addr-AD4F">AD4F</a></td> <td> <span class="opcode">DEX</span></td> </tr> <tr id="addr-AD50"> <td class="addr"><a href="#addr-AD50">AD50</a></td> <td><span class="label">.return_eof_result<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AD4D">← AD4D BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Restore Y from (&B5)</span></td> </tr> <tr id="addr-AD52"> <td class="addr"><a href="#addr-AD52">AD52</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AD53"> <td colspan="2"><div class="sub-header"> <h3>Raise EOF error</h3> <div class="sub-desc"><p>Clear EOF and buffer flags then raise error &DF: EOF.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AD53">AD53</a></td> <td><span class="label">.eof_error<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AD6D">← AD6D BNE</a><a href="#addr-AD74">← AD74 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Clear EOF and buffer dirty flags</span></td> </tr> <tr id="addr-AD56"> <td class="addr"><a href="#addr-AD56">AD56</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="200 &C8 %11001000">#&c8</span></span> <span class="comment">; Keep bits 7,6,3 (writeable,open)</span></td> </tr> <tr id="addr-AD58"> <td class="addr"><a href="#addr-AD58">AD58</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-AD5B"> <td class="addr"><a href="#addr-AD5B">AD5B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-AD5E"> <td class="addr"><a href="#addr-AD5E">AD5E</a></td> <td> <span class="directive">EQUB</span> <span data-tip="223 &DF %11011111">&DF</span> <span class="comment">; Error &DF: EOF</span></td> </tr> <tr id="addr-AD5F"> <td class="addr"><a href="#addr-AD5F">AD5F</a></td> <td> <span class="directive">EQUS</span> <span class="string">"EOF"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AD63"> <td colspan="2"><div class="sub-header"> <h3>OSBGET handler</h3> <div class="sub-desc"><p>Handle OSBGET calls to read a single byte from an open file.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AD63">AD63</a></td> <td><span class="label">.osbget_handler</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Save X register</span></td> </tr> <tr id="addr-AD65"> <td class="addr"><a href="#addr-AD65">AD65</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACFE" data-tip="&ACFE – Validate and set channel number from Y">check_set_channel_y</a></span> <span class="comment">; Validate file handle in Y</span></td> </tr> <tr id="addr-AD68"> <td class="addr"><a href="#addr-AD68">AD68</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate channel flags bit 0 to C</span></td> </tr> <tr id="addr-AD69"> <td class="addr"><a href="#addr-AD69">AD69</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AD8D" data-tip="&AD8D – Calculate sector address for BGET">calc_bget_sector_addr</a></span> <span class="comment">; Bit 0 set: file is readable</span></td> </tr> <tr id="addr-AD6B"> <td class="addr"><a href="#addr-AD6B">AD6B</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Check bit 2 (at EOF flag)</span></td> </tr> <tr id="addr-AD6D"> <td class="addr"><a href="#addr-AD6D">AD6D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD53" data-tip="&AD53 – Raise EOF error">eof_error</a></span> <span class="comment">; At EOF: raise EOF error</span></td> </tr> <tr id="addr-AD6F"> <td class="addr"><a href="#addr-AD6F">AD6F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AD16" data-tip="&AD16 – Compare file EXT to PTR">compare_ext_to_ptr</a></span> <span class="comment">; Compare EXT with PTR</span></td> </tr> <tr id="addr-AD72"> <td class="addr"><a href="#addr-AD72">AD72</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AD8D" data-tip="&AD8D – Calculate sector address for BGET">calc_bget_sector_addr</a></span> <span class="comment">; EXT != PTR: not at EOF, read byte</span></td> </tr> <tr id="addr-AD74"> <td class="addr"><a href="#addr-AD74">AD74</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AD53" data-tip="&AD53 – Raise EOF error">eof_error</a></span> <span class="comment">; EXT == PTR and EOF: raise error</span></td> </tr> <tr id="addr-AD76"> <td class="addr"><a href="#addr-AD76">AD76</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save registers for restore</span></td> </tr> <tr id="addr-AD79"> <td class="addr"><a href="#addr-AD79">AD79</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AD7B"> <td class="addr"><a href="#addr-AD7B">AD7B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-AD7E"> <td class="addr"><a href="#addr-AD7E">AD7E</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; Keep open+writable bits only</span></td> </tr> <tr id="addr-AD80"> <td class="addr"><a href="#addr-AD80">AD80</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; Set EOF-read flag (bit 3)</span></td> </tr> <tr id="addr-AD82"> <td class="addr"><a href="#addr-AD82">AD82</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Store updated channel flags</span></td> </tr> <tr id="addr-AD85"> <td class="addr"><a href="#addr-AD85">AD85</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C2">zp_save_y</span></span> <span class="comment">; Restore Y</span></td> </tr> <tr id="addr-AD87"> <td class="addr"><a href="#addr-AD87">AD87</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-AD89"> <td class="addr"><a href="#addr-AD89">AD89</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry: C=1 means EOF</span></td> </tr> <tr id="addr-AD8A"> <td class="addr"><a href="#addr-AD8A">AD8A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="254 &FE %11111110">#&fe</span></span> <span class="comment">; A=&FE: EOF return value</span></td> </tr> <tr id="addr-AD8C"> <td class="addr"><a href="#addr-AD8C">AD8C</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (EOF)</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AD8D"> <td colspan="2"><div class="sub-header"> <h3>Calculate sector address for BGET</h3> <div class="sub-desc"><p>Compute disc sector from channel base + PTR, load the sector into the buffer, and set up the byte offset.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AD8D">AD8D</a></td> <td><span class="label">.calc_bget_sector_addr<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AD69">← AD69 BCS</a><a href="#addr-AD72">← AD72 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index for buffer calc</span></td> </tr> <tr id="addr-AD8F"> <td class="addr"><a href="#addr-AD8F">AD8F</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for address calculation</span></td> </tr> <tr id="addr-AD90"> <td class="addr"><a href="#addr-AD90">AD90</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Get channel buffer offset low</span></td> </tr> <tr id="addr-AD93"> <td class="addr"><a href="#addr-AD93">AD93</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Add PTR mid-low for buffer addr</span></td> </tr> <tr id="addr-AD96"> <td class="addr"><a href="#addr-AD96">AD96</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Store sector address low</span></td> </tr> <tr id="addr-AD99"> <td class="addr"><a href="#addr-AD99">AD99</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Get channel buffer offset mid</span></td> </tr> <tr id="addr-AD9C"> <td class="addr"><a href="#addr-AD9C">AD9C</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Add PTR mid-high</span></td> </tr> <tr id="addr-AD9F"> <td class="addr"><a href="#addr-AD9F">AD9F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Store sector address mid</span></td> </tr> <tr id="addr-ADA2"> <td class="addr"><a href="#addr-ADA2">ADA2</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel buffer base page</span></td> </tr> <tr id="addr-ADA5"> <td class="addr"><a href="#addr-ADA5">ADA5</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Add PTR high</span></td> </tr> <tr id="addr-ADA8"> <td class="addr"><a href="#addr-ADA8">ADA8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Store sector address high</span></td> </tr> <tr id="addr-ADAB"> <td class="addr"><a href="#addr-ADAB">ADAB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; A=&40: read buffer mode</span></td> </tr> <tr id="addr-ADAD"> <td class="addr"><a href="#addr-ADAD">ADAD</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABD8" data-tip="&ABD8 – Find or allocate a buffer for a sector">find_buffer_for_sector</a></span> <span class="comment">; Load sector into channel buffer</span></td> </tr> <tr id="addr-ADB0"> <td class="addr"><a href="#addr-ADB0">ADB0</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-ADB2"> <td class="addr"><a href="#addr-ADB2">ADB2</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Get PTR low byte as buffer offset</span></td> </tr> <tr id="addr-ADB5"> <td class="addr"><a href="#addr-ADB5">ADB5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear modification flag</span></td> </tr> <tr id="addr-ADB7"> <td class="addr"><a href="#addr-ADB7">ADB7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10CF">wksp_bput_modified</span></span> <span class="comment">; Store zero mod flag</span></td> </tr> <tr id="addr-ADBA"> <td class="addr"><a href="#addr-ADBA">ADBA</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B123" data-tip="&B123 – Increment PTR after byte write">increment_ptr_after_write</a></span> <span class="comment">; Advance PTR and update flags</span></td> </tr> <tr id="addr-ADBD"> <td class="addr"><a href="#addr-ADBD">ADBD</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Read byte from buffer at PTR offset</span></td> </tr> <tr id="addr-ADBF"> <td class="addr"><a href="#addr-ADBF">ADBF</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C2">zp_save_y</span></span> <span class="comment">; Restore Y</span></td> </tr> <tr id="addr-ADC1"> <td class="addr"><a href="#addr-ADC1">ADC1</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-ADC3"> <td class="addr"><a href="#addr-ADC3">ADC3</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry: C=0 means success</span></td> </tr> <tr id="addr-ADC4"> <td class="addr"><a href="#addr-ADC4">ADC4</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (byte in A)</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-ADC5"> <td colspan="2"><div class="sub-header"> <h3>Switch to channel's drive for I/O</h3> <div class="sub-desc"><p>Save CSD sector and current drive, then switch to the drive associated with the current channel.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-ADC5">ADC5</a></td> <td><span class="label">.switch_to_channel_drive<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AED7">← AED7 JSR</a><a href="#addr-B3F1">← B3F1 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: save 3 bytes of CSD sector</span></td> </tr> <tr id="addr-ADC7"> <td class="addr"><a href="#addr-ADC7">ADC7</a></td> <td><span class="label">.save_csd_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ADCE">← ADCE BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span>,y</span> <span class="comment">; Get CSD sector byte</span></td> </tr> <tr id="addr-ADCA"> <td class="addr"><a href="#addr-ADCA">ADCA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1030">wksp_temp_sector</span>,y</span> <span class="comment">; Store in temp workspace</span></td> </tr> <tr id="addr-ADCD"> <td class="addr"><a href="#addr-ADCD">ADCD</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-ADCE"> <td class="addr"><a href="#addr-ADCE">ADCE</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-ADC7" data-tip="&ADC7">save_csd_sector_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-ADD0"> <td class="addr"><a href="#addr-ADD0">ADD0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Save current drive</span></td> </tr> <tr id="addr-ADD3"> <td class="addr"><a href="#addr-ADD3">ADD3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1033">wksp_last_access_drive</span></span> <span class="comment">; Store as last access drive</span></td> </tr> <tr id="addr-ADD6"> <td class="addr"><a href="#addr-ADD6">ADD6</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-ADD8"> <td class="addr"><a href="#addr-ADD8">ADD8</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel's drive number</span></td> </tr> <tr id="addr-ADDB"> <td class="addr"><a href="#addr-ADDB">ADDB</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="224 &E0 %11100000">#&e0</span></span> <span class="comment">; Isolate drive bits (top 3)</span></td> </tr> <tr id="addr-ADDD"> <td class="addr"><a href="#addr-ADDD">ADDD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Save as current working drive</span></td> </tr> <tr id="addr-ADE0"> <td class="addr"><a href="#addr-ADE0">ADE0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11E8">wksp_ch_dir_sec_ml</span>,x</span> <span class="comment">; Get channel's sector low</span></td> </tr> <tr id="addr-ADE3"> <td class="addr"><a href="#addr-ADE3">ADE3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span></span> <span class="comment">; Store in CSD sector low</span></td> </tr> <tr id="addr-ADE6"> <td class="addr"><a href="#addr-ADE6">ADE6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11DE">wksp_ch_dir_sec_mh</span>,x</span> <span class="comment">; Get channel's sector mid</span></td> </tr> <tr id="addr-ADE9"> <td class="addr"><a href="#addr-ADE9">ADE9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102D">wksp_csd_drive_sector_mid</span></span> <span class="comment">; Store in workspace mid</span></td> </tr> <tr id="addr-ADEC"> <td class="addr"><a href="#addr-ADEC">ADEC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11D4">wksp_ch_dir_sec_h</span>,x</span> <span class="comment">; Get channel's sector high</span></td> </tr> <tr id="addr-ADEF"> <td class="addr"><a href="#addr-ADEF">ADEF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102E">wksp_alt_sector_hi</span></span> <span class="comment">; Store in workspace high</span></td> </tr> <tr id="addr-ADF2"> <td class="addr"><a href="#addr-ADF2">ADF2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-ADF5"> <td class="addr"><a href="#addr-ADF5">ADF5</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: restore CSD sector</span></td> </tr> <tr id="addr-ADF7"> <td class="addr"><a href="#addr-ADF7">ADF7</a></td> <td><span class="label">.restore_csd_after_switch_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ADFE">← ADFE BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1030">wksp_temp_sector</span>,y</span> <span class="comment">; Get saved CSD sector byte</span></td> </tr> <tr id="addr-ADFA"> <td class="addr"><a href="#addr-ADFA">ADFA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,y</span> <span class="comment">; Restore to CSD workspace</span></td> </tr> <tr id="addr-ADFD"> <td class="addr"><a href="#addr-ADFD">ADFD</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-ADFE"> <td class="addr"><a href="#addr-ADFE">ADFE</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-ADF7" data-tip="&ADF7">restore_csd_after_switch_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-AE00"> <td class="addr"><a href="#addr-AE00">AE00</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1033">wksp_last_access_drive</span></span> <span class="comment">; Get last access drive</span></td> </tr> <tr id="addr-AE03"> <td class="addr"><a href="#addr-AE03">AE03</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Set as saved drive for restore</span></td> </tr> <tr id="addr-AE06"> <td class="addr"><a href="#addr-AE06">AE06</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B48E" data-tip="&B48E">read_clock_then_verify_disc_id</a></span> <span class="comment">; Ensure files on drive are closed</span></td> </tr> <tr id="addr-AE09"> <td class="addr"><a href="#addr-AE09">AE09</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AE0B"> <td class="addr"><a href="#addr-AE0B">AE0B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Get channel's allocation low</span></td> </tr> <tr id="addr-AE0E"> <td class="addr"><a href="#addr-AE0E">AE0E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Store in object sector low</span></td> </tr> <tr id="addr-AE11"> <td class="addr"><a href="#addr-AE11">AE11</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Get allocation mid</span></td> </tr> <tr id="addr-AE14"> <td class="addr"><a href="#addr-AE14">AE14</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-AE17"> <td class="addr"><a href="#addr-AE17">AE17</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get allocation high + drive</span></td> </tr> <tr id="addr-AE1A"> <td class="addr"><a href="#addr-AE1A">AE1A</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="31 &1F %00011111 US">#&1f</span></span> <span class="comment">; Mask to sector bits only</span></td> </tr> <tr id="addr-AE1C"> <td class="addr"><a href="#addr-AE1C">AE1C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Store sector high</span></td> </tr> <tr id="addr-AE1F"> <td class="addr"><a href="#addr-AE1F">AE1F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Set (&B8) to dir entry at &1205</span></td> </tr> <tr id="addr-AE21"> <td class="addr"><a href="#addr-AE21">AE21</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store low byte</span></td> </tr> <tr id="addr-AE23"> <td class="addr"><a href="#addr-AE23">AE23</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Page &12</span></td> </tr> <tr id="addr-AE25"> <td class="addr"><a href="#addr-AE25">AE25</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store high byte</span></td> </tr> <tr id="addr-AE27"> <td class="addr"><a href="#addr-AE27">AE27</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AE29"> <td class="addr"><a href="#addr-AE29">AE29</a></td> <td><span class="label">.search_dir_for_channel<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AE53">← AE53 BCC</a><a href="#addr-AE57">← AE57 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check first dir entry byte</span></td> </tr> <tr id="addr-AE2B"> <td class="addr"><a href="#addr-AE2B">AE2B</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Get first byte</span></td> </tr> <tr id="addr-AE2D"> <td class="addr"><a href="#addr-AE2D">AE2D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AE35" data-tip="&AE35">compare_entry_sequence</a></span> <span class="comment">; Non-zero: valid entry</span></td> </tr> <tr id="addr-AE2F"> <td class="addr"><a href="#addr-AE2F">AE2F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Zero: channel invalid, clear flags</span></td> </tr> <tr id="addr-AE32"> <td class="addr"><a href="#addr-AE32">AE32</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-A738" data-tip="&A738">bad_checksum_error</a></span> <span class="comment">; Bad checksum error</span></td> </tr> <tr id="addr-AE35"> <td class="addr"><a href="#addr-AE35">AE35</a></td> <td><span class="label">.compare_entry_sequence<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AE2D">← AE2D BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="25 &19 %00011001 EM">#&19</span></span> <span class="comment">; Y=&19: check entry sequence number</span></td> </tr> <tr id="addr-AE37"> <td class="addr"><a href="#addr-AE37">AE37</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Get sequence number from entry</span></td> </tr> <tr id="addr-AE39"> <td class="addr"><a href="#addr-AE39">AE39</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&11F2">wksp_ch_seq_num</span>,x</span> <span class="comment">; Compare with channel's saved seq</span></td> </tr> <tr id="addr-AE3C"> <td class="addr"><a href="#addr-AE3C">AE3C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AE4C" data-tip="&AE4C – Advance directory scan pointer">advance_to_next_dir_entry</a></span> <span class="comment">; Mismatch: different entry</span></td> </tr> <tr id="addr-AE3E"> <td class="addr"><a href="#addr-AE3E">AE3E</a></td> <td> <span class="opcode">DEY</span></td> </tr> <tr id="addr-AE3F"> <td class="addr"><a href="#addr-AE3F">AE3F</a></td> <td><span class="label">.compare_entry_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AE49">← AE49 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Check next entry field</span></td> </tr> <tr id="addr-AE41"> <td class="addr"><a href="#addr-AE41">AE41</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&101E">wksp_disc_op_sector_count</span>,y</span> <span class="comment">; Compare sector field with channel</span></td> </tr> <tr id="addr-AE44"> <td class="addr"><a href="#addr-AE44">AE44</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AE4C" data-tip="&AE4C – Advance directory scan pointer">advance_to_next_dir_entry</a></span> <span class="comment">; Mismatch: try next entry</span></td> </tr> <tr id="addr-AE46"> <td class="addr"><a href="#addr-AE46">AE46</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte (decreasing Y)</span></td> </tr> <tr id="addr-AE47"> <td class="addr"><a href="#addr-AE47">AE47</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="22 &16 %00010110 SYN">#&16</span></span> <span class="comment">; Past start of sector field (&16)?</span></td> </tr> <tr id="addr-AE49"> <td class="addr"><a href="#addr-AE49">AE49</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AE3F" data-tip="&AE3F">compare_entry_sector_loop</a></span> <span class="comment">; Still in range: continue comparing</span></td> </tr> <tr id="addr-AE4B"> <td class="addr"><a href="#addr-AE4B">AE4B</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; All fields match: return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AE4C"> <td colspan="2"><div class="sub-header"> <h3>Advance directory scan pointer</h3> <div class="sub-desc"><p>Add 26 bytes to the directory entry pointer to move to the next entry, handling page crossing.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AE4C">AE4C</a></td> <td><span class="label">.advance_to_next_dir_entry<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AE3C">← AE3C BNE</a><a href="#addr-AE44">← AE44 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Advance to next dir entry (+&1A)</span></td> </tr> <tr id="addr-AE4E"> <td class="addr"><a href="#addr-AE4E">AE4E</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry</span></td> </tr> <tr id="addr-AE4F"> <td class="addr"><a href="#addr-AE4F">AE4F</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="26 &1A %00011010 SUB">#&1a</span></span> <span class="comment">; Add 26 bytes per entry</span></td> </tr> <tr id="addr-AE51"> <td class="addr"><a href="#addr-AE51">AE51</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store updated pointer</span></td> </tr> <tr id="addr-AE53"> <td class="addr"><a href="#addr-AE53">AE53</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AE29" data-tip="&AE29">search_dir_for_channel</a></span> <span class="comment">; No page crossing: continue search</span></td> </tr> <tr id="addr-AE55"> <td class="addr"><a href="#addr-AE55">AE55</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Increment page</span></td> </tr> <tr id="addr-AE57"> <td class="addr"><a href="#addr-AE57">AE57</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AE29" data-tip="&AE29">search_dir_for_channel</a></span></td> </tr> <tr id="addr-AE59"> <td class="addr"><a href="#addr-AE59">AE59</a></td> <td><span class="label">.check_ptr_within_allocation<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-A9E5">← A9E5 JSR</a><a href="#addr-AA83">← AA83 JSR</a><a href="#addr-B0EB">← B0EB JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear allocation flag</span></td> </tr> <tr id="addr-AE5B"> <td class="addr"><a href="#addr-AE5B">AE5B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Clear extension flag</span></td> </tr> <tr id="addr-AE5E"> <td class="addr"><a href="#addr-AE5E">AE5E</a></td> <td><span class="label">.extend_file_if_needed<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5FF">← B5FF JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Get saved drive</span></td> </tr> <tr id="addr-AE61"> <td class="addr"><a href="#addr-AE61">AE61</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10BF">wksp_saved_drive_2</span></span> <span class="comment">; Store for restore later</span></td> </tr> <tr id="addr-AE64"> <td class="addr"><a href="#addr-AE64">AE64</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: save 3 bytes of CSD</span></td> </tr> <tr id="addr-AE66"> <td class="addr"><a href="#addr-AE66">AE66</a></td> <td><span class="label">.save_csd_for_extend_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AE6D">← AE6D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,x</span> <span class="comment">; Get CSD sector byte</span></td> </tr> <tr id="addr-AE69"> <td class="addr"><a href="#addr-AE69">AE69</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10BC">wksp_osgbpb_wksp_bc</span>,x</span> <span class="comment">; Store in temp workspace</span></td> </tr> <tr id="addr-AE6C"> <td class="addr"><a href="#addr-AE6C">AE6C</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-AE6D"> <td class="addr"><a href="#addr-AE6D">AE6D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AE66" data-tip="&AE66">save_csd_for_extend_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-AE6F"> <td class="addr"><a href="#addr-AE6F">AE6F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A=&FF: mark workspace as modified</span></td> </tr> <tr id="addr-AE71"> <td class="addr"><a href="#addr-AE71">AE71</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102E">wksp_alt_sector_hi</span></span> <span class="comment">; Clear alt workspace pointer</span></td> </tr> <tr id="addr-AE74"> <td class="addr"><a href="#addr-AE74">AE74</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Clear saved drive</span></td> </tr> <tr id="addr-AE77"> <td class="addr"><a href="#addr-AE77">AE77</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AE79"> <td class="addr"><a href="#addr-AE79">AE79</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; Compare allocation with new PTR</span></td> </tr> <tr id="addr-AE7C"> <td class="addr"><a href="#addr-AE7C">AE7C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; High byte matches?</span></td> </tr> <tr id="addr-AE7F"> <td class="addr"><a href="#addr-AE7F">AE7F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AE97" data-tip="&AE97">check_alloc_vs_ptr</a></span> <span class="comment">; No: need to extend</span></td> </tr> <tr id="addr-AE81"> <td class="addr"><a href="#addr-AE81">AE81</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; Compare mid-high</span></td> </tr> <tr id="addr-AE84"> <td class="addr"><a href="#addr-AE84">AE84</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-AE87"> <td class="addr"><a href="#addr-AE87">AE87</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AE97" data-tip="&AE97">check_alloc_vs_ptr</a></span> <span class="comment">; No: need to extend</span></td> </tr> <tr id="addr-AE89"> <td class="addr"><a href="#addr-AE89">AE89</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; Compare mid-low</span></td> </tr> <tr id="addr-AE8C"> <td class="addr"><a href="#addr-AE8C">AE8C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-AE8F"> <td class="addr"><a href="#addr-AE8F">AE8F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AE97" data-tip="&AE97">check_alloc_vs_ptr</a></span> <span class="comment">; No: need to extend</span></td> </tr> <tr id="addr-AE91"> <td class="addr"><a href="#addr-AE91">AE91</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11A2">wksp_ch_alloc_l</span>,x</span> <span class="comment">; Compare low byte</span></td> </tr> <tr id="addr-AE94"> <td class="addr"><a href="#addr-AE94">AE94</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-AE97"> <td class="addr"><a href="#addr-AE97">AE97</a></td> <td><span class="label">.check_alloc_vs_ptr<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AE7F">← AE7F BNE</a><a href="#addr-AE87">← AE87 BNE</a><a href="#addr-AE8F">← AE8F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AEC1" data-tip="&AEC1">extend_file_allocation</a></span> <span class="comment">; Alloc < PTR: need to extend</span></td> </tr> <tr id="addr-AE99"> <td class="addr"><a href="#addr-AE99">AE99</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Compare EXT with new PTR</span></td> </tr> <tr id="addr-AE9C"> <td class="addr"><a href="#addr-AE9C">AE9C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; High byte matches?</span></td> </tr> <tr id="addr-AE9F"> <td class="addr"><a href="#addr-AE9F">AE9F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AEBC" data-tip="&AEBC – Handle PTR exceeding EXT">update_ext_to_ptr</a></span> <span class="comment">; No: EXT needs update</span></td> </tr> <tr id="addr-AEA1"> <td class="addr"><a href="#addr-AEA1">AEA1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Compare mid-high</span></td> </tr> <tr id="addr-AEA4"> <td class="addr"><a href="#addr-AEA4">AEA4</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-AEA7"> <td class="addr"><a href="#addr-AEA7">AEA7</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AEBC" data-tip="&AEBC – Handle PTR exceeding EXT">update_ext_to_ptr</a></span> <span class="comment">; No: EXT needs update</span></td> </tr> <tr id="addr-AEA9"> <td class="addr"><a href="#addr-AEA9">AEA9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Compare mid-low</span></td> </tr> <tr id="addr-AEAC"> <td class="addr"><a href="#addr-AEAC">AEAC</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-AEAF"> <td class="addr"><a href="#addr-AEAF">AEAF</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AEBC" data-tip="&AEBC – Handle PTR exceeding EXT">update_ext_to_ptr</a></span> <span class="comment">; No: EXT needs update</span></td> </tr> <tr id="addr-AEB1"> <td class="addr"><a href="#addr-AEB1">AEB1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Compare low byte</span></td> </tr> <tr id="addr-AEB4"> <td class="addr"><a href="#addr-AEB4">AEB4</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-AEB7"> <td class="addr"><a href="#addr-AEB7">AEB7</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AEBC" data-tip="&AEBC – Handle PTR exceeding EXT">update_ext_to_ptr</a></span> <span class="comment">; No: EXT needs update</span></td> </tr> <tr id="addr-AEB9"> <td class="addr"><a href="#addr-AEB9">AEB9</a></td> <td><span class="label">.handle_eof_write<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AEBC">← AEBC BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B07D" data-tip="&B07D">restore_drive_after_extend</a></span> <span class="comment">; PTR == EXT: handle EOF write</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-AEBC"> <td colspan="2"><div class="sub-header"> <h3>Handle PTR exceeding EXT</h3> <div class="sub-desc"><p>If PTR has exceeded the file allocation, begin file extension. Otherwise jump to EOF write handler.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-AEBC">AEBC</a></td> <td><span class="label">.update_ext_to_ptr<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-AE9F">← AE9F BNE</a><a href="#addr-AEA7">← AEA7 BNE</a><a href="#addr-AEAF">← AEAF BNE</a><a href="#addr-AEB7">← AEB7 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-AEB9" data-tip="&AEB9">handle_eof_write</a></span> <span class="comment">; EXT > PTR: still within file</span></td> </tr> <tr id="addr-AEBE"> <td class="addr"><a href="#addr-AEBE">AEBE</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-AF87" data-tip="&AF87">skip_zero_fill</a></span> <span class="comment">; PTR > alloc: need to extend file</span></td> </tr> <tr id="addr-AEC1"> <td class="addr"><a href="#addr-AEC1">AEC1</a></td> <td><span class="label">.extend_file_allocation<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AE97">← AE97 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">SEC</span> <span class="comment">; Calculate new allocation size</span></td> </tr> <tr id="addr-AEC2"> <td class="addr"><a href="#addr-AEC2">AEC2</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: compute pages needed</span></td> </tr> <tr id="addr-AEC4"> <td class="addr"><a href="#addr-AEC4">AEC4</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Add PTR mid-low + 1 page</span></td> </tr> <tr id="addr-AEC7"> <td class="addr"><a href="#addr-AEC7">AEC7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109E">wksp_new_ptr_4</span></span> <span class="comment">; Store new allocation mid</span></td> </tr> <tr id="addr-AECA"> <td class="addr"><a href="#addr-AECA">AECA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: propagate carry</span></td> </tr> <tr id="addr-AECC"> <td class="addr"><a href="#addr-AECC">AECC</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Add PTR high + carry</span></td> </tr> <tr id="addr-AECF"> <td class="addr"><a href="#addr-AECF">AECF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109F">wksp_osgbpb_wksp_9f</span></span> <span class="comment">; Store new allocation high</span></td> </tr> <tr id="addr-AED2"> <td class="addr"><a href="#addr-AED2">AED2</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-AED7" data-tip="&AED7">switch_drive_for_extend</a></span> <span class="comment">; No overflow: proceed</span></td> </tr> <tr id="addr-AED4"> <td class="addr"><a href="#addr-AED4">AED4</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8656" data-tip="&8656">disc_full_error</a></span> <span class="comment">; Overflow: Disc full error</span></td> </tr> <tr id="addr-AED7"> <td class="addr"><a href="#addr-AED7">AED7</a></td> <td><span class="label">.switch_drive_for_extend<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AED2">← AED2 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ADC5" data-tip="&ADC5 – Switch to channel's drive for I/O">switch_to_channel_drive</a></span> <span class="comment">; Switch to file's drive</span></td> </tr> <tr id="addr-AEDA"> <td class="addr"><a href="#addr-AEDA">AEDA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11A2">wksp_ch_alloc_l</span>,x</span> <span class="comment">; Get current allocation low</span></td> </tr> <tr id="addr-AEDD"> <td class="addr"><a href="#addr-AEDD">AEDD</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Compare with 1 (minimum)</span></td> </tr> <tr id="addr-AEDF"> <td class="addr"><a href="#addr-AEDF">AEDF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; Get allocation mid-low</span></td> </tr> <tr id="addr-AEE2"> <td class="addr"><a href="#addr-AEE2">AEE2</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add carry from compare</span></td> </tr> <tr id="addr-AEE4"> <td class="addr"><a href="#addr-AEE4">AEE4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1037">wksp_object_size</span></span> <span class="comment">; Store as required size low</span></td> </tr> <tr id="addr-AEE7"> <td class="addr"><a href="#addr-AEE7">AEE7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; Get allocation mid-high</span></td> </tr> <tr id="addr-AEEA"> <td class="addr"><a href="#addr-AEEA">AEEA</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add carry</span></td> </tr> <tr id="addr-AEEC"> <td class="addr"><a href="#addr-AEEC">AEEC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1038">wksp_object_size_mid</span></span> <span class="comment">; Store as required size mid</span></td> </tr> <tr id="addr-AEEF"> <td class="addr"><a href="#addr-AEEF">AEEF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; Get allocation high</span></td> </tr> <tr id="addr-AEF2"> <td class="addr"><a href="#addr-AEF2">AEF2</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add carry</span></td> </tr> <tr id="addr-AEF4"> <td class="addr"><a href="#addr-AEF4">AEF4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1039">wksp_object_size_hi</span></span> <span class="comment">; Store as required size high</span></td> </tr> <tr id="addr-AEF7"> <td class="addr"><a href="#addr-AEF7">AEF7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear sector info</span></td> </tr> <tr id="addr-AEF9"> <td class="addr"><a href="#addr-AEF9">AEF9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&103D">wksp_alloc_size</span></span> <span class="comment">; Clear low</span></td> </tr> <tr id="addr-AEFC"> <td class="addr"><a href="#addr-AEFC">AEFC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109E">wksp_new_ptr_4</span></span> <span class="comment">; Get new allocation mid</span></td> </tr> <tr id="addr-AEFF"> <td class="addr"><a href="#addr-AEFF">AEFF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&103E">wksp_alloc_size_mid</span></span> <span class="comment">; Store as extension mid</span></td> </tr> <tr id="addr-AF02"> <td class="addr"><a href="#addr-AF02">AF02</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109F">wksp_osgbpb_wksp_9f</span></span> <span class="comment">; Get new allocation high</span></td> </tr> <tr id="addr-AF05"> <td class="addr"><a href="#addr-AF05">AF05</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&103F">wksp_alloc_size_hi</span></span> <span class="comment">; Store as extension high</span></td> </tr> <tr id="addr-AF08"> <td class="addr"><a href="#addr-AF08">AF08</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-84B5" data-tip="&84B5 – Release disc space back to free space map">release_disc_space</a></span></td> </tr> <tr id="addr-AF0B"> <td class="addr"><a href="#addr-AF0B">AF0B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8632" data-tip="&8632 – Allocate disc space from free space map">allocate_disc_space</a></span> <span class="comment">; Allocate disc space from FSM</span></td> </tr> <tr id="addr-AF0E"> <td class="addr"><a href="#addr-AF0E">AF0E</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Y=&12: update dir entry length</span></td> </tr> <tr id="addr-AF10"> <td class="addr"><a href="#addr-AF10">AF10</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear length low byte</span></td> </tr> <tr id="addr-AF12"> <td class="addr"><a href="#addr-AF12">AF12</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AF14"> <td class="addr"><a href="#addr-AF14">AF14</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store zero in entry length low</span></td> </tr> <tr id="addr-AF16"> <td class="addr"><a href="#addr-AF16">AF16</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11A2">wksp_ch_alloc_l</span>,x</span> <span class="comment">; Update channel alloc low</span></td> </tr> <tr id="addr-AF19"> <td class="addr"><a href="#addr-AF19">AF19</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-AF1A"> <td class="addr"><a href="#addr-AF1A">AF1A</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in dir entry</span></td> </tr> <tr id="addr-AF1C"> <td class="addr"><a href="#addr-AF1C">AF1C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; Update channel alloc mid-low</span></td> </tr> <tr id="addr-AF1F"> <td class="addr"><a href="#addr-AF1F">AF1F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109E">wksp_new_ptr_4</span></span> <span class="comment">; Get new alloc mid</span></td> </tr> <tr id="addr-AF22"> <td class="addr"><a href="#addr-AF22">AF22</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-AF23"> <td class="addr"><a href="#addr-AF23">AF23</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in dir entry</span></td> </tr> <tr id="addr-AF25"> <td class="addr"><a href="#addr-AF25">AF25</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; Update channel alloc mid-high</span></td> </tr> <tr id="addr-AF28"> <td class="addr"><a href="#addr-AF28">AF28</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109F">wksp_osgbpb_wksp_9f</span></span> <span class="comment">; Get new alloc high</span></td> </tr> <tr id="addr-AF2B"> <td class="addr"><a href="#addr-AF2B">AF2B</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-AF2C"> <td class="addr"><a href="#addr-AF2C">AF2C</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in dir entry</span></td> </tr> <tr id="addr-AF2E"> <td class="addr"><a href="#addr-AF2E">AF2E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; Update channel alloc high</span></td> </tr> <tr id="addr-AF31"> <td class="addr"><a href="#addr-AF31">AF31</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103A">wksp_alloc_sector</span></span> <span class="comment">; Get new start sector low</span></td> </tr> <tr id="addr-AF34"> <td class="addr"><a href="#addr-AF34">AF34</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-AF35"> <td class="addr"><a href="#addr-AF35">AF35</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in dir entry start sector</span></td> </tr> <tr id="addr-AF37"> <td class="addr"><a href="#addr-AF37">AF37</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Update channel start sector low</span></td> </tr> <tr id="addr-AF3A"> <td class="addr"><a href="#addr-AF3A">AF3A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103B">wksp_saved_count</span></span> <span class="comment">; Get new start sector mid</span></td> </tr> <tr id="addr-AF3D"> <td class="addr"><a href="#addr-AF3D">AF3D</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-AF3E"> <td class="addr"><a href="#addr-AF3E">AF3E</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in dir entry</span></td> </tr> <tr id="addr-AF40"> <td class="addr"><a href="#addr-AF40">AF40</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Update channel start sector mid</span></td> </tr> <tr id="addr-AF43"> <td class="addr"><a href="#addr-AF43">AF43</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103C">wksp_saved_count_1</span></span> <span class="comment">; Get new start sector high</span></td> </tr> <tr id="addr-AF46"> <td class="addr"><a href="#addr-AF46">AF46</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-AF47"> <td class="addr"><a href="#addr-AF47">AF47</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in dir entry</span></td> </tr> <tr id="addr-AF49"> <td class="addr"><a href="#addr-AF49">AF49</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; OR with drive number for channel</span></td> </tr> <tr id="addr-AF4C"> <td class="addr"><a href="#addr-AF4C">AF4C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Update channel start sector+drive</span></td> </tr> <tr id="addr-AF4F"> <td class="addr"><a href="#addr-AF4F">AF4F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write directory back to disc</span></td> </tr> <tr id="addr-AF52"> <td class="addr"><a href="#addr-AF52">AF52</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Clear bit 3 of ADFS flags</span></td> </tr> <tr id="addr-AF54"> <td class="addr"><a href="#addr-AF54">AF54</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="247 &F7 %11110111">#&f7</span></span> <span class="comment">; Mask off bit 3</span></td> </tr> <tr id="addr-AF56"> <td class="addr"><a href="#addr-AF56">AF56</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store cleared flags</span></td> </tr> <tr id="addr-AF58"> <td class="addr"><a href="#addr-AF58">AF58</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Set up buffer: page &12</span></td> </tr> <tr id="addr-AF5A"> <td class="addr"><a href="#addr-AF5A">AF5A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1060">wksp_compact_start_page</span></span> <span class="comment">; Store buffer start page</span></td> </tr> <tr id="addr-AF5D"> <td class="addr"><a href="#addr-AF5D">AF5D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Buffer length: 9 pages (&1200)</span></td> </tr> <tr id="addr-AF5F"> <td class="addr"><a href="#addr-AF5F">AF5F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1061">wksp_compact_length</span></span> <span class="comment">; Store buffer length</span></td> </tr> <tr id="addr-AF62"> <td class="addr"><a href="#addr-AF62">AF62</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: check if file was relocated</span></td> </tr> <tr id="addr-AF64"> <td class="addr"><a href="#addr-AF64">AF64</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Y=2: compare old and new sectors</span></td> </tr> <tr id="addr-AF66"> <td class="addr"><a href="#addr-AF66">AF66</a></td> <td><span class="label">.copy_old_sector_info_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AF7F">← AF7F BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span>,y</span> <span class="comment">; Get old start sector byte</span></td> </tr> <tr id="addr-AF69"> <td class="addr"><a href="#addr-AF69">AF69</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A2">wksp_copy_read_sector</span>,y</span> <span class="comment">; Store for copy source</span></td> </tr> <tr id="addr-AF6C"> <td class="addr"><a href="#addr-AF6C">AF6C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&103A">wksp_alloc_sector</span>,y</span> <span class="comment">; Compare with new start sector</span></td> </tr> <tr id="addr-AF6F"> <td class="addr"><a href="#addr-AF6F">AF6F</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AF75" data-tip="&AF75">check_relocation_needed</a></span> <span class="comment">; Same: no relocation needed</span></td> </tr> <tr id="addr-AF71"> <td class="addr"><a href="#addr-AF71">AF71</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Different: flag relocation</span></td> </tr> <tr id="addr-AF72"> <td class="addr"><a href="#addr-AF72">AF72</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103A">wksp_alloc_sector</span>,y</span> <span class="comment">; Get new start sector byte</span></td> </tr> <tr id="addr-AF75"> <td class="addr"><a href="#addr-AF75">AF75</a></td> <td><span class="label">.check_relocation_needed<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AF6F">← AF6F BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A8">wksp_copy_src_sector</span>,y</span> <span class="comment">; Store for copy destination</span></td> </tr> <tr id="addr-AF78"> <td class="addr"><a href="#addr-AF78">AF78</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1037">wksp_object_size</span>,y</span> <span class="comment">; Get required size byte</span></td> </tr> <tr id="addr-AF7B"> <td class="addr"><a href="#addr-AF7B">AF7B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10A5">wksp_copy_write_sector</span>,y</span> <span class="comment">; Store for copy length</span></td> </tr> <tr id="addr-AF7E"> <td class="addr"><a href="#addr-AF7E">AF7E</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-AF7F"> <td class="addr"><a href="#addr-AF7F">AF7F</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-AF66" data-tip="&AF66">copy_old_sector_info_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-AF81"> <td class="addr"><a href="#addr-AF81">AF81</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; X non-zero: relocation occurred</span></td> </tr> <tr id="addr-AF82"> <td class="addr"><a href="#addr-AF82">AF82</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AF87" data-tip="&AF87">skip_zero_fill</a></span> <span class="comment">; Zero: no relocation, skip copy</span></td> </tr> <tr id="addr-AF84"> <td class="addr"><a href="#addr-AF84">AF84</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-96A6" data-tip="&96A6">execute_sector_copy</a></span> <span class="comment">; Copy data from old to new location</span></td> </tr> <tr id="addr-AF87"> <td class="addr"><a href="#addr-AF87">AF87</a></td> <td><span class="label">.skip_zero_fill<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AEBE">← AEBE JMP</a><a href="#addr-AF82">← AF82 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Check extension flag</span></td> </tr> <tr id="addr-AF8A"> <td class="addr"><a href="#addr-AF8A">AF8A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-AF8F" data-tip="&AF8F">calc_zero_fill_start</a></span> <span class="comment">; Non-zero: skip zeroing</span></td> </tr> <tr id="addr-AF8C"> <td class="addr"><a href="#addr-AF8C">AF8C</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B060" data-tip="&B060 – Update EXT from new PTR value">update_ext_from_new_ptr</a></span> <span class="comment">; Jump to update EXT</span></td> </tr> <tr id="addr-AF8F"> <td class="addr"><a href="#addr-AF8F">AF8F</a></td> <td><span class="label">.calc_zero_fill_start<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AF8A">← AF8A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AF91"> <td class="addr"><a href="#addr-AF91">AF91</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for address calculation</span></td> </tr> <tr id="addr-AF92"> <td class="addr"><a href="#addr-AF92">AF92</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Get EXT mid-low</span></td> </tr> <tr id="addr-AF95"> <td class="addr"><a href="#addr-AF95">AF95</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Add channel offset low</span></td> </tr> <tr id="addr-AF98"> <td class="addr"><a href="#addr-AF98">AF98</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Store zero-fill start sector low</span></td> </tr> <tr id="addr-AF9B"> <td class="addr"><a href="#addr-AF9B">AF9B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Get EXT mid-high</span></td> </tr> <tr id="addr-AF9E"> <td class="addr"><a href="#addr-AF9E">AF9E</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Add channel offset mid</span></td> </tr> <tr id="addr-AFA1"> <td class="addr"><a href="#addr-AFA1">AFA1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Store zero-fill start mid</span></td> </tr> <tr id="addr-AFA4"> <td class="addr"><a href="#addr-AFA4">AFA4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Get EXT high</span></td> </tr> <tr id="addr-AFA7"> <td class="addr"><a href="#addr-AFA7">AFA7</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Add channel base + drive</span></td> </tr> <tr id="addr-AFAA"> <td class="addr"><a href="#addr-AFAA">AFAA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Store zero-fill start high</span></td> </tr> <tr id="addr-AFAD"> <td class="addr"><a href="#addr-AFAD">AFAD</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; A=&C0: write buffer mode</span></td> </tr> <tr id="addr-AFAF"> <td class="addr"><a href="#addr-AFAF">AFAF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABD8" data-tip="&ABD8 – Find or allocate a buffer for a sector">find_buffer_for_sector</a></span> <span class="comment">; Set up buffer for writing zeros</span></td> </tr> <tr id="addr-AFB2"> <td class="addr"><a href="#addr-AFB2">AFB2</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-AFB4"> <td class="addr"><a href="#addr-AFB4">AFB4</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Get EXT low as buffer start</span></td> </tr> <tr id="addr-AFB7"> <td class="addr"><a href="#addr-AFB7">AFB7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: zero fill</span></td> </tr> <tr id="addr-AFB9"> <td class="addr"><a href="#addr-AFB9">AFB9</a></td> <td><span class="label">.zero_fill_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AFBC">← AFBC BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Write zero to buffer</span></td> </tr> <tr id="addr-AFBB"> <td class="addr"><a href="#addr-AFBB">AFBB</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-AFBC"> <td class="addr"><a href="#addr-AFBC">AFBC</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AFB9" data-tip="&AFB9">zero_fill_sector_loop</a></span> <span class="comment">; Loop for rest of sector</span></td> </tr> <tr id="addr-AFBE"> <td class="addr"><a href="#addr-AFBE">AFBE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Get new PTR mid-low</span></td> </tr> <tr id="addr-AFC1"> <td class="addr"><a href="#addr-AFC1">AFC1</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry</span></td> </tr> <tr id="addr-AFC2"> <td class="addr"><a href="#addr-AFC2">AFC2</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Add channel base</span></td> </tr> <tr id="addr-AFC5"> <td class="addr"><a href="#addr-AFC5">AFC5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Store target sector low</span></td> </tr> <tr id="addr-AFC8"> <td class="addr"><a href="#addr-AFC8">AFC8</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Get new PTR mid-high</span></td> </tr> <tr id="addr-AFCB"> <td class="addr"><a href="#addr-AFCB">AFCB</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Add channel offset</span></td> </tr> <tr id="addr-AFCE"> <td class="addr"><a href="#addr-AFCE">AFCE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Store target sector mid</span></td> </tr> <tr id="addr-AFD1"> <td class="addr"><a href="#addr-AFD1">AFD1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Get new PTR high</span></td> </tr> <tr id="addr-AFD4"> <td class="addr"><a href="#addr-AFD4">AFD4</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Add channel base + drive</span></td> </tr> <tr id="addr-AFD7"> <td class="addr"><a href="#addr-AFD7">AFD7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Store target sector high</span></td> </tr> <tr id="addr-AFDA"> <td class="addr"><a href="#addr-AFDA">AFDA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Get PTR low byte</span></td> </tr> <tr id="addr-AFDD"> <td class="addr"><a href="#addr-AFDD">AFDD</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AFF2" data-tip="&AFF2">decrement_fill_sector</a></span> <span class="comment">; Non-zero: not sector-aligned</span></td> </tr> <tr id="addr-AFDF"> <td class="addr"><a href="#addr-AFDF">AFDF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Check sector low</span></td> </tr> <tr id="addr-AFE2"> <td class="addr"><a href="#addr-AFE2">AFE2</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AFEF" data-tip="&AFEF">check_sector_mid</a></span> <span class="comment">; Non-zero: adjust sector</span></td> </tr> <tr id="addr-AFE4"> <td class="addr"><a href="#addr-AFE4">AFE4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Check sector mid</span></td> </tr> <tr id="addr-AFE7"> <td class="addr"><a href="#addr-AFE7">AFE7</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-AFEC" data-tip="&AFEC">check_sector_low</a></span> <span class="comment">; Non-zero: adjust mid</span></td> </tr> <tr id="addr-AFE9"> <td class="addr"><a href="#addr-AFE9">AFE9</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Decrement sector high</span></td> </tr> <tr id="addr-AFEC"> <td class="addr"><a href="#addr-AFEC">AFEC</a></td> <td><span class="label">.check_sector_low<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AFE7">← AFE7 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Decrement sector mid</span></td> </tr> <tr id="addr-AFEF"> <td class="addr"><a href="#addr-AFEF">AFEF</a></td> <td><span class="label">.check_sector_mid<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AFE2">← AFE2 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Decrement sector low</span></td> </tr> <tr id="addr-AFF2"> <td class="addr"><a href="#addr-AFF2">AFF2</a></td> <td><span class="label">.decrement_fill_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AFDD">← AFDD BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Compare with buffer sector</span></td> </tr> <tr id="addr-AFF5"> <td class="addr"><a href="#addr-AFF5">AFF5</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Match low byte?</span></td> </tr> <tr id="addr-AFF8"> <td class="addr"><a href="#addr-AFF8">AFF8</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B00D" data-tip="&B00D">write_zero_sector</a></span> <span class="comment">; No: need to write more zeros</span></td> </tr> <tr id="addr-AFFA"> <td class="addr"><a href="#addr-AFFA">AFFA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Match mid byte?</span></td> </tr> <tr id="addr-AFFD"> <td class="addr"><a href="#addr-AFFD">AFFD</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Check mid</span></td> </tr> <tr id="addr-B000"> <td class="addr"><a href="#addr-B000">B000</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B00D" data-tip="&B00D">write_zero_sector</a></span> <span class="comment">; No: need more</span></td> </tr> <tr id="addr-B002"> <td class="addr"><a href="#addr-B002">B002</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Match high byte?</span></td> </tr> <tr id="addr-B005"> <td class="addr"><a href="#addr-B005">B005</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Check high</span></td> </tr> <tr id="addr-B008"> <td class="addr"><a href="#addr-B008">B008</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B00D" data-tip="&B00D">write_zero_sector</a></span> <span class="comment">; No: need more</span></td> </tr> <tr id="addr-B00A"> <td class="addr"><a href="#addr-B00A">B00A</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B060" data-tip="&B060 – Update EXT from new PTR value">update_ext_from_new_ptr</a></span> <span class="comment">; All match: done zeroing</span></td> </tr> <tr id="addr-B00D"> <td class="addr"><a href="#addr-B00D">B00D</a></td> <td><span class="label">.write_zero_sector<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AFF8">← AFF8 BNE</a><a href="#addr-B000">← B000 BNE</a><a href="#addr-B008">← B008 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8305" data-tip="&8305 – Wait while files are being ensured">wait_ensuring</a></span></td> </tr> <tr id="addr-B010"> <td class="addr"><a href="#addr-B010">B010</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Advance buffer sector: inc low</span></td> </tr> <tr id="addr-B013"> <td class="addr"><a href="#addr-B013">B013</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B01D" data-tip="&B01D">advance_fill_sector</a></span> <span class="comment">; No wrap</span></td> </tr> <tr id="addr-B015"> <td class="addr"><a href="#addr-B015">B015</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Wrap: inc mid</span></td> </tr> <tr id="addr-B018"> <td class="addr"><a href="#addr-B018">B018</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B01D" data-tip="&B01D">advance_fill_sector</a></span> <span class="comment">; No wrap</span></td> </tr> <tr id="addr-B01A"> <td class="addr"><a href="#addr-B01A">B01A</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Wrap: inc high</span></td> </tr> <tr id="addr-B01D"> <td class="addr"><a href="#addr-B01D">B01D</a></td> <td><span class="label">.advance_fill_sector<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B013">← B013 BNE</a><a href="#addr-B018">← B018 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; A=&40: read buffer mode</span></td> </tr> <tr id="addr-B01F"> <td class="addr"><a href="#addr-B01F">B01F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABD8" data-tip="&ABD8 – Find or allocate a buffer for a sector">find_buffer_for_sector</a></span> <span class="comment">; Load next sector into buffer</span></td> </tr> <tr id="addr-B022"> <td class="addr"><a href="#addr-B022">B022</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: zero fill entire sector</span></td> </tr> <tr id="addr-B024"> <td class="addr"><a href="#addr-B024">B024</a></td> <td> <span class="opcode">TYA</span></td> </tr> <tr id="addr-B025"> <td class="addr"><a href="#addr-B025">B025</a></td> <td><span class="label">.zero_entire_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B028">← B028 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Write zero to buffer</span></td> </tr> <tr id="addr-B027"> <td class="addr"><a href="#addr-B027">B027</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B028"> <td class="addr"><a href="#addr-B028">B028</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B025" data-tip="&B025">zero_entire_sector_loop</a></span> <span class="comment">; Loop for 256 bytes</span></td> </tr> <tr id="addr-B02A"> <td class="addr"><a href="#addr-B02A">B02A</a></td> <td><span class="label">.mark_buffer_dirty<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B053">← B053 BNE</a><a href="#addr-B058">← B058 BNE</a><a href="#addr-B05D">← B05D JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&10A1">wksp_ch_buf_sector_1</span></span> <span class="comment">; Get channel buffer table index</span></td> </tr> <tr id="addr-B02D"> <td class="addr"><a href="#addr-B02D">B02D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; A=&C0: mark buffer as dirty</span></td> </tr> <tr id="addr-B02F"> <td class="addr"><a href="#addr-B02F">B02F</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; OR with channel state</span></td> </tr> <tr id="addr-B032"> <td class="addr"><a href="#addr-B032">B032</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1004">wksp_buf_flag</span>,x</span> <span class="comment">; Store dirty state</span></td> </tr> <tr id="addr-B035"> <td class="addr"><a href="#addr-B035">B035</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAF3" data-tip="&AAF3">flush_dirty_channel_buffer</a></span> <span class="comment">; Flush dirty buffer to disc</span></td> </tr> <tr id="addr-B038"> <td class="addr"><a href="#addr-B038">B038</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Compare current sector with target</span></td> </tr> <tr id="addr-B03B"> <td class="addr"><a href="#addr-B03B">B03B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Compare low bytes</span></td> </tr> <tr id="addr-B03E"> <td class="addr"><a href="#addr-B03E">B03E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B050" data-tip="&B050">advance_channel_sector</a></span> <span class="comment">; No match: advance sector</span></td> </tr> <tr id="addr-B040"> <td class="addr"><a href="#addr-B040">B040</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Compare mid bytes</span></td> </tr> <tr id="addr-B043"> <td class="addr"><a href="#addr-B043">B043</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Compare</span></td> </tr> <tr id="addr-B046"> <td class="addr"><a href="#addr-B046">B046</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B050" data-tip="&B050">advance_channel_sector</a></span> <span class="comment">; No match: advance</span></td> </tr> <tr id="addr-B048"> <td class="addr"><a href="#addr-B048">B048</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Compare high bytes</span></td> </tr> <tr id="addr-B04B"> <td class="addr"><a href="#addr-B04B">B04B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Compare</span></td> </tr> <tr id="addr-B04E"> <td class="addr"><a href="#addr-B04E">B04E</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B060" data-tip="&B060 – Update EXT from new PTR value">update_ext_from_new_ptr</a></span> <span class="comment">; Match: done writing zeros</span></td> </tr> <tr id="addr-B050"> <td class="addr"><a href="#addr-B050">B050</a></td> <td><span class="label">.advance_channel_sector<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B03E">← B03E BNE</a><a href="#addr-B046">← B046 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Advance channel sector: inc low</span></td> </tr> <tr id="addr-B053"> <td class="addr"><a href="#addr-B053">B053</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B02A" data-tip="&B02A">mark_buffer_dirty</a></span> <span class="comment">; No wrap</span></td> </tr> <tr id="addr-B055"> <td class="addr"><a href="#addr-B055">B055</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Wrap: inc mid</span></td> </tr> <tr id="addr-B058"> <td class="addr"><a href="#addr-B058">B058</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B02A" data-tip="&B02A">mark_buffer_dirty</a></span> <span class="comment">; No wrap</span></td> </tr> <tr id="addr-B05A"> <td class="addr"><a href="#addr-B05A">B05A</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Wrap: inc high</span></td> </tr> <tr id="addr-B05D"> <td class="addr"><a href="#addr-B05D">B05D</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B02A" data-tip="&B02A">mark_buffer_dirty</a></span> <span class="comment">; Continue zeroing loop</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B060"> <td colspan="2"><div class="sub-header"> <h3>Update EXT from new PTR value</h3> <div class="sub-desc"><p>Copy 4-byte PTR from workspace to the channel's EXT, then save workspace and restore drive state.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B060">B060</a></td> <td><span class="label">.update_ext_from_new_ptr<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AF8C">← AF8C JMP</a><a href="#addr-B00A">← B00A JMP</a><a href="#addr-B04E">← B04E BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B062"> <td class="addr"><a href="#addr-B062">B062</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Get new PTR low</span></td> </tr> <tr id="addr-B065"> <td class="addr"><a href="#addr-B065">B065</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Store as new EXT low</span></td> </tr> <tr id="addr-B068"> <td class="addr"><a href="#addr-B068">B068</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Get new PTR mid-low</span></td> </tr> <tr id="addr-B06B"> <td class="addr"><a href="#addr-B06B">B06B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Store as new EXT mid-low</span></td> </tr> <tr id="addr-B06E"> <td class="addr"><a href="#addr-B06E">B06E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Get new PTR mid-high</span></td> </tr> <tr id="addr-B071"> <td class="addr"><a href="#addr-B071">B071</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Store as new EXT mid-high</span></td> </tr> <tr id="addr-B074"> <td class="addr"><a href="#addr-B074">B074</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Get new PTR high</span></td> </tr> <tr id="addr-B077"> <td class="addr"><a href="#addr-B077">B077</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Store as new EXT high</span></td> </tr> <tr id="addr-B07A"> <td class="addr"><a href="#addr-B07A">B07A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace</span></td> </tr> <tr id="addr-B07D"> <td class="addr"><a href="#addr-B07D">B07D</a></td> <td><span class="label">.restore_drive_after_extend<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AEB9">← AEB9 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10BF">wksp_saved_drive_2</span></span> <span class="comment">; Restore saved drive from temp</span></td> </tr> <tr id="addr-B080"> <td class="addr"><a href="#addr-B080">B080</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Set as saved drive</span></td> </tr> <tr id="addr-B083"> <td class="addr"><a href="#addr-B083">B083</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: restore 3 bytes of CSD</span></td> </tr> <tr id="addr-B085"> <td class="addr"><a href="#addr-B085">B085</a></td> <td><span class="label">.restore_csd_after_extend_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B08C">← B08C BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10BC">wksp_osgbpb_wksp_bc</span>,x</span> <span class="comment">; Get saved CSD byte</span></td> </tr> <tr id="addr-B088"> <td class="addr"><a href="#addr-B088">B088</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102C">wksp_csd_drive_sector</span>,x</span> <span class="comment">; Restore to CSD workspace</span></td> </tr> <tr id="addr-B08B"> <td class="addr"><a href="#addr-B08B">B08B</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B08C"> <td class="addr"><a href="#addr-B08C">B08C</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B085" data-tip="&B085">restore_csd_after_extend_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-B08E"> <td class="addr"><a href="#addr-B08E">B08E</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B08F"> <td colspan="2"><div class="sub-header"> <h3>OSBPUT handler</h3> <div class="sub-desc"><p>Handle OSBPUT calls to write a single byte to an open file.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B08F">B08F</a></td> <td><span class="label">.osbput_handler</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Save X register</span></td> </tr> <tr id="addr-B091"> <td class="addr"><a href="#addr-B091">B091</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save byte to write on stack</span></td> </tr> <tr id="addr-B092"> <td class="addr"><a href="#addr-B092">B092</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACFE" data-tip="&ACFE – Validate and set channel number from Y">check_set_channel_y</a></span> <span class="comment">; Validate file handle in Y</span></td> </tr> <tr id="addr-B095"> <td class="addr"><a href="#addr-B095">B095</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear modification flag</span></td> </tr> <tr id="addr-B097"> <td class="addr"><a href="#addr-B097">B097</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10CF">wksp_bput_modified</span></span> <span class="comment">; Clear modification flag</span></td> </tr> <tr id="addr-B09A"> <td class="addr"><a href="#addr-B09A">B09A</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer channel flags to Y</span></td> </tr> <tr id="addr-B09B"> <td class="addr"><a href="#addr-B09B">B09B</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B0B5" data-tip="&B0B5">check_buffer_state</a></span> <span class="comment">; Bit 7 set: file is writable</span></td> </tr> <tr id="addr-B09D"> <td class="addr"><a href="#addr-B09D">B09D</a></td> <td><span class="label">.not_open_for_update_error<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AA6C">← AA6C JMP</a><a href="#addr-B5C5">← B5C5 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-B0A0"> <td class="addr"><a href="#addr-B0A0">B0A0</a></td> <td> <span class="directive">EQUB</span> <span data-tip="193 &C1 %11000001">&C1</span> <span class="comment">; Error &C1: Not open for update</span></td> </tr> <tr id="addr-B0A1"> <td class="addr"><a href="#addr-B0A1">B0A1</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Not open for update"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-B0B5"> <td class="addr"><a href="#addr-B0B5">B0B5</a></td> <td><span class="label">.check_buffer_state<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B09B">← B09B BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-B0B8"> <td class="addr"><a href="#addr-B0B8">B0B8</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; Isolate buffer state bits (0-2)</span></td> </tr> <tr id="addr-B0BA"> <td class="addr"><a href="#addr-B0BA">B0BA</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="6 &06 %00000110 ACK">#6</span></span> <span class="comment">; State >= 6: buffer dirty, ready</span></td> </tr> <tr id="addr-B0BC"> <td class="addr"><a href="#addr-B0BC">B0BC</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B0F0" data-tip="&B0F0">calc_buffer_sector_addr</a></span> <span class="comment">; Buffer state >= 6: ready</span></td> </tr> <tr id="addr-B0BE"> <td class="addr"><a href="#addr-B0BE">B0BE</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; State = 3: buffer clean, skip load</span></td> </tr> <tr id="addr-B0C0"> <td class="addr"><a href="#addr-B0C0">B0C0</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B0F0" data-tip="&B0F0">calc_buffer_sector_addr</a></span> <span class="comment">; Buffer state = 3: skip load</span></td> </tr> <tr id="addr-B0C2"> <td class="addr"><a href="#addr-B0C2">B0C2</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Compute PTR+1 to check if extending</span></td> </tr> <tr id="addr-B0C5"> <td class="addr"><a href="#addr-B0C5">B0C5</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for PTR+1 calculation</span></td> </tr> <tr id="addr-B0C6"> <td class="addr"><a href="#addr-B0C6">B0C6</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add 1 (carry) to PTR low</span></td> </tr> <tr id="addr-B0C8"> <td class="addr"><a href="#addr-B0C8">B0C8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Store next PTR low in workspace</span></td> </tr> <tr id="addr-B0CB"> <td class="addr"><a href="#addr-B0CB">B0CB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Get PTR mid-low</span></td> </tr> <tr id="addr-B0CE"> <td class="addr"><a href="#addr-B0CE">B0CE</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add carry</span></td> </tr> <tr id="addr-B0D0"> <td class="addr"><a href="#addr-B0D0">B0D0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Store next PTR mid-low</span></td> </tr> <tr id="addr-B0D3"> <td class="addr"><a href="#addr-B0D3">B0D3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Get PTR mid-high</span></td> </tr> <tr id="addr-B0D6"> <td class="addr"><a href="#addr-B0D6">B0D6</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add carry</span></td> </tr> <tr id="addr-B0D8"> <td class="addr"><a href="#addr-B0D8">B0D8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Store next PTR mid-high</span></td> </tr> <tr id="addr-B0DB"> <td class="addr"><a href="#addr-B0DB">B0DB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Get PTR high</span></td> </tr> <tr id="addr-B0DE"> <td class="addr"><a href="#addr-B0DE">B0DE</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Add carry</span></td> </tr> <tr id="addr-B0E0"> <td class="addr"><a href="#addr-B0E0">B0E0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Store next PTR high</span></td> </tr> <tr id="addr-B0E3"> <td class="addr"><a href="#addr-B0E3">B0E3</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore byte to write</span></td> </tr> <tr id="addr-B0E4"> <td class="addr"><a href="#addr-B0E4">B0E4</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save registers for restore</span></td> </tr> <tr id="addr-B0E7"> <td class="addr"><a href="#addr-B0E7">B0E7</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Re-push byte to write</span></td> </tr> <tr id="addr-B0E8"> <td class="addr"><a href="#addr-B0E8">B0E8</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&10CF">wksp_bput_modified</span></span> <span class="comment">; Set modification flag</span></td> </tr> <tr id="addr-B0EB"> <td class="addr"><a href="#addr-B0EB">B0EB</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AE59" data-tip="&AE59">check_ptr_within_allocation</a></span> <span class="comment">; Validate PTR and load sector</span></td> </tr> <tr id="addr-B0EE"> <td class="addr"><a href="#addr-B0EE">B0EE</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B0F0"> <td class="addr"><a href="#addr-B0F0">B0F0</a></td> <td><span class="label">.calc_buffer_sector_addr<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B0BC">← B0BC BCS</a><a href="#addr-B0C0">← B0C0 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for address calc</span></td> </tr> <tr id="addr-B0F1"> <td class="addr"><a href="#addr-B0F1">B0F1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Get channel start sector low</span></td> </tr> <tr id="addr-B0F4"> <td class="addr"><a href="#addr-B0F4">B0F4</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Add PTR to get current disc sector</span></td> </tr> <tr id="addr-B0F7"> <td class="addr"><a href="#addr-B0F7">B0F7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Store disc sector address low</span></td> </tr> <tr id="addr-B0FA"> <td class="addr"><a href="#addr-B0FA">B0FA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Get channel start sector mid</span></td> </tr> <tr id="addr-B0FD"> <td class="addr"><a href="#addr-B0FD">B0FD</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Add PTR mid-high with carry</span></td> </tr> <tr id="addr-B100"> <td class="addr"><a href="#addr-B100">B100</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Store disc sector address mid</span></td> </tr> <tr id="addr-B103"> <td class="addr"><a href="#addr-B103">B103</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel start sector+drive</span></td> </tr> <tr id="addr-B106"> <td class="addr"><a href="#addr-B106">B106</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Add PTR high with carry</span></td> </tr> <tr id="addr-B109"> <td class="addr"><a href="#addr-B109">B109</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Store disc sector address high</span></td> </tr> <tr id="addr-B10C"> <td class="addr"><a href="#addr-B10C">B10C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; A=&C0: buffer write mode</span></td> </tr> <tr id="addr-B10E"> <td class="addr"><a href="#addr-B10E">B10E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABD8" data-tip="&ABD8 – Find or allocate a buffer for a sector">find_buffer_for_sector</a></span> <span class="comment">; Load sector into buffer</span></td> </tr> <tr id="addr-B111"> <td class="addr"><a href="#addr-B111">B111</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B113"> <td class="addr"><a href="#addr-B113">B113</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Get PTR low as buffer offset</span></td> </tr> <tr id="addr-B116"> <td class="addr"><a href="#addr-B116">B116</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore byte to write</span></td> </tr> <tr id="addr-B117"> <td class="addr"><a href="#addr-B117">B117</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Write byte into buffer at PTR</span></td> </tr> <tr id="addr-B119"> <td class="addr"><a href="#addr-B119">B119</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save byte again</span></td> </tr> <tr id="addr-B11A"> <td class="addr"><a href="#addr-B11A">B11A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B123" data-tip="&B123 – Increment PTR after byte write">increment_ptr_after_write</a></span> <span class="comment">; Advance PTR and update flags</span></td> </tr> <tr id="addr-B11D"> <td class="addr"><a href="#addr-B11D">B11D</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore written byte</span></td> </tr> <tr id="addr-B11E"> <td class="addr"><a href="#addr-B11E">B11E</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C2">zp_save_y</span></span> <span class="comment">; Restore Y</span></td> </tr> <tr id="addr-B120"> <td class="addr"><a href="#addr-B120">B120</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-B122"> <td class="addr"><a href="#addr-B122">B122</a></td> <td><span class="label">.return_39<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B128">← B128 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B123"> <td colspan="2"><div class="sub-header"> <h3>Increment PTR after byte write</h3> <div class="sub-desc"><p>Increment the channel's 4-byte PTR. On page boundaries, save workspace and propagate carry through mid/high bytes.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B123">B123</a></td> <td><span class="label">.increment_ptr_after_write<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-ADBA">← ADBA JSR</a><a href="#addr-B11A">← B11A JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B125"> <td class="addr"><a href="#addr-B125">B125</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Increment PTR low byte</span></td> </tr> <tr id="addr-B128"> <td class="addr"><a href="#addr-B128">B128</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B122" data-tip="&B122">return_39</a></span> <span class="comment">; No wrap: done</span></td> </tr> <tr id="addr-B12A"> <td class="addr"><a href="#addr-B12A">B12A</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&10CF">wksp_bput_modified</span></span> <span class="comment">; Check modification flag</span></td> </tr> <tr id="addr-B12D"> <td class="addr"><a href="#addr-B12D">B12D</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B132" data-tip="&B132">increment_ptr_mid_bytes</a></span> <span class="comment">; Not modified: skip workspace save</span></td> </tr> <tr id="addr-B12F"> <td class="addr"><a href="#addr-B12F">B12F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-B132"> <td class="addr"><a href="#addr-B132">B132</a></td> <td><span class="label">.increment_ptr_mid_bytes<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B12D">← B12D BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Increment PTR mid-low</span></td> </tr> <tr id="addr-B135"> <td class="addr"><a href="#addr-B135">B135</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B13F" data-tip="&B13F">update_channel_flags_for_ptr</a></span> <span class="comment">; No wrap: update flags</span></td> </tr> <tr id="addr-B137"> <td class="addr"><a href="#addr-B137">B137</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Increment PTR mid-high</span></td> </tr> <tr id="addr-B13A"> <td class="addr"><a href="#addr-B13A">B13A</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B13F" data-tip="&B13F">update_channel_flags_for_ptr</a></span> <span class="comment">; No wrap: update flags</span></td> </tr> <tr id="addr-B13C"> <td class="addr"><a href="#addr-B13C">B13C</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Increment PTR high</span></td> </tr> <tr id="addr-B13F"> <td class="addr"><a href="#addr-B13F">B13F</a></td> <td><span class="label">.update_channel_flags_for_ptr<span class="ref-badge">←5</span><span class="ref-popup"><a href="#addr-A9BD">← A9BD JSR</a><a href="#addr-B135">← B135 BNE</a><a href="#addr-B13A">← B13A BNE</a><a href="#addr-B2D5">← B2D5 JSR</a><a href="#addr-B713">← B713 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B18C" data-tip="&B18C – Synchronise EXT to PTR if at EOF">sync_ext_to_ptr</a></span> <span class="comment">; Update channel flags for new PTR</span></td> </tr> <tr id="addr-B142"> <td class="addr"><a href="#addr-B142">B142</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save current flags on stack</span></td> </tr> <tr id="addr-B143"> <td class="addr"><a href="#addr-B143">B143</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-B144"> <td class="addr"><a href="#addr-B144">B144</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Compare PTR with EXT: mid-low</span></td> </tr> <tr id="addr-B147"> <td class="addr"><a href="#addr-B147">B147</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Subtract EXT mid-low</span></td> </tr> <tr id="addr-B14A"> <td class="addr"><a href="#addr-B14A">B14A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; PTR mid-high</span></td> </tr> <tr id="addr-B14D"> <td class="addr"><a href="#addr-B14D">B14D</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Subtract EXT mid-high</span></td> </tr> <tr id="addr-B150"> <td class="addr"><a href="#addr-B150">B150</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; PTR high</span></td> </tr> <tr id="addr-B153"> <td class="addr"><a href="#addr-B153">B153</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Subtract EXT high</span></td> </tr> <tr id="addr-B156"> <td class="addr"><a href="#addr-B156">B156</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B181" data-tip="&B181">set_buffer_dirty_and_flush</a></span> <span class="comment">; PTR < EXT: not at EOF</span></td> </tr> <tr id="addr-B158"> <td class="addr"><a href="#addr-B158">B158</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; PTR >= EXT: compare low bytes</span></td> </tr> <tr id="addr-B15B"> <td class="addr"><a href="#addr-B15B">B15B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Compare PTR low with EXT low</span></td> </tr> <tr id="addr-B15E"> <td class="addr"><a href="#addr-B15E">B15E</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B164" data-tip="&B164">check_ext_vs_allocation</a></span> <span class="comment">; Not equal: PTR past EXT</span></td> </tr> <tr id="addr-B160"> <td class="addr"><a href="#addr-B160">B160</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Equal: set EOF flag (bit 2)</span></td> </tr> <tr id="addr-B161"> <td class="addr"><a href="#addr-B161">B161</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Set bit 2 in flags</span></td> </tr> <tr id="addr-B163"> <td class="addr"><a href="#addr-B163">B163</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Re-push updated flags</span></td> </tr> <tr id="addr-B164"> <td class="addr"><a href="#addr-B164">B164</a></td> <td><span class="label">.check_ext_vs_allocation<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B15E">← B15E BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">SEC</span> <span class="comment">; Check if buffer needs flushing</span></td> </tr> <tr id="addr-B165"> <td class="addr"><a href="#addr-B165">B165</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Compare EXT mid-low with allocation</span></td> </tr> <tr id="addr-B168"> <td class="addr"><a href="#addr-B168">B168</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; Subtract allocation mid-low</span></td> </tr> <tr id="addr-B16B"> <td class="addr"><a href="#addr-B16B">B16B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; EXT mid-high</span></td> </tr> <tr id="addr-B16E"> <td class="addr"><a href="#addr-B16E">B16E</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; Subtract allocation mid-high</span></td> </tr> <tr id="addr-B171"> <td class="addr"><a href="#addr-B171">B171</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; EXT high</span></td> </tr> <tr id="addr-B174"> <td class="addr"><a href="#addr-B174">B174</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; Subtract allocation high</span></td> </tr> <tr id="addr-B177"> <td class="addr"><a href="#addr-B177">B177</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B17C" data-tip="&B17C">set_buffer_flush_flag</a></span> <span class="comment">; EXT < allocation: buffer has room</span></td> </tr> <tr id="addr-B179"> <td class="addr"><a href="#addr-B179">B179</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-B17A"> <td class="addr"><a href="#addr-B17A">B17A</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B184" data-tip="&B184">apply_writable_mask</a></span> <span class="comment">; Non-zero flags: keep them</span></td> </tr> <tr id="addr-B17C"> <td class="addr"><a href="#addr-B17C">B17C</a></td> <td><span class="label">.set_buffer_flush_flag<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B177">← B177 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-B17D"> <td class="addr"><a href="#addr-B17D">B17D</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Set bit 1 (buffer needs flushing)</span></td> </tr> <tr id="addr-B17F"> <td class="addr"><a href="#addr-B17F">B17F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B184" data-tip="&B184">apply_writable_mask</a></span></td> </tr> <tr id="addr-B181"> <td class="addr"><a href="#addr-B181">B181</a></td> <td><span class="label">.set_buffer_dirty_and_flush<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B156">← B156 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-B182"> <td class="addr"><a href="#addr-B182">B182</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Set bits 0+1 (dirty + flush)</span></td> </tr> <tr id="addr-B184"> <td class="addr"><a href="#addr-B184">B184</a></td> <td><span class="label">.apply_writable_mask<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B17A">← B17A BNE</a><a href="#addr-B17F">← B17F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B188" data-tip="&B188">store_channel_flags</a></span> <span class="comment">; Bit 7 set: writable channel</span></td> </tr> <tr id="addr-B186"> <td class="addr"><a href="#addr-B186">B186</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="249 &F9 %11111001">#&f9</span></span> <span class="comment">; Clear bits 0-2 (read-only mode)</span></td> </tr> <tr id="addr-B188"> <td class="addr"><a href="#addr-B188">B188</a></td> <td><span class="label">.store_channel_flags<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B184">← B184 BMI</a><a href="#addr-B1B1">← B1B1 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Store updated channel flags</span></td> </tr> <tr id="addr-B18B"> <td class="addr"><a href="#addr-B18B">B18B</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B18C"> <td colspan="2"><div class="sub-header"> <h3>Synchronise EXT to PTR if at EOF</h3> <div class="sub-desc"><p>If the EOF flag is set, copy PTR to EXT. Then recalculate channel flags from the writable and open bits.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B18C">B18C</a></td> <td><span class="label">.sync_ext_to_ptr<span class="ref-badge">←5</span><span class="ref-popup"><a href="#addr-A99E">← A99E JSR</a><a href="#addr-AD45">← AD45 JSR</a><a href="#addr-B13F">← B13F JSR</a><a href="#addr-B3B6">← B3B6 JSR</a><a href="#addr-B5B0">← B5B0 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B18E"> <td class="addr"><a href="#addr-B18E">B18E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get current channel flags</span></td> </tr> <tr id="addr-B191"> <td class="addr"><a href="#addr-B191">B191</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save on stack</span></td> </tr> <tr id="addr-B192"> <td class="addr"><a href="#addr-B192">B192</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Check EOF flag (bit 2)</span></td> </tr> <tr id="addr-B194"> <td class="addr"><a href="#addr-B194">B194</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B1AE" data-tip="&B1AE">recalc_flags_from_base</a></span> <span class="comment">; Not at EOF: skip EXT update</span></td> </tr> <tr id="addr-B196"> <td class="addr"><a href="#addr-B196">B196</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; At EOF: set EXT = PTR</span></td> </tr> <tr id="addr-B199"> <td class="addr"><a href="#addr-B199">B199</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Copy PTR low to EXT low</span></td> </tr> <tr id="addr-B19C"> <td class="addr"><a href="#addr-B19C">B19C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Copy PTR mid-low to EXT mid-low</span></td> </tr> <tr id="addr-B19F"> <td class="addr"><a href="#addr-B19F">B19F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Store in EXT</span></td> </tr> <tr id="addr-B1A2"> <td class="addr"><a href="#addr-B1A2">B1A2</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Copy PTR mid-high to EXT mid-high</span></td> </tr> <tr id="addr-B1A5"> <td class="addr"><a href="#addr-B1A5">B1A5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Store in EXT</span></td> </tr> <tr id="addr-B1A8"> <td class="addr"><a href="#addr-B1A8">B1A8</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Copy PTR high to EXT high</span></td> </tr> <tr id="addr-B1AB"> <td class="addr"><a href="#addr-B1AB">B1AB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Store in EXT</span></td> </tr> <tr id="addr-B1AE"> <td class="addr"><a href="#addr-B1AE">B1AE</a></td> <td><span class="label">.recalc_flags_from_base<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B194">← B194 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore flags from stack</span></td> </tr> <tr id="addr-B1AF"> <td class="addr"><a href="#addr-B1AF">B1AF</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; Keep only writable+open bits</span></td> </tr> <tr id="addr-B1B1"> <td class="addr"><a href="#addr-B1B1">B1B1</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B188" data-tip="&B188">store_channel_flags</a></span> <span class="comment">; Non-zero: store flags</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B1B3"> <td colspan="2"><div class="sub-header"> <h3>*CLOSE command handler</h3> <div class="sub-desc"><p>Close all open files on all drives.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B1B3">B1B3</a></td> <td><span class="label">.star_close<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A0CB">← A0CB JSR</a><a href="#addr-A344">← A344 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: OSFIND close function</span></td> </tr> <tr id="addr-B1B5"> <td class="addr"><a href="#addr-B1B5">B1B5</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Y=0: close all files</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B1B6"> <td colspan="2"><div class="sub-header"> <h3>OSFIND handler</h3> <div class="sub-desc"><p>Handle OSFIND calls to open and close files for byte access.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B1B6">B1B6</a></td> <td><span class="label">.osfind_handler<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-A12C">← A12C JSR</a><a href="#addr-A3DC">← A3DC JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save registers for later restore</span></td> </tr> <tr id="addr-B1B9"> <td class="addr"><a href="#addr-B1B9">B1B9</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span></span> <span class="comment">; Save X in OSFILE block as filename</span></td> </tr> <tr id="addr-B1BC"> <td class="addr"><a href="#addr-B1BC">B1BC</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Filename pointer low = X</span></td> </tr> <tr id="addr-B1BE"> <td class="addr"><a href="#addr-B1BE">B1BE</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C5">zp_osfind_x</span></span> <span class="comment">; Save Y for close channel</span></td> </tr> <tr id="addr-B1C0"> <td class="addr"><a href="#addr-B1C0">B1C0</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00C4">zp_osfind_y</span></span> <span class="comment">; Y also to OSFILE block + filename hi</span></td> </tr> <tr id="addr-B1C2"> <td class="addr"><a href="#addr-B1C2">B1C2</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&1041">wksp_osfile_block_1</span></span> <span class="comment">; Store filename high in OSFILE blk</span></td> </tr> <tr id="addr-B1C5"> <td class="addr"><a href="#addr-B1C5">B1C5</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Filename pointer high = Y</span></td> </tr> <tr id="addr-B1C7"> <td class="addr"><a href="#addr-B1C7">B1C7</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; Isolate open mode (bits 6-7)</span></td> </tr> <tr id="addr-B1C9"> <td class="addr"><a href="#addr-B1C9">B1C9</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: clear current channel</span></td> </tr> <tr id="addr-B1CB"> <td class="addr"><a href="#addr-B1CB">B1CB</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10D5">wksp_cur_channel</span></span> <span class="comment">; Clear current channel for errors</span></td> </tr> <tr id="addr-B1CE"> <td class="addr"><a href="#addr-B1CE">B1CE</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer mode to Y</span></td> </tr> <tr id="addr-B1CF"> <td class="addr"><a href="#addr-B1CF">B1CF</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B1D4" data-tip="&B1D4">find_empty_channel_slot</a></span> <span class="comment">; A!=0: open file</span></td> </tr> <tr id="addr-B1D1"> <td class="addr"><a href="#addr-B1D1">B1D1</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B383" data-tip="&B383">close_file_handler</a></span> <span class="comment">; A=0: close file(s)</span></td> </tr> <tr id="addr-B1D4"> <td class="addr"><a href="#addr-B1D4">B1D4</a></td> <td><span class="label">.find_empty_channel_slot<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B1CF">← B1CF BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1132">wksp_exec_handle</span></span> <span class="comment">; Check for stored EXEC handle</span></td> </tr> <tr id="addr-B1D7"> <td class="addr"><a href="#addr-B1D7">B1D7</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B1E1" data-tip="&B1E1">store_exec_handle</a></span> <span class="comment">; No stored handle: normal open</span></td> </tr> <tr id="addr-B1D9"> <td class="addr"><a href="#addr-B1D9">B1D9</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear stored EXEC handle</span></td> </tr> <tr id="addr-B1DB"> <td class="addr"><a href="#addr-B1DB">B1DB</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&1132">wksp_exec_handle</span></span> <span class="comment">; Clear stored EXEC handle</span></td> </tr> <tr id="addr-B1DE"> <td class="addr"><a href="#addr-B1DE">B1DE</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Return with stored handle in Y</span></td> </tr> <tr id="addr-B1E0"> <td class="addr"><a href="#addr-B1E0">B1E0</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return with stored handle</span></td> </tr> <tr id="addr-B1E1"> <td class="addr"><a href="#addr-B1E1">B1E1</a></td> <td><span class="label">.store_exec_handle<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B1D7">← B1D7 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: scan channels for empty slot</span></td> </tr> <tr id="addr-B1E3"> <td class="addr"><a href="#addr-B1E3">B1E3</a></td> <td><span class="label">.scan_channels_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B1E9">← B1E9 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-B1E6"> <td class="addr"><a href="#addr-B1E6">B1E6</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B203" data-tip="&B203">open_for_read_channel</a></span> <span class="comment">; Flags=0: channel is free</span></td> </tr> <tr id="addr-B1E8"> <td class="addr"><a href="#addr-B1E8">B1E8</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Try next channel</span></td> </tr> <tr id="addr-B1E9"> <td class="addr"><a href="#addr-B1E9">B1E9</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B1E3" data-tip="&B1E3">scan_channels_loop</a></span> <span class="comment">; Loop for all 10 channels</span></td> </tr> <tr id="addr-B1EB"> <td class="addr"><a href="#addr-B1EB">B1EB</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-B1EE"> <td class="addr"><a href="#addr-B1EE">B1EE</a></td> <td> <span class="directive">EQUB</span> <span data-tip="192 &C0 %11000000">&C0</span> <span class="comment">; Error &C0: Too many open</span></td> </tr> <tr id="addr-B1EF"> <td class="addr"><a href="#addr-B1EF">B1EF</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Too many open files"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr id="addr-B203"> <td class="addr"><a href="#addr-B203">B203</a></td> <td><span class="label">.open_for_read_channel<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B1E6">← B1E6 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Store channel index in zp_cf</span></td> </tr> <tr id="addr-B205"> <td class="addr"><a href="#addr-B205">B205</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10A0">wksp_ch_buf_sector</span></span> <span class="comment">; Save open mode (Y) in workspace</span></td> </tr> <tr id="addr-B208"> <td class="addr"><a href="#addr-B208">B208</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer mode to A</span></td> </tr> <tr id="addr-B209"> <td class="addr"><a href="#addr-B209">B209</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B20E" data-tip="&B20E">search_for_input_file</a></span> <span class="comment">; Bit 7 set: open for output/random</span></td> </tr> <tr id="addr-B20B"> <td class="addr"><a href="#addr-B20B">B20B</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B2E1" data-tip="&B2E1">check_random_access_mode</a></span> <span class="comment">; Output/random: jump to write path</span></td> </tr> <tr id="addr-B20E"> <td class="addr"><a href="#addr-B20E">B20E</a></td> <td><span class="label">.search_for_input_file<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B209">← B209 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FDF" data-tip="&8FDF – Find first matching directory entry">find_first_matching_entry</a></span> <span class="comment">; Open for input: search for file</span></td> </tr> <tr id="addr-B211"> <td class="addr"><a href="#addr-B211">B211</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B218" data-tip="&B218">check_read_conflicts</a></span> <span class="comment">; Found?</span></td> </tr> <tr id="addr-B213"> <td class="addr"><a href="#addr-B213">B213</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Not found: A=0 (no handle)</span></td> </tr> <tr id="addr-B215"> <td class="addr"><a href="#addr-B215">B215</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B2D9" data-tip="&B2D9">save_and_return_handle</a></span> <span class="comment">; Return with A=0</span></td> </tr> <tr id="addr-B218"> <td class="addr"><a href="#addr-B218">B218</a></td> <td><span class="label">.check_read_conflicts<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B211">← B211 BEQ</a><a href="#addr-B2FB">← B2FB JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: check all channels for conflict</span></td> </tr> <tr id="addr-B21A"> <td class="addr"><a href="#addr-B21A">B21A</a></td> <td><span class="label">.check_open_conflict_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B24E">← B24E BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-B21D"> <td class="addr"><a href="#addr-B21D">B21D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B24D" data-tip="&B24D – Continue open-channel conflict scan">next_conflict_check</a></span> <span class="comment">; Bit 7 clear: channel not active</span></td> </tr> <tr id="addr-B21F"> <td class="addr"><a href="#addr-B21F">B21F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel drive number</span></td> </tr> <tr id="addr-B222"> <td class="addr"><a href="#addr-B222">B222</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="224 &E0 %11100000">#&e0</span></span> <span class="comment">; Isolate drive bits</span></td> </tr> <tr id="addr-B224"> <td class="addr"><a href="#addr-B224">B224</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Same drive as file being opened?</span></td> </tr> <tr id="addr-B227"> <td class="addr"><a href="#addr-B227">B227</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B24D" data-tip="&B24D – Continue open-channel conflict scan">next_conflict_check</a></span> <span class="comment">; Different drive: no conflict</span></td> </tr> <tr id="addr-B229"> <td class="addr"><a href="#addr-B229">B229</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11E8">wksp_ch_dir_sec_ml</span>,x</span> <span class="comment">; Compare sector address bytes</span></td> </tr> <tr id="addr-B22C"> <td class="addr"><a href="#addr-B22C">B22C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span></span> <span class="comment">; Compare with file's sector low</span></td> </tr> <tr id="addr-B22F"> <td class="addr"><a href="#addr-B22F">B22F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B24D" data-tip="&B24D – Continue open-channel conflict scan">next_conflict_check</a></span> <span class="comment">; No match: no conflict</span></td> </tr> <tr id="addr-B231"> <td class="addr"><a href="#addr-B231">B231</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11DE">wksp_ch_dir_sec_mh</span>,x</span> <span class="comment">; Compare sector mid</span></td> </tr> <tr id="addr-B234"> <td class="addr"><a href="#addr-B234">B234</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1115">wksp_csd_sector_mid</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-B237"> <td class="addr"><a href="#addr-B237">B237</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B24D" data-tip="&B24D – Continue open-channel conflict scan">next_conflict_check</a></span> <span class="comment">; No match: no conflict</span></td> </tr> <tr id="addr-B239"> <td class="addr"><a href="#addr-B239">B239</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11D4">wksp_ch_dir_sec_h</span>,x</span> <span class="comment">; Compare sector high</span></td> </tr> <tr id="addr-B23C"> <td class="addr"><a href="#addr-B23C">B23C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1116">wksp_csd_sector_hi</span></span> <span class="comment">; Match?</span></td> </tr> <tr id="addr-B23F"> <td class="addr"><a href="#addr-B23F">B23F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B24D" data-tip="&B24D – Continue open-channel conflict scan">next_conflict_check</a></span> <span class="comment">; No match: no conflict</span></td> </tr> <tr id="addr-B241"> <td class="addr"><a href="#addr-B241">B241</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="25 &19 %00011001 EM">#&19</span></span> <span class="comment">; Y=&19: compare sequence number</span></td> </tr> <tr id="addr-B243"> <td class="addr"><a href="#addr-B243">B243</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get entry's sequence number</span></td> </tr> <tr id="addr-B245"> <td class="addr"><a href="#addr-B245">B245</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&11F2">wksp_ch_seq_num</span>,x</span> <span class="comment">; Compare with channel's sequence</span></td> </tr> <tr id="addr-B248"> <td class="addr"><a href="#addr-B248">B248</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B24D" data-tip="&B24D – Continue open-channel conflict scan">next_conflict_check</a></span> <span class="comment">; Mismatch: not the same file</span></td> </tr> <tr id="addr-B24A"> <td class="addr"><a href="#addr-B24A">B24A</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8D53" data-tip="&8D53">channel_on_same_drive</a></span> <span class="comment">; Match: Already open error</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B24D"> <td colspan="2"><div class="sub-header"> <h3>Continue open-channel conflict scan</h3> <div class="sub-desc"><p>Advance to next channel and continue scanning for files that conflict with the file being opened.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B24D">B24D</a></td> <td><span class="label">.next_conflict_check<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-B21D">← B21D BPL</a><a href="#addr-B227">← B227 BNE</a><a href="#addr-B22F">← B22F BNE</a><a href="#addr-B237">← B237 BNE</a><a href="#addr-B23F">← B23F BNE</a><a href="#addr-B248">← B248 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next channel</span></td> </tr> <tr id="addr-B24E"> <td class="addr"><a href="#addr-B24E">B24E</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B21A" data-tip="&B21A">check_open_conflict_loop</a></span> <span class="comment">; Loop for all 10 channels</span></td> </tr> <tr id="addr-B250"> <td class="addr"><a href="#addr-B250">B250</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check entry first byte</span></td> </tr> <tr id="addr-B252"> <td class="addr"><a href="#addr-B252">B252</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get first name byte</span></td> </tr> <tr id="addr-B254"> <td class="addr"><a href="#addr-B254">B254</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B259" data-tip="&B259">copy_ext_from_entry</a></span> <span class="comment">; Bit 7 set: has attributes, open it</span></td> </tr> <tr id="addr-B256"> <td class="addr"><a href="#addr-B256">B256</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8BF0" data-tip="&8BF0">validate_found_entry</a></span> <span class="comment">; No attributes: access violation</span></td> </tr> <tr id="addr-B259"> <td class="addr"><a href="#addr-B259">B259</a></td> <td><span class="label">.copy_ext_from_entry<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B254">← B254 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Y=&12: entry length (4 bytes)</span></td> </tr> <tr id="addr-B25B"> <td class="addr"><a href="#addr-B25B">B25B</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B25D"> <td class="addr"><a href="#addr-B25D">B25D</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get length low from entry</span></td> </tr> <tr id="addr-B25F"> <td class="addr"><a href="#addr-B25F">B25F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Store as channel EXT low</span></td> </tr> <tr id="addr-B262"> <td class="addr"><a href="#addr-B262">B262</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&13: length mid-low</span></td> </tr> <tr id="addr-B263"> <td class="addr"><a href="#addr-B263">B263</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get length mid-low</span></td> </tr> <tr id="addr-B265"> <td class="addr"><a href="#addr-B265">B265</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Store as channel EXT mid-low</span></td> </tr> <tr id="addr-B268"> <td class="addr"><a href="#addr-B268">B268</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&14: length mid-high</span></td> </tr> <tr id="addr-B269"> <td class="addr"><a href="#addr-B269">B269</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get length mid-high</span></td> </tr> <tr id="addr-B26B"> <td class="addr"><a href="#addr-B26B">B26B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Store as channel EXT mid-high</span></td> </tr> <tr id="addr-B26E"> <td class="addr"><a href="#addr-B26E">B26E</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&15: length high</span></td> </tr> <tr id="addr-B26F"> <td class="addr"><a href="#addr-B26F">B26F</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get length high</span></td> </tr> <tr id="addr-B271"> <td class="addr"><a href="#addr-B271">B271</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Store as channel EXT high</span></td> </tr> <tr id="addr-B274"> <td class="addr"><a href="#addr-B274">B274</a></td> <td><span class="label">.copy_allocation_from_entry<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B380">← B380 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Y=&12: allocation size (4 bytes)</span></td> </tr> <tr id="addr-B276"> <td class="addr"><a href="#addr-B276">B276</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B278"> <td class="addr"><a href="#addr-B278">B278</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get allocation low from entry</span></td> </tr> <tr id="addr-B27A"> <td class="addr"><a href="#addr-B27A">B27A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11A2">wksp_ch_alloc_l</span>,x</span> <span class="comment">; Store as channel allocation low</span></td> </tr> <tr id="addr-B27D"> <td class="addr"><a href="#addr-B27D">B27D</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&13</span></td> </tr> <tr id="addr-B27E"> <td class="addr"><a href="#addr-B27E">B27E</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get allocation mid-low</span></td> </tr> <tr id="addr-B280"> <td class="addr"><a href="#addr-B280">B280</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; Store as channel alloc mid-low</span></td> </tr> <tr id="addr-B283"> <td class="addr"><a href="#addr-B283">B283</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&14</span></td> </tr> <tr id="addr-B284"> <td class="addr"><a href="#addr-B284">B284</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get allocation mid-high</span></td> </tr> <tr id="addr-B286"> <td class="addr"><a href="#addr-B286">B286</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; Store as channel alloc mid-high</span></td> </tr> <tr id="addr-B289"> <td class="addr"><a href="#addr-B289">B289</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&15</span></td> </tr> <tr id="addr-B28A"> <td class="addr"><a href="#addr-B28A">B28A</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get allocation high</span></td> </tr> <tr id="addr-B28C"> <td class="addr"><a href="#addr-B28C">B28C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; Store as channel alloc high</span></td> </tr> <tr id="addr-B28F"> <td class="addr"><a href="#addr-B28F">B28F</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&16: start sector (3 bytes)</span></td> </tr> <tr id="addr-B290"> <td class="addr"><a href="#addr-B290">B290</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get start sector low</span></td> </tr> <tr id="addr-B292"> <td class="addr"><a href="#addr-B292">B292</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Store as channel start sector low</span></td> </tr> <tr id="addr-B295"> <td class="addr"><a href="#addr-B295">B295</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&17: start sector mid</span></td> </tr> <tr id="addr-B296"> <td class="addr"><a href="#addr-B296">B296</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get start sector mid</span></td> </tr> <tr id="addr-B298"> <td class="addr"><a href="#addr-B298">B298</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Store as channel start sector mid</span></td> </tr> <tr id="addr-B29B"> <td class="addr"><a href="#addr-B29B">B29B</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&18: start sector high</span></td> </tr> <tr id="addr-B29C"> <td class="addr"><a href="#addr-B29C">B29C</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get start sector high from entry</span></td> </tr> <tr id="addr-B29E"> <td class="addr"><a href="#addr-B29E">B29E</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; OR with current drive number</span></td> </tr> <tr id="addr-B2A1"> <td class="addr"><a href="#addr-B2A1">B2A1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Store as channel start+drive</span></td> </tr> <tr id="addr-B2A4"> <td class="addr"><a href="#addr-B2A4">B2A4</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&19: sequence number</span></td> </tr> <tr id="addr-B2A5"> <td class="addr"><a href="#addr-B2A5">B2A5</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get sequence number</span></td> </tr> <tr id="addr-B2A7"> <td class="addr"><a href="#addr-B2A7">B2A7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11F2">wksp_ch_seq_num</span>,x</span> <span class="comment">; Store for channel</span></td> </tr> <tr id="addr-B2AA"> <td class="addr"><a href="#addr-B2AA">B2AA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1114">wksp_csd_sector_lo</span></span> <span class="comment">; Get parent dir sector low</span></td> </tr> <tr id="addr-B2AD"> <td class="addr"><a href="#addr-B2AD">B2AD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11E8">wksp_ch_dir_sec_ml</span>,x</span> <span class="comment">; Store for channel</span></td> </tr> <tr id="addr-B2B0"> <td class="addr"><a href="#addr-B2B0">B2B0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1115">wksp_csd_sector_mid</span></span> <span class="comment">; Get parent dir sector mid</span></td> </tr> <tr id="addr-B2B3"> <td class="addr"><a href="#addr-B2B3">B2B3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11DE">wksp_ch_dir_sec_mh</span>,x</span> <span class="comment">; Store for channel</span></td> </tr> <tr id="addr-B2B6"> <td class="addr"><a href="#addr-B2B6">B2B6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1116">wksp_csd_sector_hi</span></span> <span class="comment">; Get parent dir sector high</span></td> </tr> <tr id="addr-B2B9"> <td class="addr"><a href="#addr-B2B9">B2B9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11D4">wksp_ch_dir_sec_h</span>,x</span> <span class="comment">; Store for channel</span></td> </tr> <tr id="addr-B2BC"> <td class="addr"><a href="#addr-B2BC">B2BC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: set PTR to start of file</span></td> </tr> <tr id="addr-B2BE"> <td class="addr"><a href="#addr-B2BE">B2BE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Clear PTR low</span></td> </tr> <tr id="addr-B2C1"> <td class="addr"><a href="#addr-B2C1">B2C1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Clear PTR mid-low</span></td> </tr> <tr id="addr-B2C4"> <td class="addr"><a href="#addr-B2C4">B2C4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Clear PTR mid-high</span></td> </tr> <tr id="addr-B2C7"> <td class="addr"><a href="#addr-B2C7">B2C7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Clear PTR high</span></td> </tr> <tr id="addr-B2CA"> <td class="addr"><a href="#addr-B2CA">B2CA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10A0">wksp_ch_buf_sector</span></span> <span class="comment">; Get open mode flags</span></td> </tr> <tr id="addr-B2CD"> <td class="addr"><a href="#addr-B2CD">B2CD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Store as channel flags</span></td> </tr> <tr id="addr-B2D0"> <td class="addr"><a href="#addr-B2D0">B2D0</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer channel index to A</span></td> </tr> <tr id="addr-B2D1"> <td class="addr"><a href="#addr-B2D1">B2D1</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-B2D2"> <td class="addr"><a href="#addr-B2D2">B2D2</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Add &30 to get file handle</span></td> </tr> <tr id="addr-B2D4"> <td class="addr"><a href="#addr-B2D4">B2D4</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push file handle on stack</span></td> </tr> <tr id="addr-B2D5"> <td class="addr"><a href="#addr-B2D5">B2D5</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B13F" data-tip="&B13F">update_channel_flags_for_ptr</a></span> <span class="comment">; Ensure buffer state is consistent</span></td> </tr> <tr id="addr-B2D8"> <td class="addr"><a href="#addr-B2D8">B2D8</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore file handle</span></td> </tr> <tr id="addr-B2D9"> <td class="addr"><a href="#addr-B2D9">B2D9</a></td> <td><span class="label">.save_and_return_handle<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B215">← B215 JMP</a><a href="#addr-B2ED">← B2ED BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace and return A=handle</span></td> </tr> <tr id="addr-B2DC"> <td class="addr"><a href="#addr-B2DC">B2DC</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C5">zp_osfind_x</span></span> <span class="comment">; Restore X from saved value</span></td> </tr> <tr id="addr-B2DE"> <td class="addr"><a href="#addr-B2DE">B2DE</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C4">zp_osfind_y</span></span> <span class="comment">; Restore Y from saved value</span></td> </tr> <tr id="addr-B2E0"> <td class="addr"><a href="#addr-B2E0">B2E0</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-B2E1"> <td class="addr"><a href="#addr-B2E1">B2E1</a></td> <td><span class="label">.check_random_access_mode<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B20B">← B20B JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&10A0">wksp_ch_buf_sector</span></span> <span class="comment">; Check open mode for random access</span></td> </tr> <tr id="addr-B2E4"> <td class="addr"><a href="#addr-B2E4">B2E4</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-B2FE" data-tip="&B2FE">open_for_output_new</a></span> <span class="comment">; Bit 6 clear: open for output only</span></td> </tr> <tr id="addr-B2E6"> <td class="addr"><a href="#addr-B2E6">B2E6</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FDF" data-tip="&8FDF – Find first matching directory entry">find_first_matching_entry</a></span> <span class="comment">; Random: search for existing file</span></td> </tr> <tr id="addr-B2E9"> <td class="addr"><a href="#addr-B2E9">B2E9</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save search result flags</span></td> </tr> <tr id="addr-B2EA"> <td class="addr"><a href="#addr-B2EA">B2EA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: default no-file result</span></td> </tr> <tr id="addr-B2EC"> <td class="addr"><a href="#addr-B2EC">B2EC</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags from search</span></td> </tr> <tr id="addr-B2ED"> <td class="addr"><a href="#addr-B2ED">B2ED</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B2D9" data-tip="&B2D9">save_and_return_handle</a></span> <span class="comment">; Not found: return A=0</span></td> </tr> <tr id="addr-B2EF"> <td class="addr"><a href="#addr-B2EF">B2EF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8D21" data-tip="&8D21 – Check if file is open">check_open</a></span></td> </tr> <tr id="addr-B2F2"> <td class="addr"><a href="#addr-B2F2">B2F2</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: check first name byte</span></td> </tr> <tr id="addr-B2F4"> <td class="addr"><a href="#addr-B2F4">B2F4</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get name byte</span></td> </tr> <tr id="addr-B2F6"> <td class="addr"><a href="#addr-B2F6">B2F6</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B2FB" data-tip="&B2FB">open_for_random_access</a></span> <span class="comment">; Bit 7 set: has attributes</span></td> </tr> <tr id="addr-B2F8"> <td class="addr"><a href="#addr-B2F8">B2F8</a></td> <td><span class="label">.check_random_access_attr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B30D">← B30D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8BF0" data-tip="&8BF0">validate_found_entry</a></span> <span class="comment">; No attributes: access violation</span></td> </tr> <tr id="addr-B2FB"> <td class="addr"><a href="#addr-B2FB">B2FB</a></td> <td><span class="label">.open_for_random_access<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B2F6">← B2F6 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B218" data-tip="&B218">check_read_conflicts</a></span> <span class="comment">; Jump to check for open conflicts</span></td> </tr> <tr id="addr-B2FE"> <td class="addr"><a href="#addr-B2FE">B2FE</a></td> <td><span class="label">.open_for_output_new<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B2E4">← B2E4 BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8DBD" data-tip="&8DBD">set_up_gsinit_path</a></span> <span class="comment">; Parse destination path</span></td> </tr> <tr id="addr-B301"> <td class="addr"><a href="#addr-B301">B301</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FDF" data-tip="&8FDF – Find first matching directory entry">find_first_matching_entry</a></span> <span class="comment">; Search for existing file</span></td> </tr> <tr id="addr-B304"> <td class="addr"><a href="#addr-B304">B304</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B312" data-tip="&B312">clear_new_file_osfile</a></span> <span class="comment">; Not found: create new</span></td> </tr> <tr id="addr-B306"> <td class="addr"><a href="#addr-B306">B306</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8D10" data-tip="&8D10 – Check file is not locked or open">check_file_not_open</a></span> <span class="comment">; Found: check it's not open</span></td> </tr> <tr id="addr-B309"> <td class="addr"><a href="#addr-B309">B309</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: check access byte</span></td> </tr> <tr id="addr-B30B"> <td class="addr"><a href="#addr-B30B">B30B</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B6">zp_entry_ptr_lo</span>),y</span> <span class="comment">; Get first name byte</span></td> </tr> <tr id="addr-B30D"> <td class="addr"><a href="#addr-B30D">B30D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B2F8" data-tip="&B2F8">check_random_access_attr</a></span> <span class="comment">; Bit 7 clear: access violation</span></td> </tr> <tr id="addr-B30F"> <td class="addr"><a href="#addr-B30F">B30F</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B370" data-tip="&B370">set_ext_zero_for_new</a></span> <span class="comment">; Jump to open with existing entry</span></td> </tr> <tr id="addr-B312"> <td class="addr"><a href="#addr-B312">B312</a></td> <td><span class="label">.clear_new_file_osfile<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B304">← B304 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear OSFILE block</span></td> </tr> <tr id="addr-B314"> <td class="addr"><a href="#addr-B314">B314</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="15 &0F %00001111 SI">#&0f</span></span> <span class="comment">; X=&0F: clear 16 bytes</span></td> </tr> <tr id="addr-B316"> <td class="addr"><a href="#addr-B316">B316</a></td> <td><span class="label">.clear_osfile_block_loop2<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B31A">← B31A BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1042">wksp_osfile_load_addr</span>,x</span> <span class="comment">; Clear OSFILE block byte</span></td> </tr> <tr id="addr-B319"> <td class="addr"><a href="#addr-B319">B319</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B31A"> <td class="addr"><a href="#addr-B31A">B31A</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B316" data-tip="&B316">clear_osfile_block_loop2</a></span> <span class="comment">; Loop for 16 bytes</span></td> </tr> <tr id="addr-B31C"> <td class="addr"><a href="#addr-B31C">B31C</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&0FFE">fsm_s1_end_of_list_ptr</span></span> <span class="comment">; Get FSM end-of-list pointer</span></td> </tr> <tr id="addr-B31F"> <td class="addr"><a href="#addr-B31F">B31F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: initial max size = 0</span></td> </tr> <tr id="addr-B321"> <td class="addr"><a href="#addr-B321">B321</a></td> <td><span class="label">.find_best_free_space_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B335">← B335 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0EFE">fsm_s0_disc_size_hi</span>,x</span> <span class="comment">; OR FSM entry address bytes</span></td> </tr> <tr id="addr-B324"> <td class="addr"><a href="#addr-B324">B324</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0EFF">fsm_s0_checksum</span>,x</span> <span class="comment">; Continue OR-ing</span></td> </tr> <tr id="addr-B327"> <td class="addr"><a href="#addr-B327">B327</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&0EFD">fsm_s0_disc_size_mid</span>,x</span> <span class="comment">; Get FSM entry length</span></td> </tr> <tr id="addr-B32A"> <td class="addr"><a href="#addr-B32A">B32A</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="ext-label" data-tip="&104F">wksp_osfile_end_addr_1</span></span> <span class="comment">; Compare with current max</span></td> </tr> <tr id="addr-B32D"> <td class="addr"><a href="#addr-B32D">B32D</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B332" data-tip="&B332">store_default_allocation</a></span> <span class="comment">; Smaller: skip</span></td> </tr> <tr id="addr-B32F"> <td class="addr"><a href="#addr-B32F">B32F</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&104F">wksp_osfile_end_addr_1</span></span> <span class="comment">; Larger: update max</span></td> </tr> <tr id="addr-B332"> <td class="addr"><a href="#addr-B332">B332</a></td> <td><span class="label">.store_default_allocation<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B32D">← B32D BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Back up 3 bytes to prev entry</span></td> </tr> <tr id="addr-B333"> <td class="addr"><a href="#addr-B333">B333</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue</span></td> </tr> <tr id="addr-B334"> <td class="addr"><a href="#addr-B334">B334</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue</span></td> </tr> <tr id="addr-B335"> <td class="addr"><a href="#addr-B335">B335</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B321" data-tip="&B321">find_best_free_space_loop</a></span> <span class="comment">; Loop for all entries</span></td> </tr> <tr id="addr-B337"> <td class="addr"><a href="#addr-B337">B337</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer A to Y (non-zero check)</span></td> </tr> <tr id="addr-B338"> <td class="addr"><a href="#addr-B338">B338</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B341" data-tip="&B341">set_ffffffff_load_addr</a></span> <span class="comment">; Zero: no free space at all</span></td> </tr> <tr id="addr-B33A"> <td class="addr"><a href="#addr-B33A">B33A</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&104F">wksp_osfile_end_addr_1</span></span> <span class="comment">; Store 0 as max (will use default)</span></td> </tr> <tr id="addr-B33D"> <td class="addr"><a href="#addr-B33D">B33D</a></td> <td> <span class="opcode">INX</span> <span class="comment">; X=1</span></td> </tr> <tr id="addr-B33E"> <td class="addr"><a href="#addr-B33E">B33E</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&1050">wksp_osfile_end_addr_2</span></span> <span class="comment">; Store default allocation</span></td> </tr> <tr id="addr-B341"> <td class="addr"><a href="#addr-B341">B341</a></td> <td><span class="label">.set_ffffffff_load_addr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B338">← B338 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A=&FF: fill OSFILE block</span></td> </tr> <tr id="addr-B343"> <td class="addr"><a href="#addr-B343">B343</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1046">wksp_osfile_exec_addr</span></span> <span class="comment">; Set load addr to &FFFFFFFF</span></td> </tr> <tr id="addr-B346"> <td class="addr"><a href="#addr-B346">B346</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1047">wksp_osfile_exec_addr_1</span></span> <span class="comment">; Second byte</span></td> </tr> <tr id="addr-B349"> <td class="addr"><a href="#addr-B349">B349</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1048">wksp_osfile_exec_addr_2</span></span> <span class="comment">; Third byte</span></td> </tr> <tr id="addr-B34C"> <td class="addr"><a href="#addr-B34C">B34C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1049">wksp_osfile_exec_addr_3</span></span> <span class="comment">; Fourth byte</span></td> </tr> <tr id="addr-B34F"> <td class="addr"><a href="#addr-B34F">B34F</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; X=&40: OSFILE block offset</span></td> </tr> <tr id="addr-B351"> <td class="addr"><a href="#addr-B351">B351</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span></span> <span class="comment">; Store block pointer low</span></td> </tr> <tr id="addr-B353"> <td class="addr"><a href="#addr-B353">B353</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: OSFILE block page</span></td> </tr> <tr id="addr-B355"> <td class="addr"><a href="#addr-B355">B355</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00B9">zp_osfile_ptr_hi</span></span> <span class="comment">; Store block pointer high</span></td> </tr> <tr id="addr-B357"> <td class="addr"><a href="#addr-B357">B357</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace</span></td> </tr> <tr id="addr-B35A"> <td class="addr"><a href="#addr-B35A">B35A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F4C" data-tip="&8F4C – Validate file is not locked then create entry">validate_not_locked</a></span> <span class="comment">; Create directory entry for new file</span></td> </tr> <tr id="addr-B35D"> <td class="addr"><a href="#addr-B35D">B35D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write directory to disc</span></td> </tr> <tr id="addr-B360"> <td class="addr"><a href="#addr-B360">B360</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D0" data-tip="&89D0 – Load object type and save workspace">get_object_type_result</a></span> <span class="comment">; Save workspace after dir write</span></td> </tr> <tr id="addr-B363"> <td class="addr"><a href="#addr-B363">B363</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span></span> <span class="comment">; Restore original filename pointer</span></td> </tr> <tr id="addr-B366"> <td class="addr"><a href="#addr-B366">B366</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store in (&B4)</span></td> </tr> <tr id="addr-B368"> <td class="addr"><a href="#addr-B368">B368</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1041">wksp_osfile_block_1</span></span> <span class="comment">; Get filename high byte</span></td> </tr> <tr id="addr-B36B"> <td class="addr"><a href="#addr-B36B">B36B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store in (&B5)</span></td> </tr> <tr id="addr-B36D"> <td class="addr"><a href="#addr-B36D">B36D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8FDF" data-tip="&8FDF – Find first matching directory entry">find_first_matching_entry</a></span> <span class="comment">; Search for newly created entry</span></td> </tr> <tr id="addr-B370"> <td class="addr"><a href="#addr-B370">B370</a></td> <td><span class="label">.set_ext_zero_for_new<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B30F">← B30F JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: new file has zero length</span></td> </tr> <tr id="addr-B372"> <td class="addr"><a href="#addr-B372">B372</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B374"> <td class="addr"><a href="#addr-B374">B374</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Set EXT low = 0</span></td> </tr> <tr id="addr-B377"> <td class="addr"><a href="#addr-B377">B377</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Set EXT mid-low = 0</span></td> </tr> <tr id="addr-B37A"> <td class="addr"><a href="#addr-B37A">B37A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Set EXT mid-high = 0</span></td> </tr> <tr id="addr-B37D"> <td class="addr"><a href="#addr-B37D">B37D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Set EXT high = 0</span></td> </tr> <tr id="addr-B380"> <td class="addr"><a href="#addr-B380">B380</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B274" data-tip="&B274">copy_allocation_from_entry</a></span> <span class="comment">; Jump to copy allocation and finish</span></td> </tr> <tr id="addr-B383"> <td class="addr"><a href="#addr-B383">B383</a></td> <td><span class="label">.close_file_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B1D1">← B1D1 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C4">zp_osfind_y</span></span> <span class="comment">; Get channel number (Y) from saved</span></td> </tr> <tr id="addr-B385"> <td class="addr"><a href="#addr-B385">B385</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B3B3" data-tip="&B3B3">close_all_complete</a></span> <span class="comment">; Y non-zero: close specific channel</span></td> </tr> <tr id="addr-B387"> <td class="addr"><a href="#addr-B387">B387</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Y=0: close all - save X first</span></td> </tr> <tr id="addr-B388"> <td class="addr"><a href="#addr-B388">B388</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push X on stack</span></td> </tr> <tr id="addr-B389"> <td class="addr"><a href="#addr-B389">B389</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="119 &77 %01110111 'w'">#osbyte_close_spool_exec</span></span> <span class="comment">; OSBYTE &77: close SPOOL and EXEC</span></td> </tr> <tr id="addr-B38B"> <td class="addr"><a href="#addr-B38B">B38B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Close any SPOOLed or EXECed files</span></td> </tr> <tr id="addr-B38E"> <td class="addr"><a href="#addr-B38E">B38E</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-B38F"> <td class="addr"><a href="#addr-B38F">B38F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00C5">zp_osfind_x</span></span> <span class="comment">; Store as saved X</span></td> </tr> <tr id="addr-B391"> <td class="addr"><a href="#addr-B391">B391</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: scan all channels</span></td> </tr> <tr id="addr-B393"> <td class="addr"><a href="#addr-B393">B393</a></td> <td><span class="label">.close_all_scan_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B399">← B399 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-B396"> <td class="addr"><a href="#addr-B396">B396</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B3A4" data-tip="&B3A4">close_single_channel</a></span> <span class="comment">; Flags=0: not open, skip</span></td> </tr> <tr id="addr-B398"> <td class="addr"><a href="#addr-B398">B398</a></td> <td><span class="label">.close_next_channel_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B3B1">← B3B1 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next channel</span></td> </tr> <tr id="addr-B399"> <td class="addr"><a href="#addr-B399">B399</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B393" data-tip="&B393">close_all_scan_loop</a></span> <span class="comment">; Loop for all 10 channels</span></td> </tr> <tr id="addr-B39B"> <td class="addr"><a href="#addr-B39B">B39B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8305" data-tip="&8305 – Wait while files are being ensured">wait_ensuring</a></span></td> </tr> <tr id="addr-B39E"> <td class="addr"><a href="#addr-B39E">B39E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: all closed</span></td> </tr> <tr id="addr-B3A0"> <td class="addr"><a href="#addr-B3A0">B3A0</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C5">zp_osfind_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-B3A2"> <td class="addr"><a href="#addr-B3A2">B3A2</a></td> <td> <span class="opcode">TAY</span></td> </tr> <tr id="addr-B3A3"> <td class="addr"><a href="#addr-B3A3">B3A3</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-B3A4"> <td class="addr"><a href="#addr-B3A4">B3A4</a></td> <td><span class="label">.close_single_channel<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B396">← B396 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TXA</span> <span class="comment">; Channel is open: get file handle</span></td> </tr> <tr id="addr-B3A5"> <td class="addr"><a href="#addr-B3A5">B3A5</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-B3A6"> <td class="addr"><a href="#addr-B3A6">B3A6</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Add &30 to get handle</span></td> </tr> <tr id="addr-B3A8"> <td class="addr"><a href="#addr-B3A8">B3A8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store handle</span></td> </tr> <tr id="addr-B3AA"> <td class="addr"><a href="#addr-B3AA">B3AA</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Store channel index</span></td> </tr> <tr id="addr-B3AC"> <td class="addr"><a href="#addr-B3AC">B3AC</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B3B6" data-tip="&B3B6">close_and_update_dir</a></span> <span class="comment">; Close this channel</span></td> </tr> <tr id="addr-B3AF"> <td class="addr"><a href="#addr-B3AF">B3AF</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Restore channel index</span></td> </tr> <tr id="addr-B3B1"> <td class="addr"><a href="#addr-B3B1">B3B1</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B398" data-tip="&B398">close_next_channel_loop</a></span> <span class="comment">; Continue scanning</span></td> </tr> <tr id="addr-B3B3"> <td class="addr"><a href="#addr-B3B3">B3B3</a></td> <td><span class="label">.close_all_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B385">← B385 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACFE" data-tip="&ACFE – Validate and set channel number from Y">check_set_channel_y</a></span></td> </tr> <tr id="addr-B3B6"> <td class="addr"><a href="#addr-B3B6">B3B6</a></td> <td><span class="label">.close_and_update_dir<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B3AC">← B3AC JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B18C" data-tip="&B18C – Synchronise EXT to PTR if at EOF">sync_ext_to_ptr</a></span> <span class="comment">; Flush buffer if dirty</span></td> </tr> <tr id="addr-B3B9"> <td class="addr"><a href="#addr-B3B9">B3B9</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-B3BC"> <td class="addr"><a href="#addr-B3BC">B3BC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear channel flags (closed)</span></td> </tr> <tr id="addr-B3BE"> <td class="addr"><a href="#addr-B3BE">B3BE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Mark channel as closed</span></td> </tr> <tr id="addr-B3C1"> <td class="addr"><a href="#addr-B3C1">B3C1</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer old flags to A</span></td> </tr> <tr id="addr-B3C2"> <td class="addr"><a href="#addr-B3C2">B3C2</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B3E4" data-tip="&B3E4">close_read_only</a></span> <span class="comment">; Bit 7 clear: was read-only</span></td> </tr> <tr id="addr-B3C4"> <td class="addr"><a href="#addr-B3C4">B3C4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Was writable: check if EXT changed</span></td> </tr> <tr id="addr-B3C7"> <td class="addr"><a href="#addr-B3C7">B3C7</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&11A2">wksp_ch_alloc_l</span>,x</span> <span class="comment">; Compare EXT low with allocation low</span></td> </tr> <tr id="addr-B3CA"> <td class="addr"><a href="#addr-B3CA">B3CA</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B3F1" data-tip="&B3F1 – Update directory entry on file close">update_dir_entry_on_close</a></span> <span class="comment">; Different: need to update dir entry</span></td> </tr> <tr id="addr-B3CC"> <td class="addr"><a href="#addr-B3CC">B3CC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Compare EXT mid-low</span></td> </tr> <tr id="addr-B3CF"> <td class="addr"><a href="#addr-B3CF">B3CF</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; With allocation mid-low</span></td> </tr> <tr id="addr-B3D2"> <td class="addr"><a href="#addr-B3D2">B3D2</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B3F1" data-tip="&B3F1 – Update directory entry on file close">update_dir_entry_on_close</a></span> <span class="comment">; Different: update needed</span></td> </tr> <tr id="addr-B3D4"> <td class="addr"><a href="#addr-B3D4">B3D4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Compare EXT mid-high</span></td> </tr> <tr id="addr-B3D7"> <td class="addr"><a href="#addr-B3D7">B3D7</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; With allocation mid-high</span></td> </tr> <tr id="addr-B3DA"> <td class="addr"><a href="#addr-B3DA">B3DA</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B3F1" data-tip="&B3F1 – Update directory entry on file close">update_dir_entry_on_close</a></span> <span class="comment">; Different: update needed</span></td> </tr> <tr id="addr-B3DC"> <td class="addr"><a href="#addr-B3DC">B3DC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Compare EXT high</span></td> </tr> <tr id="addr-B3DF"> <td class="addr"><a href="#addr-B3DF">B3DF</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; With allocation high</span></td> </tr> <tr id="addr-B3E2"> <td class="addr"><a href="#addr-B3E2">B3E2</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B3F1" data-tip="&B3F1 – Update directory entry on file close">update_dir_entry_on_close</a></span> <span class="comment">; Different: update needed</span></td> </tr> <tr id="addr-B3E4"> <td class="addr"><a href="#addr-B3E4">B3E4</a></td> <td><span class="label">.close_read_only<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B3C2">← B3C2 BPL</a><a href="#addr-B465">← B465 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAA6" data-tip="&AAA6 – Flush buffers and set file pointer">validate_and_set_ptr</a></span> <span class="comment">; EXT == alloc: no update needed</span></td> </tr> <tr id="addr-B3E7"> <td class="addr"><a href="#addr-B3E7">B3E7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace</span></td> </tr> <tr id="addr-B3EA"> <td class="addr"><a href="#addr-B3EA">B3EA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: success</span></td> </tr> <tr id="addr-B3EC"> <td class="addr"><a href="#addr-B3EC">B3EC</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C4">zp_osfind_y</span></span> <span class="comment">; Restore Y</span></td> </tr> <tr id="addr-B3EE"> <td class="addr"><a href="#addr-B3EE">B3EE</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C5">zp_osfind_x</span></span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-B3F0"> <td class="addr"><a href="#addr-B3F0">B3F0</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B3F1"> <td colspan="2"><div class="sub-header"> <h3>Update directory entry on file close</h3> <div class="sub-desc"><p>Switch to the file's drive, calculate actual sectors used from EXT, then release unused allocation back to the free space map.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B3F1">B3F1</a></td> <td><span class="label">.update_dir_entry_on_close<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-B3CA">← B3CA BNE</a><a href="#addr-B3D2">← B3D2 BNE</a><a href="#addr-B3DA">← B3DA BNE</a><a href="#addr-B3E2">← B3E2 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ADC5" data-tip="&ADC5 – Switch to channel's drive for I/O">switch_to_channel_drive</a></span> <span class="comment">; Switch to file's drive</span></td> </tr> <tr id="addr-B3F4"> <td class="addr"><a href="#addr-B3F4">B3F4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Calculate sectors used from EXT</span></td> </tr> <tr id="addr-B3F7"> <td class="addr"><a href="#addr-B3F7">B3F7</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Compare low byte with 1</span></td> </tr> <tr id="addr-B3F9"> <td class="addr"><a href="#addr-B3F9">B3F9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Get object sector low</span></td> </tr> <tr id="addr-B3FC"> <td class="addr"><a href="#addr-B3FC">B3FC</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Add EXT mid-low + carry</span></td> </tr> <tr id="addr-B3FF"> <td class="addr"><a href="#addr-B3FF">B3FF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1034">wksp_object_sector</span></span> <span class="comment">; Store updated sector low</span></td> </tr> <tr id="addr-B402"> <td class="addr"><a href="#addr-B402">B402</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Get sector mid</span></td> </tr> <tr id="addr-B405"> <td class="addr"><a href="#addr-B405">B405</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Add EXT mid-high + carry</span></td> </tr> <tr id="addr-B408"> <td class="addr"><a href="#addr-B408">B408</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1035">wksp_object_sector_mid</span></span> <span class="comment">; Store updated sector mid</span></td> </tr> <tr id="addr-B40B"> <td class="addr"><a href="#addr-B40B">B40B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Get sector high</span></td> </tr> <tr id="addr-B40E"> <td class="addr"><a href="#addr-B40E">B40E</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Add EXT high + carry</span></td> </tr> <tr id="addr-B411"> <td class="addr"><a href="#addr-B411">B411</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1036">wksp_object_sector_hi</span></span> <span class="comment">; Store updated sector high</span></td> </tr> <tr id="addr-B414"> <td class="addr"><a href="#addr-B414">B414</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11A2">wksp_ch_alloc_l</span>,x</span> <span class="comment">; Calculate unused sectors to release</span></td> </tr> <tr id="addr-B417"> <td class="addr"><a href="#addr-B417">B417</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Compare alloc low with 1</span></td> </tr> <tr id="addr-B419"> <td class="addr"><a href="#addr-B419">B419</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1198">wksp_ch_alloc_ml</span>,x</span> <span class="comment">; Get alloc mid-low</span></td> </tr> <tr id="addr-B41C"> <td class="addr"><a href="#addr-B41C">B41C</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Subtract EXT mid-low</span></td> </tr> <tr id="addr-B41F"> <td class="addr"><a href="#addr-B41F">B41F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1037">wksp_object_size</span></span> <span class="comment">; Store unused low</span></td> </tr> <tr id="addr-B422"> <td class="addr"><a href="#addr-B422">B422</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&118E">wksp_ch_alloc_mh</span>,x</span> <span class="comment">; Get alloc mid-high</span></td> </tr> <tr id="addr-B425"> <td class="addr"><a href="#addr-B425">B425</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Subtract EXT mid-high</span></td> </tr> <tr id="addr-B428"> <td class="addr"><a href="#addr-B428">B428</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1038">wksp_object_size_mid</span></span> <span class="comment">; Store unused mid</span></td> </tr> <tr id="addr-B42B"> <td class="addr"><a href="#addr-B42B">B42B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1184">wksp_ch_alloc_h</span>,x</span> <span class="comment">; Get alloc high</span></td> </tr> <tr id="addr-B42E"> <td class="addr"><a href="#addr-B42E">B42E</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Subtract EXT high</span></td> </tr> <tr id="addr-B431"> <td class="addr"><a href="#addr-B431">B431</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1039">wksp_object_size_hi</span></span> <span class="comment">; Store unused high</span></td> </tr> <tr id="addr-B434"> <td class="addr"><a href="#addr-B434">B434</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Check if EXT has fractional sector</span></td> </tr> <tr id="addr-B437"> <td class="addr"><a href="#addr-B437">B437</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B446" data-tip="&B446">update_entry_length</a></span> <span class="comment">; Non-zero: adjust sector count</span></td> </tr> <tr id="addr-B439"> <td class="addr"><a href="#addr-B439">B439</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1037">wksp_object_size</span></span> <span class="comment">; Increment unused sector count</span></td> </tr> <tr id="addr-B43C"> <td class="addr"><a href="#addr-B43C">B43C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B446" data-tip="&B446">update_entry_length</a></span> <span class="comment">; No wrap</span></td> </tr> <tr id="addr-B43E"> <td class="addr"><a href="#addr-B43E">B43E</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1038">wksp_object_size_mid</span></span> <span class="comment">; Wrap: increment mid</span></td> </tr> <tr id="addr-B441"> <td class="addr"><a href="#addr-B441">B441</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B446" data-tip="&B446">update_entry_length</a></span> <span class="comment">; No wrap</span></td> </tr> <tr id="addr-B443"> <td class="addr"><a href="#addr-B443">B443</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&1039">wksp_object_size_hi</span></span> <span class="comment">; Wrap: increment high</span></td> </tr> <tr id="addr-B446"> <td class="addr"><a href="#addr-B446">B446</a></td> <td><span class="label">.update_entry_length<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B437">← B437 BNE</a><a href="#addr-B43C">← B43C BNE</a><a href="#addr-B441">← B441 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Update dir entry with actual length</span></td> </tr> <tr id="addr-B449"> <td class="addr"><a href="#addr-B449">B449</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Y=&12: length field in entry</span></td> </tr> <tr id="addr-B44B"> <td class="addr"><a href="#addr-B44B">B44B</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store EXT low in entry</span></td> </tr> <tr id="addr-B44D"> <td class="addr"><a href="#addr-B44D">B44D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Get EXT mid-low</span></td> </tr> <tr id="addr-B450"> <td class="addr"><a href="#addr-B450">B450</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-B451"> <td class="addr"><a href="#addr-B451">B451</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in entry</span></td> </tr> <tr id="addr-B453"> <td class="addr"><a href="#addr-B453">B453</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Get EXT mid-high</span></td> </tr> <tr id="addr-B456"> <td class="addr"><a href="#addr-B456">B456</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-B457"> <td class="addr"><a href="#addr-B457">B457</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in entry</span></td> </tr> <tr id="addr-B459"> <td class="addr"><a href="#addr-B459">B459</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Get EXT high</span></td> </tr> <tr id="addr-B45C"> <td class="addr"><a href="#addr-B45C">B45C</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-B45D"> <td class="addr"><a href="#addr-B45D">B45D</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B8">zp_osfile_ptr_lo</span>),y</span> <span class="comment">; Store in entry</span></td> </tr> <tr id="addr-B45F"> <td class="addr"><a href="#addr-B45F">B45F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-84B5" data-tip="&84B5 – Release disc space back to free space map">release_disc_space</a></span></td> </tr> <tr id="addr-B462"> <td class="addr"><a href="#addr-B462">B462</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8F86" data-tip="&8F86 – Write directory and FSM back to disc">write_dir_and_validate</a></span> <span class="comment">; Write updated directory to disc</span></td> </tr> <tr id="addr-B465"> <td class="addr"><a href="#addr-B465">B465</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B3E4" data-tip="&B3E4">close_read_only</a></span> <span class="comment">; Jump to release space and return</span></td> </tr> <tr id="addr-B468"> <td class="addr"><a href="#addr-B468">B468</a></td> <td><span class="label">.check_channels_on_drive<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-88B5">← 88B5 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: scan all channels</span></td> </tr> <tr id="addr-B46A"> <td class="addr"><a href="#addr-B46A">B46A</a></td> <td><span class="label">.scan_drive_channels_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B47A">← B47A BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11AC">wksp_ch_flags</span>,x</span> <span class="comment">; Get channel flags</span></td> </tr> <tr id="addr-B46D"> <td class="addr"><a href="#addr-B46D">B46D</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B479" data-tip="&B479">no_channels_on_drive</a></span> <span class="comment">; Not open: skip</span></td> </tr> <tr id="addr-B46F"> <td class="addr"><a href="#addr-B46F">B46F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel drive number</span></td> </tr> <tr id="addr-B472"> <td class="addr"><a href="#addr-B472">B472</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="224 &E0 %11100000">#&e0</span></span> <span class="comment">; Isolate drive bits</span></td> </tr> <tr id="addr-B474"> <td class="addr"><a href="#addr-B474">B474</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Same drive as current?</span></td> </tr> <tr id="addr-B477"> <td class="addr"><a href="#addr-B477">B477</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B48E" data-tip="&B48E">read_clock_then_verify_disc_id</a></span> <span class="comment">; Same drive: found one</span></td> </tr> <tr id="addr-B479"> <td class="addr"><a href="#addr-B479">B479</a></td> <td><span class="label">.no_channels_on_drive<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B46D">← B46D BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next channel</span></td> </tr> <tr id="addr-B47A"> <td class="addr"><a href="#addr-B47A">B47A</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B46A" data-tip="&B46A">scan_drive_channels_loop</a></span> <span class="comment">; Loop for all 10</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B47C"> <td colspan="2"><div class="sub-header"> <h3>Check for disc change via disc ID comparison</h3> <div class="sub-desc"><p>Cache the current disc ID from the FSM into per-drive workspace, read the system clock for timing, then re-read the disc ID and compare with the cached values. If either byte differs, raise a "Disc changed" error. Entry point when no channels are open on the drive; when channels are open, entry is via read_clock_then_verify_disc_id instead.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B47C">B47C</a></td> <td><span class="label">.check_disc_changed<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9C26">← 9C26 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive number</span></td> </tr> <tr id="addr-B47F"> <td class="addr"><a href="#addr-B47F">B47F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B579" data-tip="&B579 – Convert drive number to slot index">convert_drive_to_slot</a></span> <span class="comment">; Get drive slot index</span></td> </tr> <tr id="addr-B482"> <td class="addr"><a href="#addr-B482">B482</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0FFB">fsm_s1_disc_id_lo</span></span> <span class="comment">; Cache disc ID low from FSM</span></td> </tr> <tr id="addr-B485"> <td class="addr"><a href="#addr-B485">B485</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1121">wksp_disc_id_lo</span>,x</span> <span class="comment">; Store in per-drive workspace</span></td> </tr> <tr id="addr-B488"> <td class="addr"><a href="#addr-B488">B488</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0FFC">fsm_s1_disc_id_hi</span></span> <span class="comment">; Cache disc ID high from FSM</span></td> </tr> <tr id="addr-B48B"> <td class="addr"><a href="#addr-B48B">B48B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1122">wksp_disc_id_hi</span>,x</span> <span class="comment">; Store in per-drive workspace</span></td> </tr> <tr id="addr-B48E"> <td class="addr"><a href="#addr-B48E">B48E</a></td> <td><span class="label">.read_clock_then_verify_disc_id<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-AE06">← AE06 JSR</a><a href="#addr-B477">← B477 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B4BF" data-tip="&B4BF – Read system clock for disc-change timing">read_clock_for_timing</a></span> <span class="comment">; Read clock for elapsed time</span></td> </tr> <tr id="addr-B491"> <td class="addr"><a href="#addr-B491">B491</a></td> <td><span class="label">.verify_disc_id_unchanged<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B50D">← B50D JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive</span></td> </tr> <tr id="addr-B494"> <td class="addr"><a href="#addr-B494">B494</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B579" data-tip="&B579 – Convert drive number to slot index">convert_drive_to_slot</a></span> <span class="comment">; Get drive slot index</span></td> </tr> <tr id="addr-B497"> <td class="addr"><a href="#addr-B497">B497</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0FFB">fsm_s1_disc_id_lo</span></span> <span class="comment">; Re-read disc ID low from FSM</span></td> </tr> <tr id="addr-B49A"> <td class="addr"><a href="#addr-B49A">B49A</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1121">wksp_disc_id_lo</span>,x</span> <span class="comment">; Compare with cached value</span></td> </tr> <tr id="addr-B49D"> <td class="addr"><a href="#addr-B49D">B49D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B4AE" data-tip="&B4AE">raise_disc_changed_error</a></span> <span class="comment">; Mismatch: disc was changed</span></td> </tr> <tr id="addr-B49F"> <td class="addr"><a href="#addr-B49F">B49F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0FFC">fsm_s1_disc_id_hi</span></span> <span class="comment">; Re-read disc ID high from FSM</span></td> </tr> <tr id="addr-B4A2"> <td class="addr"><a href="#addr-B4A2">B4A2</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1122">wksp_disc_id_hi</span>,x</span> <span class="comment">; Compare with cached value</span></td> </tr> <tr id="addr-B4A5"> <td class="addr"><a href="#addr-B4A5">B4A5</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B4AE" data-tip="&B4AE">raise_disc_changed_error</a></span> <span class="comment">; Mismatch: disc was changed</span></td> </tr> <tr id="addr-B4A7"> <td class="addr"><a href="#addr-B4A7">B4A7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B510" data-tip="&B510 – Get bit mask for drive slot">get_drive_bit_mask</a></span> <span class="comment">; Get drive bit mask</span></td> </tr> <tr id="addr-B4AA"> <td class="addr"><a href="#addr-B4AA">B4AA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10C2">wksp_drive_change_mask</span></span> <span class="comment">; Update drive change mask</span></td> </tr> <tr id="addr-B4AD"> <td class="addr"><a href="#addr-B4AD">B4AD</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (disc unchanged)</span></td> </tr> <tr id="addr-B4AE"> <td class="addr"><a href="#addr-B4AE">B4AE</a></td> <td><span class="label">.raise_disc_changed_error<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B49D">← B49D BNE</a><a href="#addr-B4A5">← B4A5 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8348" data-tip="&8348 – Reload FSM and directory then raise error">reload_fsm_and_dir_then_brk</a></span></td> </tr> <tr id="addr-B4B1"> <td class="addr"><a href="#addr-B4B1">B4B1</a></td> <td> <span class="directive">EQUB</span> <span data-tip="200 &C8 %11001000">&C8</span> <span class="comment">; Error &C8: Disc changed</span></td> </tr> <tr id="addr-B4B2"> <td class="addr"><a href="#addr-B4B2">B4B2</a></td> <td> <span class="directive">EQUS</span> <span class="string">"Disc changed"</span>, <span data-tip="0 &00 %00000000 NUL">&00</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B4BF"> <td colspan="2"><div class="sub-header"> <h3>Read system clock for disc-change timing</h3> <div class="sub-desc"><p>Read the 5-byte system clock via OSWORD 1 and compute the elapsed time since the previous reading. If more than 1 centisecond has elapsed, set the disc-probably-changed flag to trigger a disc ID comparison on the next check.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B4BF">B4BF</a></td> <td><span class="label">.read_clock_for_timing<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B48E">← B48E JSR</a><a href="#addr-B4F5">← B4F5 JSR</a><a href="#addr-B525">← B525 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#osword_read_clock</span></span> <span class="comment">; OSWORD 1: read system clock</span></td> </tr> <tr id="addr-B4C1"> <td class="addr"><a href="#addr-B4C1">B4C1</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="200 &C8 %11001000">#<(wksp_clock)</span></span> <span class="comment">; X: control block low</span></td> </tr> <tr id="addr-B4C3"> <td class="addr"><a href="#addr-B4C3">B4C3</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#>(wksp_clock)</span></span> <span class="comment">; Y: control block high</span></td> </tr> <tr id="addr-B4C5"> <td class="addr"><a href="#addr-B4C5">B4C5</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF1">osword</span></span> <span class="comment">; Read system clock</span></td> </tr> <tr id="addr-B4C8"> <td class="addr"><a href="#addr-B4C8">B4C8</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: compare 5 clock bytes</span></td> </tr> <tr id="addr-B4CA"> <td class="addr"><a href="#addr-B4CA">B4CA</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Y=4: 5 bytes to compare</span></td> </tr> <tr id="addr-B4CC"> <td class="addr"><a href="#addr-B4CC">B4CC</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-B4CD"> <td class="addr"><a href="#addr-B4CD">B4CD</a></td> <td><span class="label">.compare_clock_bytes_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B4DD">← B4DD BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10C8">wksp_clock</span>,x</span> <span class="comment">; Get current clock byte</span></td> </tr> <tr id="addr-B4D0"> <td class="addr"><a href="#addr-B4D0">B4D0</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save current value</span></td> </tr> <tr id="addr-B4D1"> <td class="addr"><a href="#addr-B4D1">B4D1</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&10C3">wksp_prev_clock</span>,x</span> <span class="comment">; Subtract previous value</span></td> </tr> <tr id="addr-B4D4"> <td class="addr"><a href="#addr-B4D4">B4D4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10C8">wksp_clock</span>,x</span> <span class="comment">; Store difference</span></td> </tr> <tr id="addr-B4D7"> <td class="addr"><a href="#addr-B4D7">B4D7</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore current value</span></td> </tr> <tr id="addr-B4D8"> <td class="addr"><a href="#addr-B4D8">B4D8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10C3">wksp_prev_clock</span>,x</span> <span class="comment">; Save as new previous</span></td> </tr> <tr id="addr-B4DB"> <td class="addr"><a href="#addr-B4DB">B4DB</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B4DC"> <td class="addr"><a href="#addr-B4DC">B4DC</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement counter</span></td> </tr> <tr id="addr-B4DD"> <td class="addr"><a href="#addr-B4DD">B4DD</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B4CD" data-tip="&B4CD">compare_clock_bytes_loop</a></span> <span class="comment">; Loop for 5 bytes</span></td> </tr> <tr id="addr-B4DF"> <td class="addr"><a href="#addr-B4DF">B4DF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10CC">wksp_clock_4</span></span> <span class="comment">; Check if elapsed time > threshold</span></td> </tr> <tr id="addr-B4E2"> <td class="addr"><a href="#addr-B4E2">B4E2</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&10CB">wksp_clock_3</span></span> <span class="comment">; OR with byte 3</span></td> </tr> <tr id="addr-B4E5"> <td class="addr"><a href="#addr-B4E5">B4E5</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&10CA">wksp_clock_2</span></span> <span class="comment">; OR with byte 2</span></td> </tr> <tr id="addr-B4E8"> <td class="addr"><a href="#addr-B4E8">B4E8</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B4F1" data-tip="&B4F1">disc_probably_changed</a></span> <span class="comment">; Non-zero high bytes: long time</span></td> </tr> <tr id="addr-B4EA"> <td class="addr"><a href="#addr-B4EA">B4EA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10C9">wksp_clock_1</span></span> <span class="comment">; Check byte 1</span></td> </tr> <tr id="addr-B4ED"> <td class="addr"><a href="#addr-B4ED">B4ED</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Less than 2 ticks?</span></td> </tr> <tr id="addr-B4EF"> <td class="addr"><a href="#addr-B4EF">B4EF</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B4F4" data-tip="&B4F4">return_40</a></span> <span class="comment">; Yes: disc probably not changed</span></td> </tr> <tr id="addr-B4F1"> <td class="addr"><a href="#addr-B4F1">B4F1</a></td> <td><span class="label">.disc_probably_changed<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B4E8">← B4E8 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10C2">wksp_drive_change_mask</span></span> <span class="comment">; Long time: set change flag</span></td> </tr> <tr id="addr-B4F4"> <td class="addr"><a href="#addr-B4F4">B4F4</a></td> <td><span class="label">.return_40<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B4EF">← B4EF BCC</a><a href="#addr-B504">← B504 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B4F5"> <td colspan="2"><div class="sub-header"> <h3>Check disc changed and reload FSM if needed</h3> <div class="sub-desc"><p>Read the system clock for disc-change timing, then check whether the current drive's disc has changed since last access. If changed, reload the FSM from disc.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B4F5">B4F5</a></td> <td><span class="label">.check_drive_and_reload_fsm<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-88CF">← 88CF JSR</a><a href="#addr-9FFD">← 9FFD JSR</a><a href="#addr-A252">← A252 JSR</a><a href="#addr-B551">← B551 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B4BF" data-tip="&B4BF – Read system clock for disc-change timing">read_clock_for_timing</a></span> <span class="comment">; Read clock and check disc</span></td> </tr> <tr id="addr-B4F8"> <td class="addr"><a href="#addr-B4F8">B4F8</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive</span></td> </tr> <tr id="addr-B4FB"> <td class="addr"><a href="#addr-B4FB">B4FB</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B579" data-tip="&B579 – Convert drive number to slot index">convert_drive_to_slot</a></span> <span class="comment">; Get drive slot index</span></td> </tr> <tr id="addr-B4FE"> <td class="addr"><a href="#addr-B4FE">B4FE</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B510" data-tip="&B510 – Get bit mask for drive slot">get_drive_bit_mask</a></span> <span class="comment">; Get channel bit mask</span></td> </tr> <tr id="addr-B501"> <td class="addr"><a href="#addr-B501">B501</a></td> <td> <span class="opcode">EOR</span> <span class="operand"><span class="ext-label" data-tip="&10C2">wksp_drive_change_mask</span></span> <span class="comment">; XOR with stored mask</span></td> </tr> <tr id="addr-B504"> <td class="addr"><a href="#addr-B504">B504</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B4F4" data-tip="&B4F4">return_40</a></span> <span class="comment">; Same: disc not changed</span></td> </tr> <tr id="addr-B506"> <td class="addr"><a href="#addr-B506">B506</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="12 &0C %00001100 FF">#&0c</span></span> <span class="comment">; Changed: reload FSM</span></td> </tr> <tr id="addr-B508"> <td class="addr"><a href="#addr-B508">B508</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="136 &88 %10001000">#&88</span></span> <span class="comment">; Y=&88: FSM control block</span></td> </tr> <tr id="addr-B50A"> <td class="addr"><a href="#addr-B50A">B50A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-828B" data-tip="&828B – Execute disc command and check for error">exec_disc_command</a></span> <span class="comment">; Read FSM from disc</span></td> </tr> <tr id="addr-B50D"> <td class="addr"><a href="#addr-B50D">B50D</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B491" data-tip="&B491">verify_disc_id_unchanged</a></span> <span class="comment">; Continue checking</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B510"> <td colspan="2"><div class="sub-header"> <h3>Get bit mask for drive slot</h3> <div class="sub-desc"><p>Build a bit mask by rotating based on the drive slot index, then AND with drive-change flags.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="3">On Exit</th><td>A</td><td>bit mask ANDed with wksp_drive_change_mask</td></tr> <tr><td>X</td><td>corrupted</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B510">B510</a></td> <td><span class="label">.get_drive_bit_mask<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B4A7">← B4A7 JSR</a><a href="#addr-B4FE">← B4FE JSR</a><a href="#addr-B52E">← B52E JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A=&FF: start with all bits set</span></td> </tr> <tr id="addr-B512"> <td class="addr"><a href="#addr-B512">B512</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for shift</span></td> </tr> <tr id="addr-B513"> <td class="addr"><a href="#addr-B513">B513</a></td> <td><span class="label">.shift_drive_mask_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B516">← B516 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ROL</span> <span class="comment">; Shift left (rotate 0 in)</span></td> </tr> <tr id="addr-B514"> <td class="addr"><a href="#addr-B514">B514</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement drive index by 2</span></td> </tr> <tr id="addr-B515"> <td class="addr"><a href="#addr-B515">B515</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Continue</span></td> </tr> <tr id="addr-B516"> <td class="addr"><a href="#addr-B516">B516</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B513" data-tip="&B513">shift_drive_mask_loop</a></span> <span class="comment">; Loop until index < 0</span></td> </tr> <tr id="addr-B518"> <td class="addr"><a href="#addr-B518">B518</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="ext-label" data-tip="&10C2">wksp_drive_change_mask</span></span> <span class="comment">; AND with current change flags</span></td> </tr> <tr id="addr-B51B"> <td class="addr"><a href="#addr-B51B">B51B</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return bit mask in A</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B51C"> <td colspan="2"><div class="sub-header"> <h3>Set current drive from channel's drive</h3> <div class="sub-desc"><p>Extract drive bits from A, check disc-change timing, and reload the FSM if the drive's disc has changed.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B51C">B51C</a></td> <td><span class="label">.set_drive_from_channel<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-AB2A">← AB2A JSR</a><a href="#addr-AC8F">← AC8F JSR</a><a href="#addr-B5B8">← B5B8 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="224 &E0 %11100000">#&e0</span></span> <span class="comment">; Isolate drive bits from A</span></td> </tr> <tr id="addr-B51E"> <td class="addr"><a href="#addr-B51E">B51E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10CD">wksp_clock_5</span></span> <span class="comment">; Store drive for later</span></td> </tr> <tr id="addr-B521"> <td class="addr"><a href="#addr-B521">B521</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Save X</span></td> </tr> <tr id="addr-B522"> <td class="addr"><a href="#addr-B522">B522</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-B523"> <td class="addr"><a href="#addr-B523">B523</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Save Y</span></td> </tr> <tr id="addr-B524"> <td class="addr"><a href="#addr-B524">B524</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-B525"> <td class="addr"><a href="#addr-B525">B525</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B4BF" data-tip="&B4BF – Read system clock for disc-change timing">read_clock_for_timing</a></span> <span class="comment">; Read clock for timing check</span></td> </tr> <tr id="addr-B528"> <td class="addr"><a href="#addr-B528">B528</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10CD">wksp_clock_5</span></span> <span class="comment">; Get stored drive</span></td> </tr> <tr id="addr-B52B"> <td class="addr"><a href="#addr-B52B">B52B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B579" data-tip="&B579 – Convert drive number to slot index">convert_drive_to_slot</a></span> <span class="comment">; Get drive slot index</span></td> </tr> <tr id="addr-B52E"> <td class="addr"><a href="#addr-B52E">B52E</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B510" data-tip="&B510 – Get bit mask for drive slot">get_drive_bit_mask</a></span> <span class="comment">; Get bit mask for this drive</span></td> </tr> <tr id="addr-B531"> <td class="addr"><a href="#addr-B531">B531</a></td> <td> <span class="opcode">EOR</span> <span class="operand"><span class="ext-label" data-tip="&10C2">wksp_drive_change_mask</span></span> <span class="comment">; XOR with change flags</span></td> </tr> <tr id="addr-B534"> <td class="addr"><a href="#addr-B534">B534</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B574" data-tip="&B574">restore_saved_drive</a></span> <span class="comment">; Same: disc hasn't changed</span></td> </tr> <tr id="addr-B536"> <td class="addr"><a href="#addr-B536">B536</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10CD">wksp_clock_5</span></span> <span class="comment">; Different: need to reload FSM</span></td> </tr> <tr id="addr-B539"> <td class="addr"><a href="#addr-B539">B539</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-B53A"> <td class="addr"><a href="#addr-B53A">B53A</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save drive on stack</span></td> </tr> <tr id="addr-B53B"> <td class="addr"><a href="#addr-B53B">B53B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Save current drive</span></td> </tr> <tr id="addr-B53E"> <td class="addr"><a href="#addr-B53E">B53E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10CD">wksp_clock_5</span></span> <span class="comment">; Store as temp drive</span></td> </tr> <tr id="addr-B541"> <td class="addr"><a href="#addr-B541">B541</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Check saved drive</span></td> </tr> <tr id="addr-B544"> <td class="addr"><a href="#addr-B544">B544</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; &FF: not set</span></td> </tr> <tr id="addr-B546"> <td class="addr"><a href="#addr-B546">B546</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B54E" data-tip="&B54E">save_and_restore_drive</a></span> <span class="comment">; Set: don't overwrite</span></td> </tr> <tr id="addr-B548"> <td class="addr"><a href="#addr-B548">B548</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Save current as saved drive</span></td> </tr> <tr id="addr-B54B"> <td class="addr"><a href="#addr-B54B">B54B</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10CD">wksp_clock_5</span></span> <span class="comment">; Set temp to &FF</span></td> </tr> <tr id="addr-B54E"> <td class="addr"><a href="#addr-B54E">B54E</a></td> <td><span class="label">.save_and_restore_drive<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B546">← B546 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set current to new drive</span></td> </tr> <tr id="addr-B551"> <td class="addr"><a href="#addr-B551">B551</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B4F5" data-tip="&B4F5 – Check disc changed and reload FSM if needed">check_drive_and_reload_fsm</a></span> <span class="comment">; Reload FSM for new drive</span></td> </tr> <tr id="addr-B554"> <td class="addr"><a href="#addr-B554">B554</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&10CD">wksp_clock_5</span></span> <span class="comment">; Get temp drive back</span></td> </tr> <tr id="addr-B557"> <td class="addr"><a href="#addr-B557">B557</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set as current drive</span></td> </tr> <tr id="addr-B55A"> <td class="addr"><a href="#addr-B55A">B55A</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Was it &FF?</span></td> </tr> <tr id="addr-B55C"> <td class="addr"><a href="#addr-B55C">B55C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B567" data-tip="&B567">reload_fsm_for_drive</a></span> <span class="comment">; No: keep it</span></td> </tr> <tr id="addr-B55E"> <td class="addr"><a href="#addr-B55E">B55E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Restore saved drive</span></td> </tr> <tr id="addr-B561"> <td class="addr"><a href="#addr-B561">B561</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set as current</span></td> </tr> <tr id="addr-B564"> <td class="addr"><a href="#addr-B564">B564</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Restore saved drive as &FF</span></td> </tr> <tr id="addr-B567"> <td class="addr"><a href="#addr-B567">B567</a></td> <td><span class="label">.reload_fsm_for_drive<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B55C">← B55C BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore original drive from stack</span></td> </tr> <tr id="addr-B568"> <td class="addr"><a href="#addr-B568">B568</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Compare with current</span></td> </tr> <tr id="addr-B56B"> <td class="addr"><a href="#addr-B56B">B56B</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B574" data-tip="&B574">restore_saved_drive</a></span> <span class="comment">; Same: no FSM reload needed</span></td> </tr> <tr id="addr-B56D"> <td class="addr"><a href="#addr-B56D">B56D</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="12 &0C %00001100 FF">#&0c</span></span> <span class="comment">; Different: reload FSM for current</span></td> </tr> <tr id="addr-B56F"> <td class="addr"><a href="#addr-B56F">B56F</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="136 &88 %10001000">#&88</span></span> <span class="comment">; Y=&88: FSM control block</span></td> </tr> <tr id="addr-B571"> <td class="addr"><a href="#addr-B571">B571</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-828B" data-tip="&828B – Execute disc command and check for error">exec_disc_command</a></span> <span class="comment">; Read FSM from disc</span></td> </tr> <tr id="addr-B574"> <td class="addr"><a href="#addr-B574">B574</a></td> <td><span class="label">.restore_saved_drive<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B534">← B534 BEQ</a><a href="#addr-B56B">← B56B BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore Y from stack</span></td> </tr> <tr id="addr-B575"> <td class="addr"><a href="#addr-B575">B575</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer to Y</span></td> </tr> <tr id="addr-B576"> <td class="addr"><a href="#addr-B576">B576</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore X from stack</span></td> </tr> <tr id="addr-B577"> <td class="addr"><a href="#addr-B577">B577</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-B578"> <td class="addr"><a href="#addr-B578">B578</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B579"> <td colspan="2"><div class="sub-header"> <h3>Convert drive number to slot index</h3> <div class="sub-desc"><p>Shift drive number in A right 4 bits to produce a slot index in X.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>A</td><td>drive number (bits 5-7)</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>corrupted</td></tr> <tr><td>X</td><td>slot index (drive >> 4)</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B579">B579</a></td> <td><span class="label">.convert_drive_to_slot<span class="ref-badge">←5</span><span class="ref-popup"><a href="#addr-8FB4">← 8FB4 JSR</a><a href="#addr-B47F">← B47F JSR</a><a href="#addr-B494">← B494 JSR</a><a href="#addr-B4FB">← B4FB JSR</a><a href="#addr-B52B">← B52B JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LSR</span> <span class="comment">; Shift drive right 4 positions</span></td> </tr> <tr id="addr-B57A"> <td class="addr"><a href="#addr-B57A">B57A</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Second shift</span></td> </tr> <tr id="addr-B57B"> <td class="addr"><a href="#addr-B57B">B57B</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Third shift</span></td> </tr> <tr id="addr-B57C"> <td class="addr"><a href="#addr-B57C">B57C</a></td> <td> <span class="opcode">LSR</span> <span class="comment">; Fourth shift</span></td> </tr> <tr id="addr-B57D"> <td class="addr"><a href="#addr-B57D">B57D</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X as index</span></td> </tr> <tr id="addr-B57E"> <td class="addr"><a href="#addr-B57E">B57E</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B57F"> <td colspan="2"><div class="sub-header"> <h3>OSGBPB handler</h3> <div class="sub-desc"><p>Handle OSGBPB calls for reading and writing groups of bytes.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B57F">B57F</a></td> <td><span class="label">.osgbpb_handler</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A749" data-tip="&A749 – Save all registers and workspace">save_workspace_state</a></span> <span class="comment">; Save registers for restore</span></td> </tr> <tr id="addr-B582"> <td class="addr"><a href="#addr-B582">B582</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Store OSGBPB function code</span></td> </tr> <tr id="addr-B585"> <td class="addr"><a href="#addr-B585">B585</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Store mode flag copy</span></td> </tr> <tr id="addr-B588"> <td class="addr"><a href="#addr-B588">B588</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00C7">zp_gspb_ptr_hi</span></span> <span class="comment">; Save control block pointer</span></td> </tr> <tr id="addr-B58A"> <td class="addr"><a href="#addr-B58A">B58A</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span></span> <span class="comment">; Store control block pointer low</span></td> </tr> <tr id="addr-B58C"> <td class="addr"><a href="#addr-B58C">B58C</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: copy 4 bytes of memory addr</span></td> </tr> <tr id="addr-B58E"> <td class="addr"><a href="#addr-B58E">B58E</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: 4 bytes to copy</span></td> </tr> <tr id="addr-B590"> <td class="addr"><a href="#addr-B590">B590</a></td> <td><span class="label">.copy_data_addr_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B597">← B597 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Copy data address from control blk</span></td> </tr> <tr id="addr-B592"> <td class="addr"><a href="#addr-B592">B592</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B7">wksp_osgbpb_end</span>,y</span> <span class="comment">; Store in workspace</span></td> </tr> <tr id="addr-B595"> <td class="addr"><a href="#addr-B595">B595</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B596"> <td class="addr"><a href="#addr-B596">B596</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement counter</span></td> </tr> <tr id="addr-B597"> <td class="addr"><a href="#addr-B597">B597</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B590" data-tip="&B590">copy_data_addr_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-B599"> <td class="addr"><a href="#addr-B599">B599</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get function code</span></td> </tr> <tr id="addr-B59C"> <td class="addr"><a href="#addr-B59C">B59C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Function >= 5?</span></td> </tr> <tr id="addr-B59E"> <td class="addr"><a href="#addr-B59E">B59E</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B5A4" data-tip="&B5A4">dispatch_dir_operations</a></span> <span class="comment">; No, file I/O operations (1-4)</span></td> </tr> <tr id="addr-B5A0"> <td class="addr"><a href="#addr-B5A0">B5A0</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B890" data-tip="&B890">dispatch_dir_info_handler</a></span> <span class="comment">; Yes, directory operations (5-8)</span></td> </tr> <tr id="addr-B5A3"> <td class="addr"><a href="#addr-B5A3">B5A3</a></td> <td><span class="label">.return_41<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5A5">← B5A5 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (function 0: do nothing)</span></td> </tr> <tr id="addr-B5A4"> <td class="addr"><a href="#addr-B5A4">B5A4</a></td> <td><span class="label">.dispatch_dir_operations<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B59E">← B59E BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer function to Y</span></td> </tr> <tr id="addr-B5A5"> <td class="addr"><a href="#addr-B5A5">B5A5</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B5A3" data-tip="&B5A3">return_41</a></span> <span class="comment">; Function 0: do nothing</span></td> </tr> <tr id="addr-B5A7"> <td class="addr"><a href="#addr-B5A7">B5A7</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: get file handle from block</span></td> </tr> <tr id="addr-B5A9"> <td class="addr"><a href="#addr-B5A9">B5A9</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Read channel number from block+0</span></td> </tr> <tr id="addr-B5AB"> <td class="addr"><a href="#addr-B5AB">B5AB</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer function to Y</span></td> </tr> <tr id="addr-B5AC"> <td class="addr"><a href="#addr-B5AC">B5AC</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ACFE" data-tip="&ACFE – Validate and set channel number from Y">check_set_channel_y</a></span> <span class="comment">; Validate file handle</span></td> </tr> <tr id="addr-B5AF"> <td class="addr"><a href="#addr-B5AF">B5AF</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save flags for write check</span></td> </tr> <tr id="addr-B5B0"> <td class="addr"><a href="#addr-B5B0">B5B0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B18C" data-tip="&B18C – Synchronise EXT to PTR if at EOF">sync_ext_to_ptr</a></span> <span class="comment">; Flush buffer if dirty</span></td> </tr> <tr id="addr-B5B3"> <td class="addr"><a href="#addr-B5B3">B5B3</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B5B5"> <td class="addr"><a href="#addr-B5B5">B5B5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel drive+sector</span></td> </tr> <tr id="addr-B5B8"> <td class="addr"><a href="#addr-B5B8">B5B8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B51C" data-tip="&B51C – Set current drive from channel's drive">set_drive_from_channel</a></span> <span class="comment">; Check disc change for drive</span></td> </tr> <tr id="addr-B5BB"> <td class="addr"><a href="#addr-B5BB">B5BB</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags from earlier</span></td> </tr> <tr id="addr-B5BC"> <td class="addr"><a href="#addr-B5BC">B5BC</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B5C8" data-tip="&B5C8">get_function_and_set_ptr</a></span> <span class="comment">; Bit 7 set: writable channel</span></td> </tr> <tr id="addr-B5BE"> <td class="addr"><a href="#addr-B5BE">B5BE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get function code</span></td> </tr> <tr id="addr-B5C1"> <td class="addr"><a href="#addr-B5C1">B5C1</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; A >= 3 (read operation)?</span></td> </tr> <tr id="addr-B5C3"> <td class="addr"><a href="#addr-B5C3">B5C3</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B5C8" data-tip="&B5C8">get_function_and_set_ptr</a></span> <span class="comment">; Yes: skip write check</span></td> </tr> <tr id="addr-B5C5"> <td class="addr"><a href="#addr-B5C5">B5C5</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B09D" data-tip="&B09D">not_open_for_update_error</a></span> <span class="comment">; Write to read-only: error</span></td> </tr> <tr id="addr-B5C8"> <td class="addr"><a href="#addr-B5C8">B5C8</a></td> <td><span class="label">.get_function_and_set_ptr<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B5BC">← B5BC BMI</a><a href="#addr-B5C3">← B5C3 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get function code</span></td> </tr> <tr id="addr-B5CB"> <td class="addr"><a href="#addr-B5CB">B5CB</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Bit 0 set = use new PTR (A=1,3)</span></td> </tr> <tr id="addr-B5CD"> <td class="addr"><a href="#addr-B5CD">B5CD</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B5DD" data-tip="&B5DD">set_ptr_from_temp</a></span> <span class="comment">; Bit 0 clear = use current PTR</span></td> </tr> <tr id="addr-B5CF"> <td class="addr"><a href="#addr-B5CF">B5CF</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="12 &0C %00001100 FF">#&0c</span></span> <span class="comment">; Y=&0C: copy new PTR from block</span></td> </tr> <tr id="addr-B5D1"> <td class="addr"><a href="#addr-B5D1">B5D1</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: 4 PTR bytes</span></td> </tr> <tr id="addr-B5D3"> <td class="addr"><a href="#addr-B5D3">B5D3</a></td> <td><span class="label">.copy_new_ptr_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5D9">← B5D9 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Get PTR byte from control block</span></td> </tr> <tr id="addr-B5D5"> <td class="addr"><a href="#addr-B5D5">B5D5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00C8">zp_temp_ptr</span>,x</span> <span class="comment">; Store in zp_c8-cb (temp PTR)</span></td> </tr> <tr id="addr-B5D7"> <td class="addr"><a href="#addr-B5D7">B5D7</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next block byte (decreasing)</span></td> </tr> <tr id="addr-B5D8"> <td class="addr"><a href="#addr-B5D8">B5D8</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next ZP byte (decreasing)</span></td> </tr> <tr id="addr-B5D9"> <td class="addr"><a href="#addr-B5D9">B5D9</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B5D3" data-tip="&B5D3">copy_new_ptr_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-B5DB"> <td class="addr"><a href="#addr-B5DB">B5DB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: flag for new PTR</span></td> </tr> <tr id="addr-B5DD"> <td class="addr"><a href="#addr-B5DD">B5DD</a></td> <td><span class="label">.set_ptr_from_temp<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5CD">← B5CD BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C2">zp_save_y</span></span> <span class="comment">; Restore Y from saved value</span></td> </tr> <tr id="addr-B5DF"> <td class="addr"><a href="#addr-B5DF">B5DF</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="200 &C8 %11001000">#&c8</span></span> <span class="comment">; X=&C8: point to temp PTR in ZP</span></td> </tr> <tr id="addr-B5E1"> <td class="addr"><a href="#addr-B5E1">B5E1</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-A998" data-tip="&A998">set_channel_and_dispatch</a></span> <span class="comment">; Set PTR from temp PTR</span></td> </tr> <tr id="addr-B5E4"> <td class="addr"><a href="#addr-B5E4">B5E4</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-B5E5"> <td class="addr"><a href="#addr-B5E5">B5E5</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: add byte count to end PTR</span></td> </tr> <tr id="addr-B5E7"> <td class="addr"><a href="#addr-B5E7">B5E7</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: byte count in control block</span></td> </tr> <tr id="addr-B5E9"> <td class="addr"><a href="#addr-B5E9">B5E9</a></td> <td><span class="label">.calc_end_position_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5F3">← B5F3 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Get byte count byte</span></td> </tr> <tr id="addr-B5EB"> <td class="addr"><a href="#addr-B5EB">B5EB</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00C3">zp_save_x</span>,y</span> <span class="comment">; Add to start PTR byte</span></td> </tr> <tr id="addr-B5EE"> <td class="addr"><a href="#addr-B5EE">B5EE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1095">wksp_osgbpb_end_ptr</span>,y</span> <span class="comment">; Store end position</span></td> </tr> <tr id="addr-B5F1"> <td class="addr"><a href="#addr-B5F1">B5F1</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B5F2"> <td class="addr"><a href="#addr-B5F2">B5F2</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next count byte</span></td> </tr> <tr id="addr-B5F3"> <td class="addr"><a href="#addr-B5F3">B5F3</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B5E9" data-tip="&B5E9">calc_end_position_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-B5F5"> <td class="addr"><a href="#addr-B5F5">B5F5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get function code</span></td> </tr> <tr id="addr-B5F8"> <td class="addr"><a href="#addr-B5F8">B5F8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Store in mode flag</span></td> </tr> <tr id="addr-B5FB"> <td class="addr"><a href="#addr-B5FB">B5FB</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; A >= 3 (read)?</span></td> </tr> <tr id="addr-B5FD"> <td class="addr"><a href="#addr-B5FD">B5FD</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B602" data-tip="&B602">store_new_ptr_in_channel</a></span> <span class="comment">; Yes: skip extent check</span></td> </tr> <tr id="addr-B5FF"> <td class="addr"><a href="#addr-B5FF">B5FF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AE5E" data-tip="&AE5E">extend_file_if_needed</a></span> <span class="comment">; Write: extend file if needed</span></td> </tr> <tr id="addr-B602"> <td class="addr"><a href="#addr-B602">B602</a></td> <td><span class="label">.store_new_ptr_in_channel<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5FD">← B5FD BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: PTR offset in control block</span></td> </tr> <tr id="addr-B604"> <td class="addr"><a href="#addr-B604">B604</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B606"> <td class="addr"><a href="#addr-B606">B606</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Get new PTR low from workspace</span></td> </tr> <tr id="addr-B609"> <td class="addr"><a href="#addr-B609">B609</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Store in channel PTR low</span></td> </tr> <tr id="addr-B60C"> <td class="addr"><a href="#addr-B60C">B60C</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block too</span></td> </tr> <tr id="addr-B60E"> <td class="addr"><a href="#addr-B60E">B60E</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&0A: next byte</span></td> </tr> <tr id="addr-B60F"> <td class="addr"><a href="#addr-B60F">B60F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Get PTR mid-low</span></td> </tr> <tr id="addr-B612"> <td class="addr"><a href="#addr-B612">B612</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Store in channel</span></td> </tr> <tr id="addr-B615"> <td class="addr"><a href="#addr-B615">B615</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B617"> <td class="addr"><a href="#addr-B617">B617</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&0B</span></td> </tr> <tr id="addr-B618"> <td class="addr"><a href="#addr-B618">B618</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Get PTR mid-high</span></td> </tr> <tr id="addr-B61B"> <td class="addr"><a href="#addr-B61B">B61B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Store in channel</span></td> </tr> <tr id="addr-B61E"> <td class="addr"><a href="#addr-B61E">B61E</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B620"> <td class="addr"><a href="#addr-B620">B620</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=&0C</span></td> </tr> <tr id="addr-B621"> <td class="addr"><a href="#addr-B621">B621</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Get PTR high</span></td> </tr> <tr id="addr-B624"> <td class="addr"><a href="#addr-B624">B624</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Store in channel</span></td> </tr> <tr id="addr-B627"> <td class="addr"><a href="#addr-B627">B627</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B629"> <td class="addr"><a href="#addr-B629">B629</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get function code</span></td> </tr> <tr id="addr-B62C"> <td class="addr"><a href="#addr-B62C">B62C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; A >= 3 (read)?</span></td> </tr> <tr id="addr-B62E"> <td class="addr"><a href="#addr-B62E">B62E</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B644" data-tip="&B644">compare_ext_with_ptr</a></span> <span class="comment">; Yes: skip to byte transfer</span></td> </tr> <tr id="addr-B630"> <td class="addr"><a href="#addr-B630">B630</a></td> <td><span class="label">.save_byte_count_for_write<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B647">← B647 BCS</a><a href="#addr-B649">← B649 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: save 4-byte count</span></td> </tr> <tr id="addr-B632"> <td class="addr"><a href="#addr-B632">B632</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: byte count in block</span></td> </tr> <tr id="addr-B634"> <td class="addr"><a href="#addr-B634">B634</a></td> <td><span class="label">.save_and_clear_count_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B63F">← B63F BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Get byte count from block</span></td> </tr> <tr id="addr-B636"> <td class="addr"><a href="#addr-B636">B636</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&103B">wksp_saved_count</span>,y</span> <span class="comment">; Save in workspace</span></td> </tr> <tr id="addr-B639"> <td class="addr"><a href="#addr-B639">B639</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear byte count in block</span></td> </tr> <tr id="addr-B63B"> <td class="addr"><a href="#addr-B63B">B63B</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store zero in block</span></td> </tr> <tr id="addr-B63D"> <td class="addr"><a href="#addr-B63D">B63D</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B63E"> <td class="addr"><a href="#addr-B63E">B63E</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement counter</span></td> </tr> <tr id="addr-B63F"> <td class="addr"><a href="#addr-B63F">B63F</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B634" data-tip="&B634">save_and_clear_count_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-B641"> <td class="addr"><a href="#addr-B641">B641</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B6B4" data-tip="&B6B4">setup_disc_transfer</a></span> <span class="comment">; Jump to byte transfer loop</span></td> </tr> <tr id="addr-B644"> <td class="addr"><a href="#addr-B644">B644</a></td> <td><span class="label">.compare_ext_with_ptr<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B62E">← B62E BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AD16" data-tip="&AD16 – Compare file EXT to PTR">compare_ext_to_ptr</a></span> <span class="comment">; Compare EXT with PTR</span></td> </tr> <tr id="addr-B647"> <td class="addr"><a href="#addr-B647">B647</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B630" data-tip="&B630">save_byte_count_for_write</a></span> <span class="comment">; C set: EXT > PTR, data available</span></td> </tr> <tr id="addr-B649"> <td class="addr"><a href="#addr-B649">B649</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B630" data-tip="&B630">save_byte_count_for_write</a></span> <span class="comment">; Equal: at EOF already</span></td> </tr> <tr id="addr-B64B"> <td class="addr"><a href="#addr-B64B">B64B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear mode flag (partial read)</span></td> </tr> <tr id="addr-B64D"> <td class="addr"><a href="#addr-B64D">B64D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Store cleared mode</span></td> </tr> <tr id="addr-B650"> <td class="addr"><a href="#addr-B650">B650</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B652"> <td class="addr"><a href="#addr-B652">B652</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Calculate available = EXT - PTR</span></td> </tr> <tr id="addr-B653"> <td class="addr"><a href="#addr-B653">B653</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; EXT low - PTR low</span></td> </tr> <tr id="addr-B656"> <td class="addr"><a href="#addr-B656">B656</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&00C8">zp_temp_ptr</span></span> <span class="comment">; Subtract PTR byte</span></td> </tr> <tr id="addr-B658"> <td class="addr"><a href="#addr-B658">B658</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span></span> <span class="comment">; Store available low</span></td> </tr> <tr id="addr-B65B"> <td class="addr"><a href="#addr-B65B">B65B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; EXT mid-low</span></td> </tr> <tr id="addr-B65E"> <td class="addr"><a href="#addr-B65E">B65E</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&00C9">zp_temp_ptr_1</span></span> <span class="comment">; Subtract PTR mid-low</span></td> </tr> <tr id="addr-B660"> <td class="addr"><a href="#addr-B660">B660</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1041">wksp_osfile_block_1</span></span> <span class="comment">; Store available mid-low</span></td> </tr> <tr id="addr-B663"> <td class="addr"><a href="#addr-B663">B663</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; EXT mid-high</span></td> </tr> <tr id="addr-B666"> <td class="addr"><a href="#addr-B666">B666</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&00CA">zp_temp_ptr_2</span></span> <span class="comment">; Subtract PTR mid-high</span></td> </tr> <tr id="addr-B668"> <td class="addr"><a href="#addr-B668">B668</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1042">wksp_osfile_load_addr</span></span> <span class="comment">; Store available mid-high</span></td> </tr> <tr id="addr-B66B"> <td class="addr"><a href="#addr-B66B">B66B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; EXT high</span></td> </tr> <tr id="addr-B66E"> <td class="addr"><a href="#addr-B66E">B66E</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&00CB">zp_temp_ptr_3</span></span> <span class="comment">; Subtract PTR high</span></td> </tr> <tr id="addr-B670"> <td class="addr"><a href="#addr-B670">B670</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1043">wksp_osfile_load_addr_1</span></span> <span class="comment">; Store available high</span></td> </tr> <tr id="addr-B673"> <td class="addr"><a href="#addr-B673">B673</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: reduce requested by unavail</span></td> </tr> <tr id="addr-B675"> <td class="addr"><a href="#addr-B675">B675</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: byte count in control block</span></td> </tr> <tr id="addr-B677"> <td class="addr"><a href="#addr-B677">B677</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-B678"> <td class="addr"><a href="#addr-B678">B678</a></td> <td><span class="label">.reduce_count_to_available_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B681">← B681 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Get requested count byte</span></td> </tr> <tr id="addr-B67A"> <td class="addr"><a href="#addr-B67A">B67A</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&103B">wksp_saved_count</span>,y</span> <span class="comment">; Subtract saved count byte</span></td> </tr> <tr id="addr-B67D"> <td class="addr"><a href="#addr-B67D">B67D</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store reduced count in block</span></td> </tr> <tr id="addr-B67F"> <td class="addr"><a href="#addr-B67F">B67F</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B680"> <td class="addr"><a href="#addr-B680">B680</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next count byte</span></td> </tr> <tr id="addr-B681"> <td class="addr"><a href="#addr-B681">B681</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B678" data-tip="&B678">reduce_count_to_available_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-B683"> <td class="addr"><a href="#addr-B683">B683</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B685"> <td class="addr"><a href="#addr-B685">B685</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1152">wksp_ch_ext_l</span>,x</span> <span class="comment">; Get EXT low</span></td> </tr> <tr id="addr-B688"> <td class="addr"><a href="#addr-B688">B688</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Store as new PTR low</span></td> </tr> <tr id="addr-B68B"> <td class="addr"><a href="#addr-B68B">B68B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&117A">wksp_ch_ptr_l</span>,x</span> <span class="comment">; Update channel PTR low</span></td> </tr> <tr id="addr-B68E"> <td class="addr"><a href="#addr-B68E">B68E</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B690"> <td class="addr"><a href="#addr-B690">B690</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=next byte</span></td> </tr> <tr id="addr-B691"> <td class="addr"><a href="#addr-B691">B691</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1148">wksp_ch_ext_ml</span>,x</span> <span class="comment">; Get EXT mid-low</span></td> </tr> <tr id="addr-B694"> <td class="addr"><a href="#addr-B694">B694</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Store as new PTR mid-low</span></td> </tr> <tr id="addr-B697"> <td class="addr"><a href="#addr-B697">B697</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1170">wksp_ch_ptr_ml</span>,x</span> <span class="comment">; Update channel PTR mid-low</span></td> </tr> <tr id="addr-B69A"> <td class="addr"><a href="#addr-B69A">B69A</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B69C"> <td class="addr"><a href="#addr-B69C">B69C</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=next byte</span></td> </tr> <tr id="addr-B69D"> <td class="addr"><a href="#addr-B69D">B69D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&113E">wksp_ch_ext_mh</span>,x</span> <span class="comment">; Get EXT mid-high</span></td> </tr> <tr id="addr-B6A0"> <td class="addr"><a href="#addr-B6A0">B6A0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Store as new PTR mid-high</span></td> </tr> <tr id="addr-B6A3"> <td class="addr"><a href="#addr-B6A3">B6A3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1166">wksp_ch_ptr_mh</span>,x</span> <span class="comment">; Update channel PTR mid-high</span></td> </tr> <tr id="addr-B6A6"> <td class="addr"><a href="#addr-B6A6">B6A6</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B6A8"> <td class="addr"><a href="#addr-B6A8">B6A8</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=next byte</span></td> </tr> <tr id="addr-B6A9"> <td class="addr"><a href="#addr-B6A9">B6A9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1134">wksp_ch_ext_h</span>,x</span> <span class="comment">; Get EXT high</span></td> </tr> <tr id="addr-B6AC"> <td class="addr"><a href="#addr-B6AC">B6AC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Store as new PTR high</span></td> </tr> <tr id="addr-B6AF"> <td class="addr"><a href="#addr-B6AF">B6AF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&115C">wksp_ch_ptr_h</span>,x</span> <span class="comment">; Update channel PTR high</span></td> </tr> <tr id="addr-B6B2"> <td class="addr"><a href="#addr-B6B2">B6B2</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block</span></td> </tr> <tr id="addr-B6B4"> <td class="addr"><a href="#addr-B6B4">B6B4</a></td> <td><span class="label">.setup_disc_transfer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B641">← B641 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: memory address in control block</span></td> </tr> <tr id="addr-B6B6"> <td class="addr"><a href="#addr-B6B6">B6B6</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; X=3: 4 address bytes</span></td> </tr> <tr id="addr-B6B8"> <td class="addr"><a href="#addr-B6B8">B6B8</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-B6B9"> <td class="addr"><a href="#addr-B6B9">B6B9</a></td> <td><span class="label">.update_control_block_addr_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B6C2">← B6C2 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&103F">wksp_alloc_size_hi</span>,y</span> <span class="comment">; Get transferred bytes count</span></td> </tr> <tr id="addr-B6BC"> <td class="addr"><a href="#addr-B6BC">B6BC</a></td> <td> <span class="opcode">ADC</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span></td> </tr> <tr id="addr-B6BE"> <td class="addr"><a href="#addr-B6BE">B6BE</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store updated memory address</span></td> </tr> <tr id="addr-B6C0"> <td class="addr"><a href="#addr-B6C0">B6C0</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next address byte</span></td> </tr> <tr id="addr-B6C1"> <td class="addr"><a href="#addr-B6C1">B6C1</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next count byte</span></td> </tr> <tr id="addr-B6C2"> <td class="addr"><a href="#addr-B6C2">B6C2</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B6B9" data-tip="&B6B9">update_control_block_addr_loop</a></span> <span class="comment">; Loop for 4 address bytes</span></td> </tr> <tr id="addr-B6C4"> <td class="addr"><a href="#addr-B6C4">B6C4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00C8">zp_temp_ptr</span></span> <span class="comment">; Get PTR high byte</span></td> </tr> <tr id="addr-B6C6"> <td class="addr"><a href="#addr-B6C6">B6C6</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B6CB" data-tip="&B6CB">calc_disc_sector_for_channel</a></span> <span class="comment">; Non-zero: multi-sector possible</span></td> </tr> <tr id="addr-B6C8"> <td class="addr"><a href="#addr-B6C8">B6C8</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B75D" data-tip="&B75D">check_full_sectors_remain</a></span> <span class="comment">; PTR high=0: no full sectors remain</span></td> </tr> <tr id="addr-B6CB"> <td class="addr"><a href="#addr-B6CB">B6CB</a></td> <td><span class="label">.calc_disc_sector_for_channel<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B6C6">← B6C6 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index for sector calc</span></td> </tr> <tr id="addr-B6CD"> <td class="addr"><a href="#addr-B6CD">B6CD</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for sector addition</span></td> </tr> <tr id="addr-B6CE"> <td class="addr"><a href="#addr-B6CE">B6CE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Get channel start sector low</span></td> </tr> <tr id="addr-B6D1"> <td class="addr"><a href="#addr-B6D1">B6D1</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00C9">zp_temp_ptr_1</span></span> <span class="comment">; Add PTR mid-low for disc sector</span></td> </tr> <tr id="addr-B6D3"> <td class="addr"><a href="#addr-B6D3">B6D3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Store disc operation sector low</span></td> </tr> <tr id="addr-B6D6"> <td class="addr"><a href="#addr-B6D6">B6D6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Get channel start sector mid</span></td> </tr> <tr id="addr-B6D9"> <td class="addr"><a href="#addr-B6D9">B6D9</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00CA">zp_temp_ptr_2</span></span> <span class="comment">; Add PTR mid-high</span></td> </tr> <tr id="addr-B6DB"> <td class="addr"><a href="#addr-B6DB">B6DB</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Store disc operation sector mid</span></td> </tr> <tr id="addr-B6DE"> <td class="addr"><a href="#addr-B6DE">B6DE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel start sector+drive</span></td> </tr> <tr id="addr-B6E1"> <td class="addr"><a href="#addr-B6E1">B6E1</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00CB">zp_temp_ptr_3</span></span> <span class="comment">; Add PTR high byte</span></td> </tr> <tr id="addr-B6E3"> <td class="addr"><a href="#addr-B6E3">B6E3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Store disc operation sector high</span></td> </tr> <tr id="addr-B6E6"> <td class="addr"><a href="#addr-B6E6">B6E6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; A=2: compare against function code</span></td> </tr> <tr id="addr-B6E8"> <td class="addr"><a href="#addr-B6E8">B6E8</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; C set if A=1/2 (write), clear if 3/4</span></td> </tr> <tr id="addr-B6EB"> <td class="addr"><a href="#addr-B6EB">B6EB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; A=&80: base for disc command</span></td> </tr> <tr id="addr-B6ED"> <td class="addr"><a href="#addr-B6ED">B6ED</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate C into bit 0: &40=read, &80=write</span></td> </tr> <tr id="addr-B6EE"> <td class="addr"><a href="#addr-B6EE">B6EE</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABD8" data-tip="&ABD8 – Find or allocate a buffer for a sector">find_buffer_for_sector</a></span> <span class="comment">; Find/load buffer for current sector</span></td> </tr> <tr id="addr-B6F1"> <td class="addr"><a href="#addr-B6F1">B6F1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00C8">zp_temp_ptr</span></span> <span class="comment">; Get current byte offset in sector</span></td> </tr> <tr id="addr-B6F3"> <td class="addr"><a href="#addr-B6F3">B6F3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Store as transfer start position</span></td> </tr> <tr id="addr-B6F6"> <td class="addr"><a href="#addr-B6F6">B6F6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: default end position</span></td> </tr> <tr id="addr-B6F8"> <td class="addr"><a href="#addr-B6F8">B6F8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B7">wksp_osgbpb_end</span></span> <span class="comment">; Clear transfer end position</span></td> </tr> <tr id="addr-B6FB"> <td class="addr"><a href="#addr-B6FB">B6FB</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: compare 3-byte buffer sector</span></td> </tr> <tr id="addr-B6FD"> <td class="addr"><a href="#addr-B6FD">B6FD</a></td> <td><span class="label">.compare_buffer_sector_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B705">← B705 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span>,x</span> <span class="comment">; Get buffered sector address byte</span></td> </tr> <tr id="addr-B700"> <td class="addr"><a href="#addr-B700">B700</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&00C9">zp_temp_ptr_1</span>,x</span> <span class="comment">; Compare with requested sector byte</span></td> </tr> <tr id="addr-B702"> <td class="addr"><a href="#addr-B702">B702</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B720" data-tip="&B720">handle_buffer_mismatch</a></span> <span class="comment">; Mismatch: different sector in buffer</span></td> </tr> <tr id="addr-B704"> <td class="addr"><a href="#addr-B704">B704</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next sector address byte</span></td> </tr> <tr id="addr-B705"> <td class="addr"><a href="#addr-B705">B705</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B6FD" data-tip="&B6FD">compare_buffer_sector_loop</a></span> <span class="comment">; Loop for 3-byte sector comparison</span></td> </tr> <tr id="addr-B707"> <td class="addr"><a href="#addr-B707">B707</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Sector match: get bytes remaining</span></td> </tr> <tr id="addr-B70A"> <td class="addr"><a href="#addr-B70A">B70A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B7">wksp_osgbpb_end</span></span> <span class="comment">; Store as transfer end position</span></td> </tr> <tr id="addr-B70D"> <td class="addr"><a href="#addr-B70D">B70D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B980" data-tip="&B980 – Transfer sector bytes between buffer and memory">transfer_sector_bytes</a></span> <span class="comment">; Transfer bytes within this sector</span></td> </tr> <tr id="addr-B710"> <td class="addr"><a href="#addr-B710">B710</a></td> <td><span class="label">.save_and_flush_after_transfer<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B7E8">← B7E8 JMP</a><a href="#addr-B822">← B822 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-89D3" data-tip="&89D3 – Save workspace state and return result">save_wksp_and_return</a></span> <span class="comment">; Save workspace state</span></td> </tr> <tr id="addr-B713"> <td class="addr"><a href="#addr-B713">B713</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B13F" data-tip="&B13F">update_channel_flags_for_ptr</a></span> <span class="comment">; Flush buffer if modified</span></td> </tr> <tr id="addr-B716"> <td class="addr"><a href="#addr-B716">B716</a></td> <td><span class="label">.prepare_osgbpb_return<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B8DE">← B8DE JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: prepare return status</span></td> </tr> <tr id="addr-B718"> <td class="addr"><a href="#addr-B718">B718</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Compare against mode flag for C</span></td> </tr> <tr id="addr-B71B"> <td class="addr"><a href="#addr-B71B">B71B</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span></span> <span class="comment">; Restore control block pointer low</span></td> </tr> <tr id="addr-B71D"> <td class="addr"><a href="#addr-B71D">B71D</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00C7">zp_gspb_ptr_hi</span></span> <span class="comment">; Restore control block pointer high</span></td> </tr> <tr id="addr-B71F"> <td class="addr"><a href="#addr-B71F">B71F</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return to OSGBPB caller</span></td> </tr> <tr id="addr-B720"> <td class="addr"><a href="#addr-B720">B720</a></td> <td><span class="label">.handle_buffer_mismatch<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B702">← B702 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B980" data-tip="&B980 – Transfer sector bytes between buffer and memory">transfer_sector_bytes</a></span> <span class="comment">; Buffer mismatch: handle partial xfer</span></td> </tr> <tr id="addr-B723"> <td class="addr"><a href="#addr-B723">B723</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: compute bytes already done</span></td> </tr> <tr id="addr-B725"> <td class="addr"><a href="#addr-B725">B725</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-B726"> <td class="addr"><a href="#addr-B726">B726</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Subtract start position</span></td> </tr> <tr id="addr-B729"> <td class="addr"><a href="#addr-B729">B729</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Store bytes transferred this pass</span></td> </tr> <tr id="addr-B72C"> <td class="addr"><a href="#addr-B72C">B72C</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-B72D"> <td class="addr"><a href="#addr-B72D">B72D</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&10B8">wksp_osgbpb_data_addr</span></span> <span class="comment">; Add to cumulative data address low</span></td> </tr> <tr id="addr-B730"> <td class="addr"><a href="#addr-B730">B730</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B8">wksp_osgbpb_data_addr</span></span> <span class="comment">; Store updated address low</span></td> </tr> <tr id="addr-B733"> <td class="addr"><a href="#addr-B733">B733</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B742" data-tip="&B742">adjust_remaining_count</a></span> <span class="comment">; No carry: skip higher bytes</span></td> </tr> <tr id="addr-B735"> <td class="addr"><a href="#addr-B735">B735</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&10B9">wksp_osgbpb_data_addr_1</span></span> <span class="comment">; Propagate carry to address byte 2</span></td> </tr> <tr id="addr-B738"> <td class="addr"><a href="#addr-B738">B738</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B742" data-tip="&B742">adjust_remaining_count</a></span> <span class="comment">; No carry: skip</span></td> </tr> <tr id="addr-B73A"> <td class="addr"><a href="#addr-B73A">B73A</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&10BA">wksp_osgbpb_data_addr_2</span></span> <span class="comment">; Propagate carry to address byte 3</span></td> </tr> <tr id="addr-B73D"> <td class="addr"><a href="#addr-B73D">B73D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B742" data-tip="&B742">adjust_remaining_count</a></span> <span class="comment">; No carry: skip</span></td> </tr> <tr id="addr-B73F"> <td class="addr"><a href="#addr-B73F">B73F</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&10BB">wksp_osgbpb_data_addr_3</span></span> <span class="comment">; Propagate carry to address byte 4</span></td> </tr> <tr id="addr-B742"> <td class="addr"><a href="#addr-B742">B742</a></td> <td><span class="label">.adjust_remaining_count<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B733">← B733 BCC</a><a href="#addr-B738">← B738 BNE</a><a href="#addr-B73D">← B73D BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">SEC</span> <span class="comment">; Subtract from remaining byte count</span></td> </tr> <tr id="addr-B743"> <td class="addr"><a href="#addr-B743">B743</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span></span> <span class="comment">; Get remaining count low</span></td> </tr> <tr id="addr-B746"> <td class="addr"><a href="#addr-B746">B746</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Subtract bytes transferred</span></td> </tr> <tr id="addr-B749"> <td class="addr"><a href="#addr-B749">B749</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span></span> <span class="comment">; Store updated remaining count</span></td> </tr> <tr id="addr-B74C"> <td class="addr"><a href="#addr-B74C">B74C</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B75D" data-tip="&B75D">check_full_sectors_remain</a></span> <span class="comment">; No borrow: count still positive</span></td> </tr> <tr id="addr-B74E"> <td class="addr"><a href="#addr-B74E">B74E</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: propagate borrow to higher bytes</span></td> </tr> <tr id="addr-B750"> <td class="addr"><a href="#addr-B750">B750</a></td> <td><span class="label">.propagate_borrow_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B75B">← B75B BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span>,y</span> <span class="comment">; Get remaining count byte</span></td> </tr> <tr id="addr-B753"> <td class="addr"><a href="#addr-B753">B753</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Subtract borrow</span></td> </tr> <tr id="addr-B755"> <td class="addr"><a href="#addr-B755">B755</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1040">wksp_osfile_block</span>,y</span> <span class="comment">; Store updated count byte</span></td> </tr> <tr id="addr-B758"> <td class="addr"><a href="#addr-B758">B758</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B75D" data-tip="&B75D">check_full_sectors_remain</a></span> <span class="comment">; No borrow: done adjusting</span></td> </tr> <tr id="addr-B75A"> <td class="addr"><a href="#addr-B75A">B75A</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next count byte</span></td> </tr> <tr id="addr-B75B"> <td class="addr"><a href="#addr-B75B">B75B</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B750" data-tip="&B750">propagate_borrow_loop</a></span> <span class="comment">; Loop for remaining bytes</span></td> </tr> <tr id="addr-B75D"> <td class="addr"><a href="#addr-B75D">B75D</a></td> <td><span class="label">.check_full_sectors_remain<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B6C8">← B6C8 JMP</a><a href="#addr-B74C">← B74C BCS</a><a href="#addr-B758">← B758 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1041">wksp_osfile_block_1</span></span> <span class="comment">; Check if any full sectors to transfer</span></td> </tr> <tr id="addr-B760"> <td class="addr"><a href="#addr-B760">B760</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1042">wksp_osfile_load_addr</span></span> <span class="comment">; OR mid-low count byte</span></td> </tr> <tr id="addr-B763"> <td class="addr"><a href="#addr-B763">B763</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1043">wksp_osfile_load_addr_1</span></span> <span class="comment">; OR mid-high count byte</span></td> </tr> <tr id="addr-B766"> <td class="addr"><a href="#addr-B766">B766</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B76B" data-tip="&B76B">setup_disc_op_block</a></span> <span class="comment">; Non-zero: full sectors remain</span></td> </tr> <tr id="addr-B768"> <td class="addr"><a href="#addr-B768">B768</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B7E3" data-tip="&B7E3">check_remaining_buffered</a></span> <span class="comment">; No full sectors: finish transfer</span></td> </tr> <tr id="addr-B76B"> <td class="addr"><a href="#addr-B76B">B76B</a></td> <td><span class="label">.setup_disc_op_block<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B766">← B766 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: flag multi-sector disc operation</span></td> </tr> <tr id="addr-B76D"> <td class="addr"><a href="#addr-B76D">B76D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1015">wksp_disc_op_result</span></span> <span class="comment">; Store in disc op result field</span></td> </tr> <tr id="addr-B770"> <td class="addr"><a href="#addr-B770">B770</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Y=3: copy 4-byte data address</span></td> </tr> <tr id="addr-B772"> <td class="addr"><a href="#addr-B772">B772</a></td> <td><span class="label">.copy_data_addr_to_disc_op_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B779">← B779 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B8">wksp_osgbpb_data_addr</span>,y</span> <span class="comment">; Get data address byte</span></td> </tr> <tr id="addr-B775"> <td class="addr"><a href="#addr-B775">B775</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span>,y</span> <span class="comment">; Store in disc op memory address</span></td> </tr> <tr id="addr-B778"> <td class="addr"><a href="#addr-B778">B778</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte (decreasing)</span></td> </tr> <tr id="addr-B779"> <td class="addr"><a href="#addr-B779">B779</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B772" data-tip="&B772">copy_data_addr_to_disc_op_loop</a></span> <span class="comment">; Loop for 4 bytes</span></td> </tr> <tr id="addr-B77B"> <td class="addr"><a href="#addr-B77B">B77B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; A=2: compare against function code</span></td> </tr> <tr id="addr-B77D"> <td class="addr"><a href="#addr-B77D">B77D</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; C set if write (A<=2), clear if read</span></td> </tr> <tr id="addr-B780"> <td class="addr"><a href="#addr-B780">B780</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; A=2: base for disc command</span></td> </tr> <tr id="addr-B782"> <td class="addr"><a href="#addr-B782">B782</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Rotate C into bit 0</span></td> </tr> <tr id="addr-B783"> <td class="addr"><a href="#addr-B783">B783</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Shift to command position</span></td> </tr> <tr id="addr-B784"> <td class="addr"><a href="#addr-B784">B784</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&101A">wksp_disc_op_command</span></span> <span class="comment">; Store read/write disc command</span></td> </tr> <tr id="addr-B787"> <td class="addr"><a href="#addr-B787">B787</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B789"> <td class="addr"><a href="#addr-B789">B789</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00C8">zp_temp_ptr</span></span> <span class="comment">; Get PTR low (byte offset in sector)</span></td> </tr> <tr id="addr-B78B"> <td class="addr"><a href="#addr-B78B">B78B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Compare with 1 to set carry</span></td> </tr> <tr id="addr-B78D"> <td class="addr"><a href="#addr-B78D">B78D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Get channel start sector low</span></td> </tr> <tr id="addr-B790"> <td class="addr"><a href="#addr-B790">B790</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00C9">zp_temp_ptr_1</span></span> <span class="comment">; Add PTR mid-low for disc sector</span></td> </tr> <tr id="addr-B792"> <td class="addr"><a href="#addr-B792">B792</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&101D">wksp_disc_op_sector_lo</span></span> <span class="comment">; Store disc op sector low byte</span></td> </tr> <tr id="addr-B795"> <td class="addr"><a href="#addr-B795">B795</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Get channel start sector mid</span></td> </tr> <tr id="addr-B798"> <td class="addr"><a href="#addr-B798">B798</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00CA">zp_temp_ptr_2</span></span> <span class="comment">; Add PTR mid-high</span></td> </tr> <tr id="addr-B79A"> <td class="addr"><a href="#addr-B79A">B79A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&101C">wksp_disc_op_sector_mid</span></span> <span class="comment">; Store disc op sector mid byte</span></td> </tr> <tr id="addr-B79D"> <td class="addr"><a href="#addr-B79D">B79D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel start sector+drive</span></td> </tr> <tr id="addr-B7A0"> <td class="addr"><a href="#addr-B7A0">B7A0</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&00CB">zp_temp_ptr_3</span></span> <span class="comment">; Add PTR high</span></td> </tr> <tr id="addr-B7A2"> <td class="addr"><a href="#addr-B7A2">B7A2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&101B">wksp_disc_op_sector</span></span> <span class="comment">; Store disc op sector high byte</span></td> </tr> <tr id="addr-B7A5"> <td class="addr"><a href="#addr-B7A5">B7A5</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Y=4: save 5 bytes of CSD state</span></td> </tr> <tr id="addr-B7A7"> <td class="addr"><a href="#addr-B7A7">B7A7</a></td> <td><span class="label">.save_csd_state_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B7AE">← B7AE BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1113">wksp_csd_sector</span>,y</span> <span class="comment">; Get CSD sector/drive byte</span></td> </tr> <tr id="addr-B7AA"> <td class="addr"><a href="#addr-B7AA">B7AA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102B">wksp_csd_sector_temp</span>,y</span> <span class="comment">; Save in temp workspace</span></td> </tr> <tr id="addr-B7AD"> <td class="addr"><a href="#addr-B7AD">B7AD</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte (decreasing)</span></td> </tr> <tr id="addr-B7AE"> <td class="addr"><a href="#addr-B7AE">B7AE</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B7A7" data-tip="&B7A7">save_csd_state_loop</a></span> <span class="comment">; Loop for 5 bytes</span></td> </tr> <tr id="addr-B7B0"> <td class="addr"><a href="#addr-B7B0">B7B0</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Clear current drive (Y=0)</span></td> </tr> <tr id="addr-B7B3"> <td class="addr"><a href="#addr-B7B3">B7B3</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&101E">wksp_disc_op_sector_count</span></span> <span class="comment">; Clear disc op sector count</span></td> </tr> <tr id="addr-B7B6"> <td class="addr"><a href="#addr-B7B6">B7B6</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&101F">wksp_disc_op_control</span></span> <span class="comment">; Clear disc op control byte</span></td> </tr> <tr id="addr-B7B9"> <td class="addr"><a href="#addr-B7B9">B7B9</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&1020">wksp_disc_op_transfer_len</span></span> <span class="comment">; Clear disc op transfer length</span></td> </tr> <tr id="addr-B7BC"> <td class="addr"><a href="#addr-B7BC">B7BC</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for sector calculation</span></td> </tr> <tr id="addr-B7BD"> <td class="addr"><a href="#addr-B7BD">B7BD</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; X=2: add 3-byte sector count</span></td> </tr> <tr id="addr-B7BF"> <td class="addr"><a href="#addr-B7BF">B7BF</a></td> <td><span class="label">.add_sector_count_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B7CD">← B7CD BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1041">wksp_osfile_block_1</span>,y</span> <span class="comment">; Get remaining count byte</span></td> </tr> <tr id="addr-B7C2"> <td class="addr"><a href="#addr-B7C2">B7C2</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1021">wksp_disc_op_xfer_len_1</span>,y</span> <span class="comment">; Copy to disc op transfer length</span></td> </tr> <tr id="addr-B7C5"> <td class="addr"><a href="#addr-B7C5">B7C5</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&10B9">wksp_osgbpb_data_addr_1</span>,y</span> <span class="comment">; Add to cumulative address</span></td> </tr> <tr id="addr-B7C8"> <td class="addr"><a href="#addr-B7C8">B7C8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B9">wksp_osgbpb_data_addr_1</span>,y</span> <span class="comment">; Store updated address</span></td> </tr> <tr id="addr-B7CB"> <td class="addr"><a href="#addr-B7CB">B7CB</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-B7CC"> <td class="addr"><a href="#addr-B7CC">B7CC</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next sector byte</span></td> </tr> <tr id="addr-B7CD"> <td class="addr"><a href="#addr-B7CD">B7CD</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B7BF" data-tip="&B7BF">add_sector_count_loop</a></span> <span class="comment">; Loop for 3 bytes</span></td> </tr> <tr id="addr-B7CF"> <td class="addr"><a href="#addr-B7CF">B7CF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-AAA6" data-tip="&AAA6 – Flush buffers and set file pointer">validate_and_set_ptr</a></span> <span class="comment">; Flush channel ensure buffers</span></td> </tr> <tr id="addr-B7D2"> <td class="addr"><a href="#addr-B7D2">B7D2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8A3D" data-tip="&8A3D – Execute multi-sector disc command">multi_sector_disc_command</a></span> <span class="comment">; Execute multi-sector disc command</span></td> </tr> <tr id="addr-B7D5"> <td class="addr"><a href="#addr-B7D5">B7D5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Restore saved drive number</span></td> </tr> <tr id="addr-B7D8"> <td class="addr"><a href="#addr-B7D8">B7D8</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Set as current drive</span></td> </tr> <tr id="addr-B7DB"> <td class="addr"><a href="#addr-B7DB">B7DB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A=&FF: mark saved drive as unused</span></td> </tr> <tr id="addr-B7DD"> <td class="addr"><a href="#addr-B7DD">B7DD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102F">wksp_saved_drive</span></span> <span class="comment">; Store in saved drive slot</span></td> </tr> <tr id="addr-B7E0"> <td class="addr"><a href="#addr-B7E0">B7E0</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&102E">wksp_alt_sector_hi</span></span> <span class="comment">; Mark alt workspace as unused</span></td> </tr> <tr id="addr-B7E3"> <td class="addr"><a href="#addr-B7E3">B7E3</a></td> <td><span class="label">.check_remaining_buffered<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B768">← B768 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Check for remaining buffered bytes</span></td> </tr> <tr id="addr-B7E6"> <td class="addr"><a href="#addr-B7E6">B7E6</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B7EB" data-tip="&B7EB">calc_remaining_sector</a></span> <span class="comment">; Non-zero: more bytes in buffer</span></td> </tr> <tr id="addr-B7E8"> <td class="addr"><a href="#addr-B7E8">B7E8</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B710" data-tip="&B710">save_and_flush_after_transfer</a></span> <span class="comment">; Zero: finish via save and return</span></td> </tr> <tr id="addr-B7EB"> <td class="addr"><a href="#addr-B7EB">B7EB</a></td> <td><span class="label">.calc_remaining_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B7E6">← B7E6 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00CF">zp_channel_offset</span></span> <span class="comment">; Get channel index</span></td> </tr> <tr id="addr-B7ED"> <td class="addr"><a href="#addr-B7ED">B7ED</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for sector addition</span></td> </tr> <tr id="addr-B7EE"> <td class="addr"><a href="#addr-B7EE">B7EE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11CA">wksp_ch_start_sec_ml</span>,x</span> <span class="comment">; Get channel start sector low</span></td> </tr> <tr id="addr-B7F1"> <td class="addr"><a href="#addr-B7F1">B7F1</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&109B">wksp_new_ptr_mid</span></span> <span class="comment">; Add remaining PTR low</span></td> </tr> <tr id="addr-B7F4"> <td class="addr"><a href="#addr-B7F4">B7F4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1096">wksp_osgbpb_sector_lo</span></span> <span class="comment">; Store result sector low</span></td> </tr> <tr id="addr-B7F7"> <td class="addr"><a href="#addr-B7F7">B7F7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11C0">wksp_ch_start_sec_mh</span>,x</span> <span class="comment">; Get channel start sector mid</span></td> </tr> <tr id="addr-B7FA"> <td class="addr"><a href="#addr-B7FA">B7FA</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&109C">wksp_new_ptr_mid_hi</span></span> <span class="comment">; Add remaining PTR mid</span></td> </tr> <tr id="addr-B7FD"> <td class="addr"><a href="#addr-B7FD">B7FD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1097">wksp_osgbpb_sector_mid</span></span> <span class="comment">; Store result sector mid</span></td> </tr> <tr id="addr-B800"> <td class="addr"><a href="#addr-B800">B800</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&11B6">wksp_ch_start_sec_h</span>,x</span> <span class="comment">; Get channel start sector+drive</span></td> </tr> <tr id="addr-B803"> <td class="addr"><a href="#addr-B803">B803</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="ext-label" data-tip="&109D">wksp_new_ptr_hi</span></span> <span class="comment">; Add remaining PTR high</span></td> </tr> <tr id="addr-B806"> <td class="addr"><a href="#addr-B806">B806</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&1098">wksp_osgbpb_sector_hi</span></span> <span class="comment">; Store result sector high</span></td> </tr> <tr id="addr-B809"> <td class="addr"><a href="#addr-B809">B809</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; A=2: compare against function code</span></td> </tr> <tr id="addr-B80B"> <td class="addr"><a href="#addr-B80B">B80B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; C set if write, clear if read</span></td> </tr> <tr id="addr-B80E"> <td class="addr"><a href="#addr-B80E">B80E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; A=&80: base disc command</span></td> </tr> <tr id="addr-B810"> <td class="addr"><a href="#addr-B810">B810</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate C to form read/write command</span></td> </tr> <tr id="addr-B811"> <td class="addr"><a href="#addr-B811">B811</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-ABD8" data-tip="&ABD8 – Find or allocate a buffer for a sector">find_buffer_for_sector</a></span> <span class="comment">; Find/load buffer for remaining sector</span></td> </tr> <tr id="addr-B814"> <td class="addr"><a href="#addr-B814">B814</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear start position</span></td> </tr> <tr id="addr-B816"> <td class="addr"><a href="#addr-B816">B816</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Store start at beginning of sector</span></td> </tr> <tr id="addr-B819"> <td class="addr"><a href="#addr-B819">B819</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&109A">wksp_new_ptr_lo</span></span> <span class="comment">; Get bytes remaining in buffer</span></td> </tr> <tr id="addr-B81C"> <td class="addr"><a href="#addr-B81C">B81C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B7">wksp_osgbpb_end</span></span> <span class="comment">; Store as transfer end position</span></td> </tr> <tr id="addr-B81F"> <td class="addr"><a href="#addr-B81F">B81F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B980" data-tip="&B980 – Transfer sector bytes between buffer and memory">transfer_sector_bytes</a></span> <span class="comment">; Transfer remaining bytes in sector</span></td> </tr> <tr id="addr-B822"> <td class="addr"><a href="#addr-B822">B822</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B710" data-tip="&B710">save_and_flush_after_transfer</a></span> <span class="comment">; Finish via save and return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B825"> <td colspan="2"><div class="sub-header"> <h3>Set up OSGBPB output buffer</h3> <div class="sub-desc"><p>Configure the output buffer for OSGBPB A=5-8. Claims the Tube if the target address is in second processor memory.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B825">B825</a></td> <td><span class="label">.setup_osgbpb_output_buffer<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-B8A1">← B8A1 JSR</a><a href="#addr-B8E1">← B8E1 JSR</a><a href="#addr-B905">← B905 JSR</a><a href="#addr-B920">← B920 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Tube in use (bit 7 of flags)?</span></td> </tr> <tr id="addr-B827"> <td class="addr"><a href="#addr-B827">B827</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B84C" data-tip="&B84C">setup_output_pointer</a></span> <span class="comment">; No Tube: skip to buffer setup</span></td> </tr> <tr id="addr-B829"> <td class="addr"><a href="#addr-B829">B829</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10BA">wksp_osgbpb_data_addr_2</span></span> <span class="comment">; Get output address byte 3</span></td> </tr> <tr id="addr-B82C"> <td class="addr"><a href="#addr-B82C">B82C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="254 &FE %11111110">#&fe</span></span> <span class="comment">; Address < &FE00?</span></td> </tr> <tr id="addr-B82E"> <td class="addr"><a href="#addr-B82E">B82E</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B837" data-tip="&B837">claim_tube_for_output</a></span> <span class="comment">; Yes: second processor, claim Tube</span></td> </tr> <tr id="addr-B830"> <td class="addr"><a href="#addr-B830">B830</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10BB">wksp_osgbpb_data_addr_3</span></span> <span class="comment">; Get output address byte 4</span></td> </tr> <tr id="addr-B833"> <td class="addr"><a href="#addr-B833">B833</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Address = &FFxx (host memory)?</span></td> </tr> <tr id="addr-B835"> <td class="addr"><a href="#addr-B835">B835</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B84C" data-tip="&B84C">setup_output_pointer</a></span> <span class="comment">; Yes: skip Tube claim</span></td> </tr> <tr id="addr-B837"> <td class="addr"><a href="#addr-B837">B837</a></td> <td><span class="label">.claim_tube_for_output<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B82E">← B82E BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save flags for restore after Tube</span></td> </tr> <tr id="addr-B838"> <td class="addr"><a href="#addr-B838">B838</a></td> <td> <span class="opcode">SEI</span> <span class="comment">; Disable interrupts for Tube claim</span></td> </tr> <tr id="addr-B839"> <td class="addr"><a href="#addr-B839">B839</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-803B" data-tip="&803B">claim_tube_retry</a></span> <span class="comment">; Claim Tube for transfer</span></td> </tr> <tr id="addr-B83C"> <td class="addr"><a href="#addr-B83C">B83C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Set bit 6: Tube data transfer active</span></td> </tr> <tr id="addr-B83E"> <td class="addr"><a href="#addr-B83E">B83E</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; OR with &40 flag</span></td> </tr> <tr id="addr-B840"> <td class="addr"><a href="#addr-B840">B840</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-B842"> <td class="addr"><a href="#addr-B842">B842</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: Tube read transfer type</span></td> </tr> <tr id="addr-B844"> <td class="addr"><a href="#addr-B844">B844</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="184 &B8 %10111000">#&b8</span></span> <span class="comment">; X=&B8: Tube address workspace low</span></td> </tr> <tr id="addr-B846"> <td class="addr"><a href="#addr-B846">B846</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: Tube address workspace high</span></td> </tr> <tr id="addr-B848"> <td class="addr"><a href="#addr-B848">B848</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&0406">tube_entry</span></span> <span class="comment">; Start Tube transfer</span></td> </tr> <tr id="addr-B84B"> <td class="addr"><a href="#addr-B84B">B84B</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags (re-enable interrupts)</span></td> </tr> <tr id="addr-B84C"> <td class="addr"><a href="#addr-B84C">B84C</a></td> <td><span class="label">.setup_output_pointer<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B827">← B827 BPL</a><a href="#addr-B835">← B835 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear output byte counter</span></td> </tr> <tr id="addr-B84E"> <td class="addr"><a href="#addr-B84E">B84E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00BD">zp_buf_src_hi</span></span> <span class="comment">; Store zero in output byte counter</span></td> </tr> <tr id="addr-B850"> <td class="addr"><a href="#addr-B850">B850</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B8">wksp_osgbpb_data_addr</span></span> <span class="comment">; Get output address low byte</span></td> </tr> <tr id="addr-B853"> <td class="addr"><a href="#addr-B853">B853</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B2">zp_mem_ptr_lo</span></span> <span class="comment">; Store in output pointer low</span></td> </tr> <tr id="addr-B855"> <td class="addr"><a href="#addr-B855">B855</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B9">wksp_osgbpb_data_addr_1</span></span> <span class="comment">; Get output address high byte</span></td> </tr> <tr id="addr-B858"> <td class="addr"><a href="#addr-B858">B858</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B3">zp_mem_ptr_hi</span></span> <span class="comment">; Store in output pointer high</span></td> </tr> <tr id="addr-B85A"> <td class="addr"><a href="#addr-B85A">B85A</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (buffer ready)</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B85B"> <td colspan="2"><div class="sub-header"> <h3>Output byte to Tube or host buffer</h3> <div class="sub-desc"><p>Write byte in A to the OSGBPB output destination. If Tube is active, sends via Tube R3; otherwise stores via (zp_mem_ptr) indirect and advances the byte counter.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>A</td><td>byte to output</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>preserved</td></tr> <tr><td>X</td><td>preserved</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B85B">B85B</a></td> <td><span class="label">.output_byte_to_buffer<span class="ref-badge">←9</span><span class="ref-popup"><a href="#addr-B874">← B874 JSR</a><a href="#addr-B889">← B889 JSR</a><a href="#addr-B8B5">← B8B5 JSR</a><a href="#addr-B8C4">← B8C4 JSR</a><a href="#addr-B8CE">← B8CE JSR</a><a href="#addr-B8D8">← B8D8 JSR</a><a href="#addr-B8E6">← B8E6 JSR</a><a href="#addr-B902">← B902 JMP</a><a href="#addr-B90A">← B90A JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Tube active (V flag)?</span></td> </tr> <tr id="addr-B85D"> <td class="addr"><a href="#addr-B85D">B85D</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-B863" data-tip="&B863">output_byte_direct</a></span> <span class="comment">; No: write to host memory</span></td> </tr> <tr id="addr-B85F"> <td class="addr"><a href="#addr-B85F">B85F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FEE5">tube_data_register_3</span></span> <span class="comment">; Write byte to Tube R3 data register</span></td> </tr> <tr id="addr-B862"> <td class="addr"><a href="#addr-B862">B862</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-B863"> <td class="addr"><a href="#addr-B863">B863</a></td> <td><span class="label">.output_byte_direct<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B85D">← B85D BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00BC">zp_buf_src_lo</span></span> <span class="comment">; Save Y (caller's index)</span></td> </tr> <tr id="addr-B865"> <td class="addr"><a href="#addr-B865">B865</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00BD">zp_buf_src_hi</span></span> <span class="comment">; Get output byte counter as offset</span></td> </tr> <tr id="addr-B867"> <td class="addr"><a href="#addr-B867">B867</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B2">zp_mem_ptr_lo</span>),y</span> <span class="comment">; Store byte at (zp_b2)+offset</span></td> </tr> <tr id="addr-B869"> <td class="addr"><a href="#addr-B869">B869</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00BD">zp_buf_src_hi</span></span> <span class="comment">; Increment output byte counter</span></td> </tr> <tr id="addr-B86B"> <td class="addr"><a href="#addr-B86B">B86B</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B86F" data-tip="&B86F">restore_caller_y</a></span> <span class="comment">; No page crossing: restore Y</span></td> </tr> <tr id="addr-B86D"> <td class="addr"><a href="#addr-B86D">B86D</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B3">zp_mem_ptr_hi</span></span> <span class="comment">; Page crossed: increment pointer high</span></td> </tr> <tr id="addr-B86F"> <td class="addr"><a href="#addr-B86F">B86F</a></td> <td><span class="label">.restore_caller_y<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B86B">← B86B BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00BC">zp_buf_src_lo</span></span> <span class="comment">; Restore Y (caller's index)</span></td> </tr> <tr id="addr-B871"> <td class="addr"><a href="#addr-B871">B871</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B872"> <td colspan="2"><div class="sub-header"> <h3>Output 10-byte directory entry name</h3> <div class="sub-desc"><p>Write name length byte then 10 characters from (zp_text_ptr), replacing control chars with spaces.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B872">B872</a></td> <td><span class="label">.output_dir_entry_name<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B8F7">← B8F7 JSR</a><a href="#addr-B91B">← B91B JSR</a><a href="#addr-B95D">← B95D JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; A=&0A: name is 10 bytes long</span></td> </tr> <tr id="addr-B874"> <td class="addr"><a href="#addr-B874">B874</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output name length byte</span></td> </tr> <tr id="addr-B877"> <td class="addr"><a href="#addr-B877">B877</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for first iteration</span></td> </tr> <tr id="addr-B878"> <td class="addr"><a href="#addr-B878">B878</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; X=9: countdown for 10 name bytes</span></td> </tr> <tr id="addr-B87A"> <td class="addr"><a href="#addr-B87A">B87A</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: will increment to 0 first</span></td> </tr> <tr id="addr-B87C"> <td class="addr"><a href="#addr-B87C">B87C</a></td> <td><span class="label">.output_name_char_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B88D">← B88D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Next name byte position</span></td> </tr> <tr id="addr-B87D"> <td class="addr"><a href="#addr-B87D">B87D</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B889" data-tip="&B889">output_printable_char</a></span> <span class="comment">; C clear from prev: skip fetch</span></td> </tr> <tr id="addr-B87F"> <td class="addr"><a href="#addr-B87F">B87F</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get name byte from entry</span></td> </tr> <tr id="addr-B881"> <td class="addr"><a href="#addr-B881">B881</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7 (attribute flags)</span></td> </tr> <tr id="addr-B883"> <td class="addr"><a href="#addr-B883">B883</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Printable character (>= '!')?</span></td> </tr> <tr id="addr-B885"> <td class="addr"><a href="#addr-B885">B885</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B889" data-tip="&B889">output_printable_char</a></span> <span class="comment">; Yes: output as-is</span></td> </tr> <tr id="addr-B887"> <td class="addr"><a href="#addr-B887">B887</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Control char: replace with space</span></td> </tr> <tr id="addr-B889"> <td class="addr"><a href="#addr-B889">B889</a></td> <td><span class="label">.output_printable_char<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B87D">← B87D BCC</a><a href="#addr-B885">← B885 BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output character to buffer/Tube</span></td> </tr> <tr id="addr-B88C"> <td class="addr"><a href="#addr-B88C">B88C</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Next character</span></td> </tr> <tr id="addr-B88D"> <td class="addr"><a href="#addr-B88D">B88D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B87C" data-tip="&B87C">output_name_char_loop</a></span> <span class="comment">; Loop for 10 characters</span></td> </tr> <tr id="addr-B88F"> <td class="addr"><a href="#addr-B88F">B88F</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-B890"> <td class="addr"><a href="#addr-B890">B890</a></td> <td><span class="label">.dispatch_dir_info_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B5A0">← B5A0 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Subtract 5 to get sub-function 0-3</span></td> </tr> <tr id="addr-B892"> <td class="addr"><a href="#addr-B892">B892</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Transfer to Y for dispatch</span></td> </tr> <tr id="addr-B893"> <td class="addr"><a href="#addr-B893">B893</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B8A1" data-tip="&B8A1">read_dir_title_handler</a></span> <span class="comment">; Y=0 (A=5): read title/boot/drive</span></td> </tr> <tr id="addr-B895"> <td class="addr"><a href="#addr-B895">B895</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement for next check</span></td> </tr> <tr id="addr-B896"> <td class="addr"><a href="#addr-B896">B896</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B8E1" data-tip="&B8E1">read_csd_name_handler</a></span> <span class="comment">; Y=0 (A=6): read CSD name</span></td> </tr> <tr id="addr-B898"> <td class="addr"><a href="#addr-B898">B898</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement for next check</span></td> </tr> <tr id="addr-B899"> <td class="addr"><a href="#addr-B899">B899</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B905" data-tip="&B905">read_lib_name_handler</a></span> <span class="comment">; Y=0 (A=7): read library name</span></td> </tr> <tr id="addr-B89B"> <td class="addr"><a href="#addr-B89B">B89B</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Decrement for next check</span></td> </tr> <tr id="addr-B89C"> <td class="addr"><a href="#addr-B89C">B89C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B8DB" data-tip="&B8DB">release_tube_and_return</a></span> <span class="comment">; Y!=0: invalid sub-function, exit</span></td> </tr> <tr id="addr-B89E"> <td class="addr"><a href="#addr-B89E">B89E</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B920" data-tip="&B920">read_filenames_handler</a></span> <span class="comment">; A=8: read filenames from CSD</span></td> </tr> <tr id="addr-B8A1"> <td class="addr"><a href="#addr-B8A1">B8A1</a></td> <td><span class="label">.read_dir_title_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B893">← B893 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B825" data-tip="&B825 – Set up OSGBPB output buffer">setup_osgbpb_output_buffer</a></span> <span class="comment">; Set up output buffer/Tube</span></td> </tr> <tr id="addr-B8A4"> <td class="addr"><a href="#addr-B8A4">B8A4</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: will increment to 0 first</span></td> </tr> <tr id="addr-B8A6"> <td class="addr"><a href="#addr-B8A6">B8A6</a></td> <td><span class="label">.scan_title_length_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B8B2">← B8B2 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Next title byte</span></td> </tr> <tr id="addr-B8A7"> <td class="addr"><a href="#addr-B8A7">B8A7</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&16D9">dir_title</span>,y</span> <span class="comment">; Get directory title character</span></td> </tr> <tr id="addr-B8AA"> <td class="addr"><a href="#addr-B8AA">B8AA</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7</span></td> </tr> <tr id="addr-B8AC"> <td class="addr"><a href="#addr-B8AC">B8AC</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Printable (>= space)?</span></td> </tr> <tr id="addr-B8AE"> <td class="addr"><a href="#addr-B8AE">B8AE</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B8B4" data-tip="&B8B4">output_title_length</a></span> <span class="comment">; Control char: end of title</span></td> </tr> <tr id="addr-B8B0"> <td class="addr"><a href="#addr-B8B0">B8B0</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="19 &13 %00010011 DC3">#&13</span></span> <span class="comment">; Reached max 19 chars?</span></td> </tr> <tr id="addr-B8B2"> <td class="addr"><a href="#addr-B8B2">B8B2</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B8A6" data-tip="&B8A6">scan_title_length_loop</a></span> <span class="comment">; No: continue scanning title</span></td> </tr> <tr id="addr-B8B4"> <td class="addr"><a href="#addr-B8B4">B8B4</a></td> <td><span class="label">.output_title_length<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B8AE">← B8AE BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TYA</span> <span class="comment">; Output title length byte</span></td> </tr> <tr id="addr-B8B5"> <td class="addr"><a href="#addr-B8B5">B8B5</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Write length to buffer/Tube</span></td> </tr> <tr id="addr-B8B8"> <td class="addr"><a href="#addr-B8B8">B8B8</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: will increment to 0 first</span></td> </tr> <tr id="addr-B8BA"> <td class="addr"><a href="#addr-B8BA">B8BA</a></td> <td><span class="label">.output_title_chars_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B8C9">← B8C9 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Next title byte</span></td> </tr> <tr id="addr-B8BB"> <td class="addr"><a href="#addr-B8BB">B8BB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&16D9">dir_title</span>,y</span> <span class="comment">; Get directory title character</span></td> </tr> <tr id="addr-B8BE"> <td class="addr"><a href="#addr-B8BE">B8BE</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Strip bit 7</span></td> </tr> <tr id="addr-B8C0"> <td class="addr"><a href="#addr-B8C0">B8C0</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Printable (>= space)?</span></td> </tr> <tr id="addr-B8C2"> <td class="addr"><a href="#addr-B8C2">B8C2</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B8CB" data-tip="&B8CB">output_boot_and_drive</a></span> <span class="comment">; Control char: done outputting title</span></td> </tr> <tr id="addr-B8C4"> <td class="addr"><a href="#addr-B8C4">B8C4</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output title character</span></td> </tr> <tr id="addr-B8C7"> <td class="addr"><a href="#addr-B8C7">B8C7</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="imm" data-tip="19 &13 %00010011 DC3">#&13</span></span> <span class="comment">; Reached max 19 chars?</span></td> </tr> <tr id="addr-B8C9"> <td class="addr"><a href="#addr-B8C9">B8C9</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B8BA" data-tip="&B8BA">output_title_chars_loop</a></span> <span class="comment">; No: continue outputting</span></td> </tr> <tr id="addr-B8CB"> <td class="addr"><a href="#addr-B8CB">B8CB</a></td> <td><span class="label">.output_boot_and_drive<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B8C2">← B8C2 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0FFD">fsm_s1_boot_option</span></span> <span class="comment">; Get boot option from FSM sector 1</span></td> </tr> <tr id="addr-B8CE"> <td class="addr"><a href="#addr-B8CE">B8CE</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output boot option byte</span></td> </tr> <tr id="addr-B8D1"> <td class="addr"><a href="#addr-B8D1">B8D1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive number</span></td> </tr> <tr id="addr-B8D4"> <td class="addr"><a href="#addr-B8D4">B8D4</a></td> <td> <span class="opcode">ASL</span> <span class="comment">; Shift drive into low 3 bits</span></td> </tr> <tr id="addr-B8D5"> <td class="addr"><a href="#addr-B8D5">B8D5</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Second shift</span></td> </tr> <tr id="addr-B8D6"> <td class="addr"><a href="#addr-B8D6">B8D6</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Third shift</span></td> </tr> <tr id="addr-B8D7"> <td class="addr"><a href="#addr-B8D7">B8D7</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Fourth shift (now in bits 0-2)</span></td> </tr> <tr id="addr-B8D8"> <td class="addr"><a href="#addr-B8D8">B8D8</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output drive number byte</span></td> </tr> <tr id="addr-B8DB"> <td class="addr"><a href="#addr-B8DB">B8DB</a></td> <td><span class="label">.release_tube_and_return<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-B89C">← B89C BNE</a><a href="#addr-B8FA">← B8FA BMI</a><a href="#addr-B91E">← B91E BMI</a><a href="#addr-B933">← B933 BEQ</a><a href="#addr-B93D">← B93D BCS</a><a href="#addr-B97D">← B97D JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8043" data-tip="&8043 – Release Tube if in use">release_tube</a></span> <span class="comment">; Release Tube if in use</span></td> </tr> <tr id="addr-B8DE"> <td class="addr"><a href="#addr-B8DE">B8DE</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B716" data-tip="&B716">prepare_osgbpb_return</a></span> <span class="comment">; Return via OSGBPB exit path</span></td> </tr> <tr id="addr-B8E1"> <td class="addr"><a href="#addr-B8E1">B8E1</a></td> <td><span class="label">.read_csd_name_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B896">← B896 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B825" data-tip="&B825 – Set up OSGBPB output buffer">setup_osgbpb_output_buffer</a></span> <span class="comment">; Set up output buffer/Tube</span></td> </tr> <tr id="addr-B8E4"> <td class="addr"><a href="#addr-B8E4">B8E4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: drive prefix is 1 char long</span></td> </tr> <tr id="addr-B8E6"> <td class="addr"><a href="#addr-B8E6">B8E6</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output drive prefix length</span></td> </tr> <tr id="addr-B8E9"> <td class="addr"><a href="#addr-B8E9">B8E9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; Get current drive number</span></td> </tr> <tr id="addr-B8EC"> <td class="addr"><a href="#addr-B8EC">B8EC</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B8FC" data-tip="&B8FC">drive_to_ascii_digit</a></span> <span class="comment">; Convert drive to ASCII digit</span></td> </tr> <tr id="addr-B8EF"> <td class="addr"><a href="#addr-B8EF">B8EF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: CSD name starts at offset 0</span></td> </tr> <tr id="addr-B8F1"> <td class="addr"><a href="#addr-B8F1">B8F1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store CSD name pointer low</span></td> </tr> <tr id="addr-B8F3"> <td class="addr"><a href="#addr-B8F3">B8F3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="17 &11 %00010001 DC1">#&11</span></span> <span class="comment">; A=&11: CSD name is at &1100</span></td> </tr> <tr id="addr-B8F5"> <td class="addr"><a href="#addr-B8F5">B8F5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store CSD name pointer high</span></td> </tr> <tr id="addr-B8F7"> <td class="addr"><a href="#addr-B8F7">B8F7</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B872" data-tip="&B872 – Output 10-byte directory entry name">output_dir_entry_name</a></span> <span class="comment">; Output 10-byte CSD directory name</span></td> </tr> <tr id="addr-B8FA"> <td class="addr"><a href="#addr-B8FA">B8FA</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B8DB" data-tip="&B8DB">release_tube_and_return</a></span> <span class="comment">; Exit via cleanup</span></td> </tr> <tr id="addr-B8FC"> <td class="addr"><a href="#addr-B8FC">B8FC</a></td> <td><span class="label">.drive_to_ascii_digit<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B8EC">← B8EC JSR</a><a href="#addr-B910">← B910 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ASL</span> <span class="comment">; Shift drive into high nibble</span></td> </tr> <tr id="addr-B8FD"> <td class="addr"><a href="#addr-B8FD">B8FD</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Continue shift</span></td> </tr> <tr id="addr-B8FE"> <td class="addr"><a href="#addr-B8FE">B8FE</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Continue shift</span></td> </tr> <tr id="addr-B8FF"> <td class="addr"><a href="#addr-B8FF">B8FF</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Continue shift (now in bits 4-7)</span></td> </tr> <tr id="addr-B900"> <td class="addr"><a href="#addr-B900">B900</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="48 &30 %00110000 '0'">#&30</span></span> <span class="comment">; Add &30 for ASCII '0'</span></td> </tr> <tr id="addr-B902"> <td class="addr"><a href="#addr-B902">B902</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output via cb85b</span></td> </tr> <tr id="addr-B905"> <td class="addr"><a href="#addr-B905">B905</a></td> <td><span class="label">.read_lib_name_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B899">← B899 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B825" data-tip="&B825 – Set up OSGBPB output buffer">setup_osgbpb_output_buffer</a></span> <span class="comment">; Set up output buffer/Tube</span></td> </tr> <tr id="addr-B908"> <td class="addr"><a href="#addr-B908">B908</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; A=1: drive prefix is 1 char long</span></td> </tr> <tr id="addr-B90A"> <td class="addr"><a href="#addr-B90A">B90A</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B85B" data-tip="&B85B – Output byte to Tube or host buffer">output_byte_to_buffer</a></span> <span class="comment">; Output drive prefix length</span></td> </tr> <tr id="addr-B90D"> <td class="addr"><a href="#addr-B90D">B90D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&111B">wksp_lib_sector_hi</span></span> <span class="comment">; Get library drive number</span></td> </tr> <tr id="addr-B910"> <td class="addr"><a href="#addr-B910">B910</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B8FC" data-tip="&B8FC">drive_to_ascii_digit</a></span> <span class="comment">; Convert drive to ASCII digit</span></td> </tr> <tr id="addr-B913"> <td class="addr"><a href="#addr-B913">B913</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; A=&0A: library name at offset &0A</span></td> </tr> <tr id="addr-B915"> <td class="addr"><a href="#addr-B915">B915</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store library name pointer low</span></td> </tr> <tr id="addr-B917"> <td class="addr"><a href="#addr-B917">B917</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="17 &11 %00010001 DC1">#&11</span></span> <span class="comment">; A=&11: library name is at &110A</span></td> </tr> <tr id="addr-B919"> <td class="addr"><a href="#addr-B919">B919</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store library name pointer high</span></td> </tr> <tr id="addr-B91B"> <td class="addr"><a href="#addr-B91B">B91B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B872" data-tip="&B872 – Output 10-byte directory entry name">output_dir_entry_name</a></span> <span class="comment">; Output 10-byte library dir name</span></td> </tr> <tr id="addr-B91E"> <td class="addr"><a href="#addr-B91E">B91E</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B8DB" data-tip="&B8DB">release_tube_and_return</a></span> <span class="comment">; Exit via cleanup</span></td> </tr> <tr id="addr-B920"> <td class="addr"><a href="#addr-B920">B920</a></td> <td><span class="label">.read_filenames_handler<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B89E">← B89E JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B825" data-tip="&B825 – Set up OSGBPB output buffer">setup_osgbpb_output_buffer</a></span> <span class="comment">; Set up output buffer/Tube</span></td> </tr> <tr id="addr-B923"> <td class="addr"><a href="#addr-B923">B923</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: clear result counter</span></td> </tr> <tr id="addr-B925"> <td class="addr"><a href="#addr-B925">B925</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Clear result file count</span></td> </tr> <tr id="addr-B928"> <td class="addr"><a href="#addr-B928">B928</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&16FA">dir_master_sequence</span></span> <span class="comment">; Get directory sequence number</span></td> </tr> <tr id="addr-B92B"> <td class="addr"><a href="#addr-B92B">B92B</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block byte 0</span></td> </tr> <tr id="addr-B92D"> <td class="addr"><a href="#addr-B92D">B92D</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: get requested count from block</span></td> </tr> <tr id="addr-B92F"> <td class="addr"><a href="#addr-B92F">B92F</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Get requested entry count</span></td> </tr> <tr id="addr-B931"> <td class="addr"><a href="#addr-B931">B931</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Store as entries remaining</span></td> </tr> <tr id="addr-B933"> <td class="addr"><a href="#addr-B933">B933</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B8DB" data-tip="&B8DB">release_tube_and_return</a></span> <span class="comment">; Zero entries requested: done</span></td> </tr> <tr id="addr-B935"> <td class="addr"><a href="#addr-B935">B935</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: get start index from block</span></td> </tr> <tr id="addr-B937"> <td class="addr"><a href="#addr-B937">B937</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Get starting entry index</span></td> </tr> <tr id="addr-B939"> <td class="addr"><a href="#addr-B939">B939</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Store as current entry counter</span></td> </tr> <tr id="addr-B93B"> <td class="addr"><a href="#addr-B93B">B93B</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="47 &2F %00101111 '/'">#&2f</span></span> <span class="comment">; Index >= 47? Past max entries</span></td> </tr> <tr id="addr-B93D"> <td class="addr"><a href="#addr-B93D">B93D</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B8DB" data-tip="&B8DB">release_tube_and_return</a></span> <span class="comment">; Yes: exit (no more entries)</span></td> </tr> <tr id="addr-B93F"> <td class="addr"><a href="#addr-B93F">B93F</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer index to X for loop</span></td> </tr> <tr id="addr-B940"> <td class="addr"><a href="#addr-B940">B940</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for pointer arithmetic</span></td> </tr> <tr id="addr-B941"> <td class="addr"><a href="#addr-B941">B941</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; A=5: first entry at offset &1205</span></td> </tr> <tr id="addr-B943"> <td class="addr"><a href="#addr-B943">B943</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="18 &12 %00010010 DC2">#&12</span></span> <span class="comment">; Y=&12: directory buffer page</span></td> </tr> <tr id="addr-B945"> <td class="addr"><a href="#addr-B945">B945</a></td> <td><span class="label">.skip_to_start_entry<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B94A">← B94A BCC</a><a href="#addr-B94E">← B94E BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Decrement entries to skip</span></td> </tr> <tr id="addr-B946"> <td class="addr"><a href="#addr-B946">B946</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-B950" data-tip="&B950">set_entry_pointer</a></span> <span class="comment">; Skipped enough: start reading</span></td> </tr> <tr id="addr-B948"> <td class="addr"><a href="#addr-B948">B948</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="26 &1A %00011010 SUB">#&1a</span></span> <span class="comment">; Add &1A (26 bytes per dir entry)</span></td> </tr> <tr id="addr-B94A"> <td class="addr"><a href="#addr-B94A">B94A</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B945" data-tip="&B945">skip_to_start_entry</a></span> <span class="comment">; No page crossing: continue</span></td> </tr> <tr id="addr-B94C"> <td class="addr"><a href="#addr-B94C">B94C</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Page crossing: increment page</span></td> </tr> <tr id="addr-B94D"> <td class="addr"><a href="#addr-B94D">B94D</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for next addition</span></td> </tr> <tr id="addr-B94E"> <td class="addr"><a href="#addr-B94E">B94E</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B945" data-tip="&B945">skip_to_start_entry</a></span> <span class="comment">; Continue skipping entries</span></td> </tr> <tr id="addr-B950"> <td class="addr"><a href="#addr-B950">B950</a></td> <td><span class="label">.set_entry_pointer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B946">← B946 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Store entry pointer high</span></td> </tr> <tr id="addr-B952"> <td class="addr"><a href="#addr-B952">B952</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store entry pointer low</span></td> </tr> <tr id="addr-B954"> <td class="addr"><a href="#addr-B954">B954</a></td> <td><span class="label">.output_entries_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B96F">← B96F BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: check first byte of entry</span></td> </tr> <tr id="addr-B956"> <td class="addr"><a href="#addr-B956">B956</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span>),y</span> <span class="comment">; Get entry name byte 0</span></td> </tr> <tr id="addr-B958"> <td class="addr"><a href="#addr-B958">B958</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10B5">wksp_osgbpb_mode</span></span> <span class="comment">; Store as non-zero check for output</span></td> </tr> <tr id="addr-B95B"> <td class="addr"><a href="#addr-B95B">B95B</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B971" data-tip="&B971">store_remaining_count</a></span> <span class="comment">; Zero: end of directory entries</span></td> </tr> <tr id="addr-B95D"> <td class="addr"><a href="#addr-B95D">B95D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-B872" data-tip="&B872 – Output 10-byte directory entry name">output_dir_entry_name</a></span> <span class="comment">; Output 10-byte entry name</span></td> </tr> <tr id="addr-B960"> <td class="addr"><a href="#addr-B960">B960</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Get entry pointer low</span></td> </tr> <tr id="addr-B962"> <td class="addr"><a href="#addr-B962">B962</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-B963"> <td class="addr"><a href="#addr-B963">B963</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="26 &1A %00011010 SUB">#&1a</span></span> <span class="comment">; Add &1A to advance to next entry</span></td> </tr> <tr id="addr-B965"> <td class="addr"><a href="#addr-B965">B965</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B4">zp_text_ptr_lo</span></span> <span class="comment">; Store updated entry pointer low</span></td> </tr> <tr id="addr-B967"> <td class="addr"><a href="#addr-B967">B967</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B96B" data-tip="&B96B">advance_entry_index</a></span> <span class="comment">; No page crossing</span></td> </tr> <tr id="addr-B969"> <td class="addr"><a href="#addr-B969">B969</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B5">zp_text_ptr_hi</span></span> <span class="comment">; Page crossing: increment high byte</span></td> </tr> <tr id="addr-B96B"> <td class="addr"><a href="#addr-B96B">B96B</a></td> <td><span class="label">.advance_entry_index<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B967">← B967 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Increment current entry index</span></td> </tr> <tr id="addr-B96D"> <td class="addr"><a href="#addr-B96D">B96D</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Decrement remaining count</span></td> </tr> <tr id="addr-B96F"> <td class="addr"><a href="#addr-B96F">B96F</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B954" data-tip="&B954">output_entries_loop</a></span> <span class="comment">; More entries to read: continue</span></td> </tr> <tr id="addr-B971"> <td class="addr"><a href="#addr-B971">B971</a></td> <td><span class="label">.store_remaining_count<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B95B">← B95B BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: update remaining count in block</span></td> </tr> <tr id="addr-B973"> <td class="addr"><a href="#addr-B973">B973</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Get remaining entries count</span></td> </tr> <tr id="addr-B975"> <td class="addr"><a href="#addr-B975">B975</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block byte 5</span></td> </tr> <tr id="addr-B977"> <td class="addr"><a href="#addr-B977">B977</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: update current index in block</span></td> </tr> <tr id="addr-B979"> <td class="addr"><a href="#addr-B979">B979</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Get current entry index</span></td> </tr> <tr id="addr-B97B"> <td class="addr"><a href="#addr-B97B">B97B</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00C6">zp_gspb_ptr_lo</span>),y</span> <span class="comment">; Store in control block byte 9</span></td> </tr> <tr id="addr-B97D"> <td class="addr"><a href="#addr-B97D">B97D</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-B8DB" data-tip="&B8DB">release_tube_and_return</a></span> <span class="comment">; Exit via cleanup and return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-B980"> <td colspan="2"><div class="sub-header"> <h3>Transfer sector bytes between buffer and memory</h3> <div class="sub-desc"><p>Copy bytes from position l10b6 to l10b7 within the current sector buffer, routing through direct memory, indirect via (zp_buf_dest), or the Tube.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-B980">B980</a></td> <td><span class="label">.transfer_sector_bytes<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B70D">← B70D JSR</a><a href="#addr-B720">← B720 JSR</a><a href="#addr-B81F">← B81F JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Get transfer start position</span></td> </tr> <tr id="addr-B983"> <td class="addr"><a href="#addr-B983">B983</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&10B7">wksp_osgbpb_end</span></span> <span class="comment">; Compare with end position</span></td> </tr> <tr id="addr-B986"> <td class="addr"><a href="#addr-B986">B986</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B989" data-tip="&B989">claim_tube_for_sector</a></span> <span class="comment">; Not equal: bytes to transfer</span></td> </tr> <tr id="addr-B988"> <td class="addr"><a href="#addr-B988">B988</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Equal: no bytes to transfer, return</span></td> </tr> <tr id="addr-B989"> <td class="addr"><a href="#addr-B989">B989</a></td> <td><span class="label">.claim_tube_for_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B986">← B986 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save flags for Tube check</span></td> </tr> <tr id="addr-B98A"> <td class="addr"><a href="#addr-B98A">B98A</a></td> <td> <span class="opcode">SEI</span> <span class="comment">; Disable interrupts for Tube setup</span></td> </tr> <tr id="addr-B98B"> <td class="addr"><a href="#addr-B98B">B98B</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Tube in use (bit 7 of flags)?</span></td> </tr> <tr id="addr-B98D"> <td class="addr"><a href="#addr-B98D">B98D</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-B9B5" data-tip="&B9B5">setup_buffer_pointers</a></span> <span class="comment">; No Tube: skip to direct transfer</span></td> </tr> <tr id="addr-B98F"> <td class="addr"><a href="#addr-B98F">B98F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10BA">wksp_osgbpb_data_addr_2</span></span> <span class="comment">; Get Tube address byte 3</span></td> </tr> <tr id="addr-B992"> <td class="addr"><a href="#addr-B992">B992</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="254 &FE %11111110">#&fe</span></span> <span class="comment">; Address < &FE00?</span></td> </tr> <tr id="addr-B994"> <td class="addr"><a href="#addr-B994">B994</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B99D" data-tip="&B99D">set_tube_transfer_flag</a></span> <span class="comment">; Yes: Tube address, claim it</span></td> </tr> <tr id="addr-B996"> <td class="addr"><a href="#addr-B996">B996</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10BB">wksp_osgbpb_data_addr_3</span></span> <span class="comment">; Get Tube address byte 4</span></td> </tr> <tr id="addr-B999"> <td class="addr"><a href="#addr-B999">B999</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Address = &FFxx (host memory)?</span></td> </tr> <tr id="addr-B99B"> <td class="addr"><a href="#addr-B99B">B99B</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-B9B5" data-tip="&B9B5">setup_buffer_pointers</a></span> <span class="comment">; Yes: skip Tube claim</span></td> </tr> <tr id="addr-B99D"> <td class="addr"><a href="#addr-B99D">B99D</a></td> <td><span class="label">.set_tube_transfer_flag<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B994">← B994 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Set bit 6: Tube transfer active</span></td> </tr> <tr id="addr-B99F"> <td class="addr"><a href="#addr-B99F">B99F</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; OR with &40 flag</span></td> </tr> <tr id="addr-B9A1"> <td class="addr"><a href="#addr-B9A1">B9A1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-B9A3"> <td class="addr"><a href="#addr-B9A3">B9A3</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-803B" data-tip="&803B">claim_tube_retry</a></span> <span class="comment">; Claim Tube for transfer</span></td> </tr> <tr id="addr-B9A6"> <td class="addr"><a href="#addr-B9A6">B9A6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get OSGBPB function code</span></td> </tr> <tr id="addr-B9A9"> <td class="addr"><a href="#addr-B9A9">B9A9</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; C set if A>=3 (read from file)</span></td> </tr> <tr id="addr-B9AB"> <td class="addr"><a href="#addr-B9AB">B9AB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: base for Tube direction</span></td> </tr> <tr id="addr-B9AD"> <td class="addr"><a href="#addr-B9AD">B9AD</a></td> <td> <span class="opcode">ROL</span> <span class="comment">; Rotate C to set direction bit</span></td> </tr> <tr id="addr-B9AE"> <td class="addr"><a href="#addr-B9AE">B9AE</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="184 &B8 %10111000">#&b8</span></span> <span class="comment">; X=&B8: Tube address workspace low</span></td> </tr> <tr id="addr-B9B0"> <td class="addr"><a href="#addr-B9B0">B9B0</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: Tube address workspace high</span></td> </tr> <tr id="addr-B9B2"> <td class="addr"><a href="#addr-B9B2">B9B2</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&0406">tube_entry</span></span> <span class="comment">; Start Tube transfer</span></td> </tr> <tr id="addr-B9B5"> <td class="addr"><a href="#addr-B9B5">B9B5</a></td> <td><span class="label">.setup_buffer_pointers<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-B98D">← B98D BPL</a><a href="#addr-B99B">← B99B BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-B9B6"> <td class="addr"><a href="#addr-B9B6">B9B6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B8">wksp_osgbpb_data_addr</span></span> <span class="comment">; Get data address low</span></td> </tr> <tr id="addr-B9B9"> <td class="addr"><a href="#addr-B9B9">B9B9</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-B9BA"> <td class="addr"><a href="#addr-B9BA">B9BA</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Subtract start offset for buffer ptr</span></td> </tr> <tr id="addr-B9BD"> <td class="addr"><a href="#addr-B9BD">B9BD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B2">zp_mem_ptr_lo</span></span> <span class="comment">; Store buffer pointer low</span></td> </tr> <tr id="addr-B9BF"> <td class="addr"><a href="#addr-B9BF">B9BF</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B9">wksp_osgbpb_data_addr_1</span></span> <span class="comment">; Get data address high</span></td> </tr> <tr id="addr-B9C2"> <td class="addr"><a href="#addr-B9C2">B9C2</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Subtract borrow</span></td> </tr> <tr id="addr-B9C4"> <td class="addr"><a href="#addr-B9C4">B9C4</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B3">zp_mem_ptr_hi</span></span> <span class="comment">; Store buffer pointer high</span></td> </tr> <tr id="addr-B9C6"> <td class="addr"><a href="#addr-B9C6">B9C6</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10B4">wksp_osgbpb_func</span></span> <span class="comment">; Get OSGBPB function code</span></td> </tr> <tr id="addr-B9C9"> <td class="addr"><a href="#addr-B9C9">B9C9</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; C set if A>=3 (read from file)</span></td> </tr> <tr id="addr-B9CB"> <td class="addr"><a href="#addr-B9CB">B9CB</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&10B6">wksp_osgbpb_start</span></span> <span class="comment">; Get start position as byte index</span></td> </tr> <tr id="addr-B9CE"> <td class="addr"><a href="#addr-B9CE">B9CE</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save read/write direction flag</span></td> </tr> <tr id="addr-B9CF"> <td class="addr"><a href="#addr-B9CF">B9CF</a></td> <td><span class="label">.copy_byte_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B9F8">← B9F8 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore direction flag</span></td> </tr> <tr id="addr-B9D0"> <td class="addr"><a href="#addr-B9D0">B9D0</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Tube active (V flag)?</span></td> </tr> <tr id="addr-B9D2"> <td class="addr"><a href="#addr-B9D2">B9D2</a></td> <td> <span class="opcode">BVS</span> <span class="operand"><a href="#addr-B9E2" data-tip="&B9E2">tube_byte_transfer</a></span> <span class="comment">; Yes: use Tube data path</span></td> </tr> <tr id="addr-B9D4"> <td class="addr"><a href="#addr-B9D4">B9D4</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B9DC" data-tip="&B9DC">write_byte_from_memory</a></span> <span class="comment">; C set: reading from file to memory</span></td> </tr> <tr id="addr-B9D6"> <td class="addr"><a href="#addr-B9D6">B9D6</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Read: get byte from sector buffer</span></td> </tr> <tr id="addr-B9D8"> <td class="addr"><a href="#addr-B9D8">B9D8</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00B2">zp_mem_ptr_lo</span>),y</span> <span class="comment">; Write to user memory</span></td> </tr> <tr id="addr-B9DA"> <td class="addr"><a href="#addr-B9DA">B9DA</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B9F3" data-tip="&B9F3">advance_byte_position</a></span> <span class="comment">; Always branch to advance</span></td> </tr> <tr id="addr-B9DC"> <td class="addr"><a href="#addr-B9DC">B9DC</a></td> <td><span class="label">.write_byte_from_memory<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B9D4">← B9D4 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B2">zp_mem_ptr_lo</span>),y</span> <span class="comment">; Write: get byte from user memory</span></td> </tr> <tr id="addr-B9DE"> <td class="addr"><a href="#addr-B9DE">B9DE</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Store in sector buffer</span></td> </tr> <tr id="addr-B9E0"> <td class="addr"><a href="#addr-B9E0">B9E0</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B9F3" data-tip="&B9F3">advance_byte_position</a></span> <span class="comment">; Always branch to advance</span></td> </tr> <tr id="addr-B9E2"> <td class="addr"><a href="#addr-B9E2">B9E2</a></td> <td><span class="label">.tube_byte_transfer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B9D2">← B9D2 BVS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-81F8" data-tip="&81F8">tube_delay2</a></span> <span class="comment">; Tube: delay for synchronisation</span></td> </tr> <tr id="addr-B9E5"> <td class="addr"><a href="#addr-B9E5">B9E5</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-B9EE" data-tip="&B9EE">read_byte_from_tube</a></span> <span class="comment">; C clear: writing to file from Tube</span></td> </tr> <tr id="addr-B9E7"> <td class="addr"><a href="#addr-B9E7">B9E7</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Read file: get byte from buffer</span></td> </tr> <tr id="addr-B9E9"> <td class="addr"><a href="#addr-B9E9">B9E9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FEE5">tube_data_register_3</span></span> <span class="comment">; Write to Tube R4</span></td> </tr> <tr id="addr-B9EC"> <td class="addr"><a href="#addr-B9EC">B9EC</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-B9F3" data-tip="&B9F3">advance_byte_position</a></span> <span class="comment">; Always branch to advance</span></td> </tr> <tr id="addr-B9EE"> <td class="addr"><a href="#addr-B9EE">B9EE</a></td> <td><span class="label">.read_byte_from_tube<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-B9E5">← B9E5 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FEE5">tube_data_register_3</span></span> <span class="comment">; Write file: read byte from Tube R4</span></td> </tr> <tr id="addr-B9F1"> <td class="addr"><a href="#addr-B9F1">B9F1</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span>),y</span> <span class="comment">; Store in sector buffer</span></td> </tr> <tr id="addr-B9F3"> <td class="addr"><a href="#addr-B9F3">B9F3</a></td> <td><span class="label">.advance_byte_position<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-B9DA">← B9DA BCS</a><a href="#addr-B9E0">← B9E0 BCC</a><a href="#addr-B9EC">← B9EC BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte position</span></td> </tr> <tr id="addr-B9F4"> <td class="addr"><a href="#addr-B9F4">B9F4</a></td> <td> <span class="opcode">PHP</span> <span class="comment">; Save direction flag for next byte</span></td> </tr> <tr id="addr-B9F5"> <td class="addr"><a href="#addr-B9F5">B9F5</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="ext-label" data-tip="&10B7">wksp_osgbpb_end</span></span> <span class="comment">; Reached end position?</span></td> </tr> <tr id="addr-B9F8"> <td class="addr"><a href="#addr-B9F8">B9F8</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-B9CF" data-tip="&B9CF">copy_byte_loop</a></span> <span class="comment">; No: continue copying</span></td> </tr> <tr id="addr-B9FA"> <td class="addr"><a href="#addr-B9FA">B9FA</a></td> <td> <span class="opcode">PLP</span> <span class="comment">; Restore flags</span></td> </tr> <tr id="addr-B9FB"> <td class="addr"><a href="#addr-B9FB">B9FB</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-8043" data-tip="&8043 – Release Tube if in use">release_tube</a></span> <span class="comment">; Release Tube and return</span></td> </tr> <tr id="addr-B9FE"> <td class="addr"><a href="#addr-B9FE">B9FE</a></td> <td> <span class="directive">EQUS</span> <span class="string">"."</span>, <span data-tip="13 &0D %00001101 CR">&0D</span> <span class="comment">; Unused "." + CR: dead remnant</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BA00"> <td colspan="2"><div class="sub-header"> <h3>Floppy disc command (indirect entry)</h3> <div class="sub-desc"><p>Indirect entry point for floppy disc operations. Jumps through to floppy_command.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BA00">BA00</a></td> <td><span class="label">.floppy_command_ind<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-80CC">← 80CC JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BB14" data-tip="&BB14 – Execute floppy disc command">floppy_command</a></span></td> </tr> <tr id="addr-BA03"> <td class="addr"><a href="#addr-BA03">BA03</a></td> <td><span class="label">.exec_floppy_partial_sector_buf_ind<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-8B3E">← 8B3E JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BB25" data-tip="&BB25">exec_floppy_partial_sector_buf</a></span></td> </tr> <tr id="addr-BA06"> <td class="addr"><a href="#addr-BA06">BA06</a></td> <td><span class="label">.exec_floppy_write_bput_sector_ind<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-AB3F">← AB3F JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BA26" data-tip="&BA26">exec_floppy_write_bput_sector</a></span></td> </tr> <tr id="addr-BA09"> <td class="addr"><a href="#addr-BA09">BA09</a></td> <td><span class="label">.exec_floppy_read_bput_sector_ind<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-ACA6">← ACA6 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BA2A" data-tip="&BA2A">exec_floppy_read_bput_sector</a></span></td> </tr> <tr id="addr-BA0C"> <td class="addr"><a href="#addr-BA0C">BA0C</a></td> <td><span class="label">.mark_partial_transfer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9BCA">← 9BCA JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A=&FF: mark transfer state</span></td> </tr> <tr id="addr-BA0E"> <td class="addr"><a href="#addr-BA0E">BA0E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Store in transfer workspace</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BA11"> <td colspan="2"><div class="sub-header"> <h3>Check floppy disc hardware present</h3> <div class="sub-desc"><p>Test whether the WD1770 floppy disc controller is present by probing its registers.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="3">On Exit</th><td>A</td><td>corrupted (C set if present, clear if not)</td></tr> <tr><td>X</td><td>preserved</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BA11">BA11</a></td> <td><span class="label">.floppy_check_present<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-9ACF">← 9ACF JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="90 &5A %01011010 'Z'">#&5a</span></span> <span class="comment">; Write &5A to WD1770 track register</span></td> </tr> <tr id="addr-BA13"> <td class="addr"><a href="#addr-BA13">BA13</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE85">fdc_1770_track</span></span> <span class="comment">; Write to FDC track register</span></td> </tr> <tr id="addr-BA16"> <td class="addr"><a href="#addr-BA16">BA16</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FE85">fdc_1770_track</span></span> <span class="comment">; Read back from track register</span></td> </tr> <tr id="addr-BA19"> <td class="addr"><a href="#addr-BA19">BA19</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="90 &5A %01011010 'Z'">#&5a</span></span> <span class="comment">; Does it match &5A?</span></td> </tr> <tr id="addr-BA1B"> <td class="addr"><a href="#addr-BA1B">BA1B</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BA25" data-tip="&BA25">return_42</a></span> <span class="comment">; No: WD1770 not present, return C=1</span></td> </tr> <tr id="addr-BA1D"> <td class="addr"><a href="#addr-BA1D">BA1D</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FE80">fdc_1770_drive_control</span></span> <span class="comment">; Read drive control register</span></td> </tr> <tr id="addr-BA20"> <td class="addr"><a href="#addr-BA20">BA20</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Check drive select bits (0-1)</span></td> </tr> <tr id="addr-BA22"> <td class="addr"><a href="#addr-BA22">BA22</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BA25" data-tip="&BA25">return_42</a></span> <span class="comment">; Both zero: no drive, return C=1</span></td> </tr> <tr id="addr-BA24"> <td class="addr"><a href="#addr-BA24">BA24</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; WD1770 present: C=0</span></td> </tr> <tr id="addr-BA25"> <td class="addr"><a href="#addr-BA25">BA25</a></td> <td><span class="label">.return_42<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BA1B">← BA1B BNE</a><a href="#addr-BA22">← BA22 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (C=1: not present)</span></td> </tr> <tr id="addr-BA26"> <td class="addr"><a href="#addr-BA26">BA26</a></td> <td><span class="label">.exec_floppy_write_bput_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA06">← BA06 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; A=&40: write direction flag</span></td> </tr> <tr id="addr-BA28"> <td class="addr"><a href="#addr-BA28">BA28</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BA2C" data-tip="&BA2C">store_direction_flag</a></span></td> </tr> <tr id="addr-BA2A"> <td class="addr"><a href="#addr-BA2A">BA2A</a></td> <td><span class="label">.exec_floppy_read_bput_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA09">← BA09 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="192 &C0 %11000000">#&c0</span></span> <span class="comment">; A=&C0: read direction flag</span></td> </tr> <tr id="addr-BA2C"> <td class="addr"><a href="#addr-BA2C">BA2C</a></td> <td><span class="label">.store_direction_flag<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA28">← BA28 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Store direction in workspace</span></td> </tr> <tr id="addr-BA2F"> <td class="addr"><a href="#addr-BA2F">BA2F</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer X to A</span></td> </tr> <tr id="addr-BA30"> <td class="addr"><a href="#addr-BA30">BA30</a></td> <td> <span class="opcode">TSX</span> <span class="comment">; Save current stack pointer</span></td> </tr> <tr id="addr-BA31"> <td class="addr"><a href="#addr-BA31">BA31</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&10E7">wksp_stack_save</span></span> <span class="comment">; For error recovery</span></td> </tr> <tr id="addr-BA34"> <td class="addr"><a href="#addr-BA34">BA34</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save X on stack</span></td> </tr> <tr id="addr-BA35"> <td class="addr"><a href="#addr-BA35">BA35</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BBB4" data-tip="&BBB4 – Get floppy step rate">floppy_get_step_rate</a></span> <span class="comment">; Get disc step rate from settings</span></td> </tr> <tr id="addr-BA38"> <td class="addr"><a href="#addr-BA38">BA38</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB92" data-tip="&BB92 – Claim NMI and initialise floppy transfer">claim_nmi_and_init</a></span> <span class="comment">; Set up drive select and step rate</span></td> </tr> <tr id="addr-BA3B"> <td class="addr"><a href="#addr-BA3B">BA3B</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore X</span></td> </tr> <tr id="addr-BA3C"> <td class="addr"><a href="#addr-BA3C">BA3C</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-BA3D"> <td class="addr"><a href="#addr-BA3D">BA3D</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check read/write direction</span></td> </tr> <tr id="addr-BA3F"> <td class="addr"><a href="#addr-BA3F">BA3F</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BA4D" data-tip="&BA4D">set_buffer_addr_for_read</a></span> <span class="comment">; Reading: set up read buffer address</span></td> </tr> <tr id="addr-BA41"> <td class="addr"><a href="#addr-BA41">BA41</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00BC">zp_buf_src_lo</span></span> <span class="comment">; Writing: use zp_bc,bd as buffer</span></td> </tr> <tr id="addr-BA43"> <td class="addr"><a href="#addr-BA43">BA43</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0B">nmi_write_addr_lo</span></span> <span class="comment">; Patch NMI handler buffer addr low</span></td> </tr> <tr id="addr-BA46"> <td class="addr"><a href="#addr-BA46">BA46</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00BD">zp_buf_src_hi</span></span> <span class="comment">; Buffer address high byte</span></td> </tr> <tr id="addr-BA48"> <td class="addr"><a href="#addr-BA48">BA48</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0C">nmi_write_addr_hi</span></span> <span class="comment">; Patch NMI handler buffer addr high</span></td> </tr> <tr id="addr-BA4B"> <td class="addr"><a href="#addr-BA4B">BA4B</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BA57" data-tip="&BA57">get_sector_count</a></span> <span class="comment">; Always branch (high byte non-zero)</span></td> </tr> <tr id="addr-BA4D"> <td class="addr"><a href="#addr-BA4D">BA4D</a></td> <td><span class="label">.set_buffer_addr_for_read<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA3F">← BA3F BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00BE">zp_buf_dest_lo</span></span> <span class="comment">; Reading: use zp_be,bf as buffer</span></td> </tr> <tr id="addr-BA4F"> <td class="addr"><a href="#addr-BA4F">BA4F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0E">nmi_read_addr_lo</span></span> <span class="comment">; Patch NMI read buffer addr low</span></td> </tr> <tr id="addr-BA52"> <td class="addr"><a href="#addr-BA52">BA52</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00BF">zp_buf_dest_hi</span></span> <span class="comment">; Get read buffer addr high</span></td> </tr> <tr id="addr-BA54"> <td class="addr"><a href="#addr-BA54">BA54</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0F">nmi_read_addr_hi</span></span> <span class="comment">; Patch NMI read buffer addr high</span></td> </tr> <tr id="addr-BA57"> <td class="addr"><a href="#addr-BA57">BA57</a></td> <td><span class="label">.get_sector_count<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA4B">← BA4B BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1003">wksp_buf_sec_hi</span>,x</span> <span class="comment">; Get sector count from control block</span></td> </tr> <tr id="addr-BA5A"> <td class="addr"><a href="#addr-BA5A">BA5A</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save sector count on stack</span></td> </tr> <tr id="addr-BA5B"> <td class="addr"><a href="#addr-BA5B">BA5B</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="31 &1F %00011111 US">#&1f</span></span> <span class="comment">; Check drive number bits</span></td> </tr> <tr id="addr-BA5D"> <td class="addr"><a href="#addr-BA5D">BA5D</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BA63" data-tip="&BA63">set_drive_1_select</a></span> <span class="comment">; Drive 0: continue</span></td> </tr> <tr id="addr-BA5F"> <td class="addr"><a href="#addr-BA5F">BA5F</a></td> <td><span class="label">.check_drive_number<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA67">← BA67 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Pop and discard</span></td> </tr> <tr id="addr-BA60"> <td class="addr"><a href="#addr-BA60">BA60</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BF66" data-tip="&BF66">bad_address_error</a></span> <span class="comment">; Jump to error: bad drive number</span></td> </tr> <tr id="addr-BA63"> <td class="addr"><a href="#addr-BA63">BA63</a></td> <td><span class="label">.set_drive_1_select<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA5D">← BA5D BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Pop sector count</span></td> </tr> <tr id="addr-BA64"> <td class="addr"><a href="#addr-BA64">BA64</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Re-push for later</span></td> </tr> <tr id="addr-BA65"> <td class="addr"><a href="#addr-BA65">BA65</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; Check format bit</span></td> </tr> <tr id="addr-BA67"> <td class="addr"><a href="#addr-BA67">BA67</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BA5F" data-tip="&BA5F">check_drive_number</a></span> <span class="comment">; Non-zero format bit: error</span></td> </tr> <tr id="addr-BA69"> <td class="addr"><a href="#addr-BA69">BA69</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Pop sector count</span></td> </tr> <tr id="addr-BA6A"> <td class="addr"><a href="#addr-BA6A">BA6A</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Check verify bit</span></td> </tr> <tr id="addr-BA6C"> <td class="addr"><a href="#addr-BA6C">BA6C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BA72" data-tip="&BA72">check_format_command</a></span> <span class="comment">; Non-zero verify bit: use verify cmd</span></td> </tr> <tr id="addr-BA6E"> <td class="addr"><a href="#addr-BA6E">BA6E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Not verify: seek+read (&21)</span></td> </tr> <tr id="addr-BA70"> <td class="addr"><a href="#addr-BA70">BA70</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BA74" data-tip="&BA74">set_read_write_command</a></span></td> </tr> <tr id="addr-BA72"> <td class="addr"><a href="#addr-BA72">BA72</a></td> <td><span class="label">.check_format_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA6C">← BA6C BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="34 &22 %00100010 '"'">#&22</span></span> <span class="comment">; Verify: seek+read (&22)</span></td> </tr> <tr id="addr-BA74"> <td class="addr"><a href="#addr-BA74">BA74</a></td> <td><span class="label">.set_read_write_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA70">← BA70 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Store in NMI control byte</span></td> </tr> <tr id="addr-BA77"> <td class="addr"><a href="#addr-BA77">BA77</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Set head-loaded flag in state</span></td> </tr> <tr id="addr-BA7A"> <td class="addr"><a href="#addr-BA7A">BA7A</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-BA7B"> <td class="addr"><a href="#addr-BA7B">BA7B</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Restore head-loaded flag</span></td> </tr> <tr id="addr-BA7E"> <td class="addr"><a href="#addr-BA7E">BA7E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1001">wksp_buf_sec_lo</span>,x</span> <span class="comment">; Get sector address from control blk</span></td> </tr> <tr id="addr-BA81"> <td class="addr"><a href="#addr-BA81">BA81</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save sector address on stack</span></td> </tr> <tr id="addr-BA82"> <td class="addr"><a href="#addr-BA82">BA82</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1002">wksp_buf_sec_mid</span>,x</span> <span class="comment">; Get sector address mid byte</span></td> </tr> <tr id="addr-BA85"> <td class="addr"><a href="#addr-BA85">BA85</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; X = sector address high byte</span></td> </tr> <tr id="addr-BA86"> <td class="addr"><a href="#addr-BA86">BA86</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore sector address</span></td> </tr> <tr id="addr-BA87"> <td class="addr"><a href="#addr-BA87">BA87</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: init track counter</span></td> </tr> <tr id="addr-BA89"> <td class="addr"><a href="#addr-BA89">BA89</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BFA2" data-tip="&BFA2 – Divide X:A by 16, result in Y:A">xa_div_16_to_ya</a></span> <span class="comment">; Convert sector to track/sector</span></td> </tr> <tr id="addr-BA8C"> <td class="addr"><a href="#addr-BA8C">BA8C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; Store sector number</span></td> </tr> <tr id="addr-BA8E"> <td class="addr"><a href="#addr-BA8E">BA8E</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span></span> <span class="comment">; Store track number</span></td> </tr> <tr id="addr-BA90"> <td class="addr"><a href="#addr-BA90">BA90</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Track to A for side check</span></td> </tr> <tr id="addr-BA91"> <td class="addr"><a href="#addr-BA91">BA91</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for track calculation</span></td> </tr> <tr id="addr-BA92"> <td class="addr"><a href="#addr-BA92">BA92</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="80 &50 %01010000 'P'">#&50</span></span> <span class="comment">; Subtract 80 (side 0 tracks)</span></td> </tr> <tr id="addr-BA94"> <td class="addr"><a href="#addr-BA94">BA94</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BA9B" data-tip="&BA9B">set_fdc_control_byte</a></span> <span class="comment">; Track < 80: side 0</span></td> </tr> <tr id="addr-BA96"> <td class="addr"><a href="#addr-BA96">BA96</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span></span> <span class="comment">; Track >= 80: adjust for side 1</span></td> </tr> <tr id="addr-BA98"> <td class="addr"><a href="#addr-BA98">BA98</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD22" data-tip="&BD22 – Select floppy disc side 1">floppy_set_side_1</a></span> <span class="comment">; Select side 1</span></td> </tr> <tr id="addr-BA9B"> <td class="addr"><a href="#addr-BA9B">BA9B</a></td> <td><span class="label">.set_fdc_control_byte<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA94">← BA94 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Get NMI control byte</span></td> </tr> <tr id="addr-BA9E"> <td class="addr"><a href="#addr-BA9E">BA9E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE80">fdc_1770_drive_control</span></span> <span class="comment">; Write to FDC control register</span></td> </tr> <tr id="addr-BAA1"> <td class="addr"><a href="#addr-BAA1">BAA1</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate drive select into carry</span></td> </tr> <tr id="addr-BAA2"> <td class="addr"><a href="#addr-BAA2">BAA2</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BAB0" data-tip="&BAB0">set_track_and_sector</a></span> <span class="comment">; C=0: not last sector, continue</span></td> </tr> <tr id="addr-BAA4"> <td class="addr"><a href="#addr-BAA4">BAA4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E5">wksp_fdc_track_0</span></span> <span class="comment">; Get previous track for drive</span></td> </tr> <tr id="addr-BAA7"> <td class="addr"><a href="#addr-BAA7">BAA7</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store as target track</span></td> </tr> <tr id="addr-BAA9"> <td class="addr"><a href="#addr-BAA9">BAA9</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Check head-loaded state</span></td> </tr> <tr id="addr-BAAC"> <td class="addr"><a href="#addr-BAAC">BAAC</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BABD" data-tip="&BABD">setup_nmi_for_transfer</a></span> <span class="comment">; Head loaded: skip restore</span></td> </tr> <tr id="addr-BAAE"> <td class="addr"><a href="#addr-BAAE">BAAE</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BABA" data-tip="&BABA">seek_to_track_0</a></span></td> </tr> <tr id="addr-BAB0"> <td class="addr"><a href="#addr-BAB0">BAB0</a></td> <td><span class="label">.set_track_and_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BAA2">← BAA2 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E6">wksp_fdc_track_1</span></span> <span class="comment">; Get alternative track for drive</span></td> </tr> <tr id="addr-BAB3"> <td class="addr"><a href="#addr-BAB3">BAB3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store as target track</span></td> </tr> <tr id="addr-BAB5"> <td class="addr"><a href="#addr-BAB5">BAB5</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Check head-loaded state</span></td> </tr> <tr id="addr-BAB8"> <td class="addr"><a href="#addr-BAB8">BAB8</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-BABD" data-tip="&BABD">setup_nmi_for_transfer</a></span> <span class="comment">; Not loaded: skip restore</span></td> </tr> <tr id="addr-BABA"> <td class="addr"><a href="#addr-BABA">BABA</a></td> <td><span class="label">.seek_to_track_0<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BAAE">← BAAE BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD3F" data-tip="&BD3F – Seek floppy head to track 0">floppy_restore_track_0</a></span> <span class="comment">; Seek to track 0 first</span></td> </tr> <tr id="addr-BABD"> <td class="addr"><a href="#addr-BABD">BABD</a></td> <td><span class="label">.setup_nmi_for_transfer<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BAAC">← BAAC BPL</a><a href="#addr-BAB8">← BAB8 BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BAC6" data-tip="&BAC6 – Set up FDC registers and seek to track">setup_fdc_and_seek</a></span> <span class="comment">; Set up sector parameters</span></td> </tr> <tr id="addr-BAC0"> <td class="addr"><a href="#addr-BAC0">BAC0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BCFD" data-tip="&BCFD – Select and issue FDC read/write command">select_fdc_rw_command</a></span> <span class="comment">; Set up NMI handler</span></td> </tr> <tr id="addr-BAC3"> <td class="addr"><a href="#addr-BAC3">BAC3</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span> <span class="comment">; Process result/error</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BAC6"> <td colspan="2"><div class="sub-header"> <h3>Set up FDC registers and seek to track</h3> <div class="sub-desc"><p>Write track and sector to the WD1770 registers with readback verify, then seek to the target track.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BAC6">BAC6</a></td> <td><span class="label">.setup_fdc_and_seek<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BABD">← BABD JSR</a><a href="#addr-BD63">← BD63 JSR</a><a href="#addr-BDA6">← BDA6 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD2B" data-tip="&BD2B – Clear floppy transfer complete flag">clear_transfer_complete</a></span> <span class="comment">; Clear seek-complete flag</span></td> </tr> <tr id="addr-BAC9"> <td class="addr"><a href="#addr-BAC9">BAC9</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: first FDC register</span></td> </tr> <tr id="addr-BACB"> <td class="addr"><a href="#addr-BACB">BACB</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB09" data-tip="&BB09 – Write to WD1770 register with readback verify">fdc_write_register_verify</a></span></td> </tr> <tr id="addr-BACE"> <td class="addr"><a href="#addr-BACE">BACE</a></td> <td> <span class="opcode">INX</span> <span class="comment">; X=1: track register</span></td> </tr> <tr id="addr-BACF"> <td class="addr"><a href="#addr-BACF">BACF</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB09" data-tip="&BB09 – Write to WD1770 register with readback verify">fdc_write_register_verify</a></span></td> </tr> <tr id="addr-BAD2"> <td class="addr"><a href="#addr-BAD2">BAD2</a></td> <td> <span class="opcode">INX</span> <span class="comment">; X=2: sector register</span></td> </tr> <tr id="addr-BAD3"> <td class="addr"><a href="#addr-BAD3">BAD3</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB09" data-tip="&BB09 – Write to WD1770 register with readback verify">fdc_write_register_verify</a></span></td> </tr> <tr id="addr-BAD6"> <td class="addr"><a href="#addr-BAD6">BAD6</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Compare with target track</span></td> </tr> <tr id="addr-BAD8"> <td class="addr"><a href="#addr-BAD8">BAD8</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BAF4" data-tip="&BAF4 – Set up track for floppy retry">retry_after_error</a></span> <span class="comment">; Already on track: skip seek</span></td> </tr> <tr id="addr-BADA"> <td class="addr"><a href="#addr-BADA">BADA</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Set head-loaded flag</span></td> </tr> <tr id="addr-BADD"> <td class="addr"><a href="#addr-BADD">BADD</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-BADE"> <td class="addr"><a href="#addr-BADE">BADE</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Restore head-loaded flag</span></td> </tr> <tr id="addr-BAE1"> <td class="addr"><a href="#addr-BAE1">BAE1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="20 &14 %00010100 DC4">#&14</span></span> <span class="comment">; FDC seek command (&14)</span></td> </tr> <tr id="addr-BAE3"> <td class="addr"><a href="#addr-BAE3">BAE3</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0D5C">nmi_drive_cmd</span></span> <span class="comment">; OR in drive select bits</span></td> </tr> <tr id="addr-BAE6"> <td class="addr"><a href="#addr-BAE6">BAE6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue seek command to FDC</span></td> </tr> <tr id="addr-BAE9"> <td class="addr"><a href="#addr-BAE9">BAE9</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BCC2" data-tip="&BCC2 – Wait for floppy NMI transfer to complete">floppy_wait_nmi_finish</a></span></td> </tr> <tr id="addr-BAEC"> <td class="addr"><a href="#addr-BAEC">BAEC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Get control flags</span></td> </tr> <tr id="addr-BAEE"> <td class="addr"><a href="#addr-BAEE">BAEE</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate verify flag to carry</span></td> </tr> <tr id="addr-BAEF"> <td class="addr"><a href="#addr-BAEF">BAEF</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BAF4" data-tip="&BAF4 – Set up track for floppy retry">retry_after_error</a></span> <span class="comment">; C=0: no verify, proceed to data</span></td> </tr> <tr id="addr-BAF1"> <td class="addr"><a href="#addr-BAF1">BAF1</a></td> <td><span class="label">.check_floppy_error_code<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BB04">← BB04 BEQ</a><a href="#addr-BB23">← BB23 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BAF4"> <td colspan="2"><div class="sub-header"> <h3>Set up track for floppy retry</h3> <div class="sub-desc"><p>After a floppy error, set up the track for a retry attempt by copying target sector to current track.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BAF4">BAF4</a></td> <td><span class="label">.retry_after_error<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BAD8">← BAD8 BEQ</a><a href="#addr-BAEF">← BAEF BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span></span> <span class="comment">; Set sector number as target</span></td> </tr> <tr id="addr-BAF6"> <td class="addr"><a href="#addr-BAF6">BAF6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store as current track</span></td> </tr> <tr id="addr-BAF8"> <td class="addr"><a href="#addr-BAF8">BAF8</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check transfer direction</span></td> </tr> <tr id="addr-BAFA"> <td class="addr"><a href="#addr-BAFA">BAFA</a></td> <td> <span class="opcode">BVS</span> <span class="operand"><a href="#addr-BB06" data-tip="&BB06">return_floppy_result</a></span> <span class="comment">; V set: multi-sector operation</span></td> </tr> <tr id="addr-BAFC"> <td class="addr"><a href="#addr-BAFC">BAFC</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: check command byte</span></td> </tr> <tr id="addr-BAFE"> <td class="addr"><a href="#addr-BAFE">BAFE</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get command from control block</span></td> </tr> <tr id="addr-BB00"> <td class="addr"><a href="#addr-BB00">BB00</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="11 &0B %00001011 VT">#&0b</span></span> <span class="comment">; Is it &0B (verify)?</span></td> </tr> <tr id="addr-BB02"> <td class="addr"><a href="#addr-BB02">BB02</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BB06" data-tip="&BB06">return_floppy_result</a></span> <span class="comment">; No, proceed with data transfer</span></td> </tr> <tr id="addr-BB04"> <td class="addr"><a href="#addr-BB04">BB04</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BAF1" data-tip="&BAF1">check_floppy_error_code</a></span></td> </tr> <tr id="addr-BB06"> <td class="addr"><a href="#addr-BB06">BB06</a></td> <td><span class="label">.return_floppy_result<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BAFA">← BAFA BVS</a><a href="#addr-BB02">← BB02 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BD2B" data-tip="&BD2B – Clear floppy transfer complete flag">clear_transfer_complete</a></span> <span class="comment">; Clear seek flag and return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BB09"> <td colspan="2"><div class="sub-header"> <h3>Write to WD1770 register with readback verify</h3> <div class="sub-desc"><p>Write value from zp_a3+X to FDC register at &FE85+X, then read back and loop until the value matches. This handles the WD1770's register write timing.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>X</td><td>FDC register index (0=track, 1=sector, 2=data)</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>value written to register</td></tr> <tr><td>X</td><td>preserved</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BB09">BB09</a></td> <td><span class="label">.fdc_write_register_verify<span class="ref-badge">←5</span><span class="ref-popup"><a href="#addr-BACB">← BACB JSR</a><a href="#addr-BACF">← BACF JSR</a><a href="#addr-BAD3">← BAD3 JSR</a><a href="#addr-BB11">← BB11 BNE</a><a href="#addr-BE19">← BE19 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span>,x</span> <span class="comment">; Get value to write</span></td> </tr> <tr id="addr-BB0B"> <td class="addr"><a href="#addr-BB0B">BB0B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE85">fdc_1770_track</span>,x</span> <span class="comment">; Write to FDC register</span></td> </tr> <tr id="addr-BB0E"> <td class="addr"><a href="#addr-BB0E">BB0E</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&FE85">fdc_1770_track</span>,x</span> <span class="comment">; Read back from register</span></td> </tr> <tr id="addr-BB11"> <td class="addr"><a href="#addr-BB11">BB11</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BB09" data-tip="&BB09 – Write to WD1770 register with readback verify">fdc_write_register_verify</a></span> <span class="comment">; Loop until value sticks</span></td> </tr> <tr id="addr-BB13"> <td class="addr"><a href="#addr-BB13">BB13</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BB14"> <td colspan="2"><div class="sub-header"> <h3>Execute floppy disc command</h3> <div class="sub-desc"><p>Execute a disc operation on the floppy disc using the WD1770 controller. Handles sector read, write, and format operations.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BB14">BB14</a></td> <td><span class="label">.floppy_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA00">← BA00 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">TSX</span> <span class="comment">; Save stack pointer for error recovery</span></td> </tr> <tr id="addr-BB15"> <td class="addr"><a href="#addr-BB15">BB15</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&10E7">wksp_stack_save</span></span> <span class="comment">; Save stack for error recovery</span></td> </tr> <tr id="addr-BB18"> <td class="addr"><a href="#addr-BB18">BB18</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Set transfer mode flags</span></td> </tr> <tr id="addr-BB1A"> <td class="addr"><a href="#addr-BB1A">BB1A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Store transfer mode</span></td> </tr> <tr id="addr-BB1D"> <td class="addr"><a href="#addr-BB1D">BB1D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB42" data-tip="&BB42 – Initialise floppy disc transfer">floppy_init_transfer</a></span> <span class="comment">; Set up NMI handler and drive select</span></td> </tr> <tr id="addr-BB20"> <td class="addr"><a href="#addr-BB20">BB20</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BDA6" data-tip="&BDA6">issue_fdc_track_command</a></span> <span class="comment">; Execute the read/write operation</span></td> </tr> <tr id="addr-BB23"> <td class="addr"><a href="#addr-BB23">BB23</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BAF1" data-tip="&BAF1">check_floppy_error_code</a></span> <span class="comment">; Error: jump to floppy error handler</span></td> </tr> <tr id="addr-BB25"> <td class="addr"><a href="#addr-BB25">BB25</a></td> <td><span class="label">.exec_floppy_partial_sector_buf<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BA03">← BA03 JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E2">wksp_format_page</span></span> <span class="comment">; Partial sector buffer: save count</span></td> </tr> <tr id="addr-BB28"> <td class="addr"><a href="#addr-BB28">BB28</a></td> <td> <span class="opcode">TSX</span> <span class="comment">; Save stack for error recovery</span></td> </tr> <tr id="addr-BB29"> <td class="addr"><a href="#addr-BB29">BB29</a></td> <td> <span class="opcode">STX</span> <span class="operand"><span class="ext-label" data-tip="&10E7">wksp_stack_save</span></span> <span class="comment">; Save stack for error recovery</span></td> </tr> <tr id="addr-BB2C"> <td class="addr"><a href="#addr-BB2C">BB2C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Workspace page for control block</span></td> </tr> <tr id="addr-BB2E"> <td class="addr"><a href="#addr-BB2E">BB2E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Point (&B0) to workspace control blk</span></td> </tr> <tr id="addr-BB30"> <td class="addr"><a href="#addr-BB30">BB30</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="21 &15 %00010101 NAK">#&15</span></span> <span class="comment">; Control block offset</span></td> </tr> <tr id="addr-BB32"> <td class="addr"><a href="#addr-BB32">BB32</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Store in (&B0)</span></td> </tr> <tr id="addr-BB34"> <td class="addr"><a href="#addr-BB34">BB34</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear transfer mode flags</span></td> </tr> <tr id="addr-BB36"> <td class="addr"><a href="#addr-BB36">BB36</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Clear transfer mode for format</span></td> </tr> <tr id="addr-BB39"> <td class="addr"><a href="#addr-BB39">BB39</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB42" data-tip="&BB42 – Initialise floppy disc transfer">floppy_init_transfer</a></span> <span class="comment">; Set up NMI handler</span></td> </tr> <tr id="addr-BB3C"> <td class="addr"><a href="#addr-BB3C">BB3C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD58" data-tip="&BD58 – Format a floppy disc track">floppy_format_track</a></span> <span class="comment">; Execute format track operation</span></td> </tr> <tr id="addr-BB3F"> <td class="addr"><a href="#addr-BB3F">BB3F</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span> <span class="comment">; Process result/error</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BB42"> <td colspan="2"><div class="sub-header"> <h3>Initialise floppy disc transfer</h3> <div class="sub-desc"><p>Set up for a floppy disc operation: clear error number, copy the transfer address and control parameters from the control block, claim NMI, set step rate, and copy the NMI handler code to NMI workspace.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BB42">BB42</a></td> <td><span class="label">.floppy_init_transfer<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BB1D">← BB1D JSR</a><a href="#addr-BB39">← BB39 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear error number</span></td> </tr> <tr id="addr-BB44"> <td class="addr"><a href="#addr-BB44">BB44</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E3">wksp_err_number</span></span> <span class="comment">; Clear error number</span></td> </tr> <tr id="addr-BB47"> <td class="addr"><a href="#addr-BB47">BB47</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: get transfer address from blk</span></td> </tr> <tr id="addr-BB49"> <td class="addr"><a href="#addr-BB49">BB49</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Transfer address low</span></td> </tr> <tr id="addr-BB4B"> <td class="addr"><a href="#addr-BB4B">BB4B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B2">zp_mem_ptr_lo</span></span> <span class="comment">; Store transfer addr low in (&B2)</span></td> </tr> <tr id="addr-BB4D"> <td class="addr"><a href="#addr-BB4D">BB4D</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-BB4E"> <td class="addr"><a href="#addr-BB4E">BB4E</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Transfer address high</span></td> </tr> <tr id="addr-BB50"> <td class="addr"><a href="#addr-BB50">BB50</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00B3">zp_mem_ptr_hi</span></span> <span class="comment">; Store transfer addr high in (&B3)</span></td> </tr> <tr id="addr-BB52"> <td class="addr"><a href="#addr-BB52">BB52</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-BB53"> <td class="addr"><a href="#addr-BB53">BB53</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get control byte 3</span></td> </tr> <tr id="addr-BB55"> <td class="addr"><a href="#addr-BB55">BB55</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-BB56"> <td class="addr"><a href="#addr-BB56">BB56</a></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-BB57"> <td class="addr"><a href="#addr-BB57">BB57</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get control byte 4</span></td> </tr> <tr id="addr-BB59"> <td class="addr"><a href="#addr-BB59">BB59</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Check X+1 for zero (was &FF)</span></td> </tr> <tr id="addr-BB5A"> <td class="addr"><a href="#addr-BB5A">BB5A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BB5F" data-tip="&BB5F">check_host_memory</a></span> <span class="comment">; X was &FF: check A for &FF too</span></td> </tr> <tr id="addr-BB5C"> <td class="addr"><a href="#addr-BB5C">BB5C</a></td> <td> <span class="opcode">INX</span> <span class="comment">; Check X for zero (wrap from &FF)</span></td> </tr> <tr id="addr-BB5D"> <td class="addr"><a href="#addr-BB5D">BB5D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BB63" data-tip="&BB63">check_tube_present</a></span> <span class="comment">; X non-zero: check Tube flag</span></td> </tr> <tr id="addr-BB5F"> <td class="addr"><a href="#addr-BB5F">BB5F</a></td> <td><span class="label">.check_host_memory<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB5A">← BB5A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A == &FF?</span></td> </tr> <tr id="addr-BB61"> <td class="addr"><a href="#addr-BB61">BB61</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BB6A" data-tip="&BB6A">validate_disc_command</a></span> <span class="comment">; Both &FF: host memory, skip Tube</span></td> </tr> <tr id="addr-BB63"> <td class="addr"><a href="#addr-BB63">BB63</a></td> <td><span class="label">.check_tube_present<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB5D">← BB5D BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Check if Tube is present</span></td> </tr> <tr id="addr-BB65"> <td class="addr"><a href="#addr-BB65">BB65</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BB6A" data-tip="&BB6A">validate_disc_command</a></span> <span class="comment">; No Tube: skip Tube setup</span></td> </tr> <tr id="addr-BB67"> <td class="addr"><a href="#addr-BB67">BB67</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8027" data-tip="&8027 – Claim Tube if present">claim_tube</a></span></td> </tr> <tr id="addr-BB6A"> <td class="addr"><a href="#addr-BB6A">BB6A</a></td> <td><span class="label">.validate_disc_command<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BB61">← BB61 BEQ</a><a href="#addr-BB65">← BB65 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="5 &05 %00000101 ENQ">#5</span></span> <span class="comment">; Y=5: get command byte from block</span></td> </tr> <tr id="addr-BB6C"> <td class="addr"><a href="#addr-BB6C">BB6C</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Read command byte</span></td> </tr> <tr id="addr-BB6E"> <td class="addr"><a href="#addr-BB6E">BB6E</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; Command 8 (read)?</span></td> </tr> <tr id="addr-BB70"> <td class="addr"><a href="#addr-BB70">BB70</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BB82" data-tip="&BB82 – Set read mode and initialise floppy">set_read_transfer_mode</a></span> <span class="comment">; Yes, valid command</span></td> </tr> <tr id="addr-BB72"> <td class="addr"><a href="#addr-BB72">BB72</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; Command &0A (write)?</span></td> </tr> <tr id="addr-BB74"> <td class="addr"><a href="#addr-BB74">BB74</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BB89" data-tip="&BB89">setup_nmi_and_step_rate</a></span> <span class="comment">; Yes, valid command</span></td> </tr> <tr id="addr-BB76"> <td class="addr"><a href="#addr-BB76">BB76</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="11 &0B %00001011 VT">#&0b</span></span> <span class="comment">; Command &0B (verify)?</span></td> </tr> <tr id="addr-BB78"> <td class="addr"><a href="#addr-BB78">BB78</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BB82" data-tip="&BB82 – Set read mode and initialise floppy">set_read_transfer_mode</a></span> <span class="comment">; Yes, valid command</span></td> </tr> <tr id="addr-BB7A"> <td class="addr"><a href="#addr-BB7A">BB7A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="103 &67 %01100111 'g'">#&67</span></span> <span class="comment">; Error &67: bad command</span></td> </tr> <tr id="addr-BB7C"> <td class="addr"><a href="#addr-BB7C">BB7C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E3">wksp_err_number</span></span> <span class="comment">; Store error code</span></td> </tr> <tr id="addr-BB7F"> <td class="addr"><a href="#addr-BB7F">BB7F</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BB82"> <td colspan="2"><div class="sub-header"> <h3>Set read mode and initialise floppy</h3> <div class="sub-desc"><p>Set bit 7 of transfer mode for read, get step rate, claim NMI, and set up the track.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BB82">BB82</a></td> <td><span class="label">.set_read_transfer_mode<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BB70">← BB70 BEQ</a><a href="#addr-BB78">← BB78 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Set bit 7 of transfer mode</span></td> </tr> <tr id="addr-BB85"> <td class="addr"><a href="#addr-BB85">BB85</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for rotate</span></td> </tr> <tr id="addr-BB86"> <td class="addr"><a href="#addr-BB86">BB86</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Restore bit 7 set</span></td> </tr> <tr id="addr-BB89"> <td class="addr"><a href="#addr-BB89">BB89</a></td> <td><span class="label">.setup_nmi_and_step_rate<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB74">← BB74 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BBB4" data-tip="&BBB4 – Get floppy step rate">floppy_get_step_rate</a></span></td> </tr> <tr id="addr-BB8C"> <td class="addr"><a href="#addr-BB8C">BB8C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB92" data-tip="&BB92 – Claim NMI and initialise floppy transfer">claim_nmi_and_init</a></span> <span class="comment">; Set up drive select and NMI</span></td> </tr> <tr id="addr-BB8F"> <td class="addr"><a href="#addr-BB8F">BB8F</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BEFF" data-tip="&BEFF">setup_track_for_rw</a></span> <span class="comment">; Jump to floppy track setup</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BB92"> <td colspan="2"><div class="sub-header"> <h3>Claim NMI and initialise floppy transfer</h3> <div class="sub-desc"><p>Claim the NMI vector via service call 12, set FDC step rate, clear error flags, and copy the NMI handler code into NMI workspace.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BB92">BB92</a></td> <td><span class="label">.claim_nmi_and_init<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BA38">← BA38 JSR</a><a href="#addr-BB8C">← BB8C JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BBDA" data-tip="&BBDA – Claim NMI via service call 12">claim_nmi</a></span></td> </tr> <tr id="addr-BB95"> <td class="addr"><a href="#addr-BB95">BB95</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E8">wksp_fdc_cmd_step</span></span> <span class="comment">; Get FDC step rate setting</span></td> </tr> <tr id="addr-BB98"> <td class="addr"><a href="#addr-BB98">BB98</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5C">nmi_drive_cmd</span></span> <span class="comment">; Store in NMI control byte</span></td> </tr> <tr id="addr-BB9B"> <td class="addr"><a href="#addr-BB9B">BB9B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: clear error flag</span></td> </tr> <tr id="addr-BB9D"> <td class="addr"><a href="#addr-BB9D">BB9D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Clear error code</span></td> </tr> <tr id="addr-BB9F"> <td class="addr"><a href="#addr-BB9F">BB9F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Clear transfer state</span></td> </tr> <tr id="addr-BBA1"> <td class="addr"><a href="#addr-BBA1">BBA1</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Get transfer mode flags</span></td> </tr> <tr id="addr-BBA4"> <td class="addr"><a href="#addr-BBA4">BBA4</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Set bit 5 (NMI active)</span></td> </tr> <tr id="addr-BBA6"> <td class="addr"><a href="#addr-BBA6">BBA6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Store updated mode</span></td> </tr> <tr id="addr-BBA9"> <td class="addr"><a href="#addr-BBA9">BBA9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Store as control flags</span></td> </tr> <tr id="addr-BBAB"> <td class="addr"><a href="#addr-BBAB">BBAB</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Get ADFS flags</span></td> </tr> <tr id="addr-BBAD"> <td class="addr"><a href="#addr-BBAD">BBAD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5D">nmi_adfs_flags</span></span> <span class="comment">; Store in NMI workspace</span></td> </tr> <tr id="addr-BBB0"> <td class="addr"><a href="#addr-BBB0">BBB0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BBF1" data-tip="&BBF1 – Copy NMI handler code to NMI workspace">copy_code_to_nmi_space</a></span></td> </tr> <tr id="addr-BBB3"> <td class="addr"><a href="#addr-BBB3">BBB3</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BBB4"> <td colspan="2"><div class="sub-header"> <h3>Get floppy step rate</h3> <div class="sub-desc"><p>Fetch the startup options byte via OSBYTE &FF and use bits 4 and 5 to set the FDC step rate and head settle time in milliseconds.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BBB4">BBB4</a></td> <td><span class="label">.floppy_get_step_rate<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BA35">← BA35 JSR</a><a href="#addr-BB89">← BB89 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear side select flag</span></td> </tr> <tr id="addr-BBB6"> <td class="addr"><a href="#addr-BBB6">BBB6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D56">nmi_step_rate</span></span> <span class="comment">; Store in NMI side select</span></td> </tr> <tr id="addr-BBB9"> <td class="addr"><a href="#addr-BBB9">BBB9</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E8">wksp_fdc_cmd_step</span></span> <span class="comment">; Clear FDC step rate command bits</span></td> </tr> <tr id="addr-BBBC"> <td class="addr"><a href="#addr-BBBC">BBBC</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#osbyte_read_write_startup_options</span></span> <span class="comment">; OSBYTE &FF: read startup options</span></td> </tr> <tr id="addr-BBBE"> <td class="addr"><a href="#addr-BBBE">BBBE</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: read current value</span></td> </tr> <tr id="addr-BBC0"> <td class="addr"><a href="#addr-BBC0">BBC0</a></td> <td> <span class="opcode">TAY</span> <span class="comment">; Y=&FF: read current value</span></td> </tr> <tr id="addr-BBC1"> <td class="addr"><a href="#addr-BBC1">BBC1</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; osbyte: read write startup options</span></td> </tr> <tr id="addr-BBC4"> <td class="addr"><a href="#addr-BBC4">BBC4</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Get startup byte to A</span></td> </tr> <tr id="addr-BBC5"> <td class="addr"><a href="#addr-BBC5">BBC5</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save startup byte</span></td> </tr> <tr id="addr-BBC6"> <td class="addr"><a href="#addr-BBC6">BBC6</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Test bit 5 (step rate high)</span></td> </tr> <tr id="addr-BBC8"> <td class="addr"><a href="#addr-BBC8">BBC8</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BBCF" data-tip="&BBCF">step_rate_fast</a></span> <span class="comment">; Clear: fast step rate</span></td> </tr> <tr id="addr-BBCA"> <td class="addr"><a href="#addr-BBCA">BBCA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Bit 5 set: slow step (rate=3)</span></td> </tr> <tr id="addr-BBCC"> <td class="addr"><a href="#addr-BBCC">BBCC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E8">wksp_fdc_cmd_step</span></span> <span class="comment">; Store in FDC command step field</span></td> </tr> <tr id="addr-BBCF"> <td class="addr"><a href="#addr-BBCF">BBCF</a></td> <td><span class="label">.step_rate_fast<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BBC8">← BBC8 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore startup byte</span></td> </tr> <tr id="addr-BBD0"> <td class="addr"><a href="#addr-BBD0">BBD0</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Test bit 4 (settle time)</span></td> </tr> <tr id="addr-BBD2"> <td class="addr"><a href="#addr-BBD2">BBD2</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BBD9" data-tip="&BBD9">return_43</a></span> <span class="comment">; Clear: short settle</span></td> </tr> <tr id="addr-BBD4"> <td class="addr"><a href="#addr-BBD4">BBD4</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Bit 4 set: long settle (rate=2)</span></td> </tr> <tr id="addr-BBD6"> <td class="addr"><a href="#addr-BBD6">BBD6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D56">nmi_step_rate</span></span> <span class="comment">; Store in NMI workspace</span></td> </tr> <tr id="addr-BBD9"> <td class="addr"><a href="#addr-BBD9">BBD9</a></td> <td><span class="label">.return_43<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BBD2">← BBD2 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BBDA"> <td colspan="2"><div class="sub-header"> <h3>Claim NMI via service call 12</h3> <div class="sub-desc"><p>Issue service call 12 (NMI claim) via OSBYTE &8F to claim exclusive use of the NMI handler for floppy disc operations. Saves the return argument for later release.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BBDA">BBDA</a></td> <td><span class="label">.claim_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB92">← BB92 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="143 &8F %10001111">#osbyte_issue_service_request</span></span> <span class="comment">; OSBYTE &8F: issue service request</span></td> </tr> <tr id="addr-BBDC"> <td class="addr"><a href="#addr-BBDC">BBDC</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="12 &0C %00001100 FF">#&0c</span></span> <span class="comment">; X=&0C: service 12 (NMI claim)</span></td> </tr> <tr id="addr-BBDE"> <td class="addr"><a href="#addr-BBDE">BBDE</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y = NMI owner return value</span></td> </tr> <tr id="addr-BBE0"> <td class="addr"><a href="#addr-BBE0">BBE0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Issue service call Issue paged ROM service call, Reason X</span></td> </tr> <tr id="addr-BBE3"> <td class="addr"><a href="#addr-BBE3">BBE3</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10E1">wksp_nmi_owner</span></span> <span class="comment">; Save NMI owner for release</span></td> </tr> <tr id="addr-BBE6"> <td class="addr"><a href="#addr-BBE6">BBE6</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BBE7"> <td colspan="2"><div class="sub-header"> <h3>Release NMI via service call 11</h3> <div class="sub-desc"><p>Issue service call 11 (NMI released) via OSBYTE &8F to release the NMI handler after floppy disc operations.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BBE7">BBE7</a></td> <td><span class="label">.release_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BFDD">← BFDD JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&10E1">wksp_nmi_owner</span></span> <span class="comment">; Retrieve NMI owner</span></td> </tr> <tr id="addr-BBEA"> <td class="addr"><a href="#addr-BBEA">BBEA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="143 &8F %10001111">#osbyte_issue_service_request</span></span> <span class="comment">; OSBYTE &8F: issue service request</span></td> </tr> <tr id="addr-BBEC"> <td class="addr"><a href="#addr-BBEC">BBEC</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="11 &0B %00001011 VT">#&0b</span></span> <span class="comment">; X=&0B: service 11 (NMI released)</span></td> </tr> <tr id="addr-BBEE"> <td class="addr"><a href="#addr-BBEE">BBEE</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><span class="ext-label" data-tip="&FFF4">osbyte</span></span> <span class="comment">; Issue service call Issue paged ROM service call, Reason X</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table class="relocated"> <tr id="addr-BBF1"> <td colspan="3"><div class="sub-header"> <h3>Copy NMI handler code to NMI workspace</h3> <div class="sub-desc"><p>Copy the NMI handler routine from ROM to the NMI workspace at &0D00. The NMI handler is used for byte-by-byte data transfer between the WD1770 and memory.</p> </div> </div></td> </tr> <tr class="addr-header"><th>ROM</th><th>Exec</th><th></th></tr> <tr> <td class="addr"><a href="#addr-BBF1">BBF1</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.copy_code_to_nmi_space<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BBB0">← BBB0 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="72 &48 %01001000 'H'">#&48</span></span> <span class="comment">; Y=&48: copy 73 bytes of NMI code</span></td> </tr> <tr id="addr-BBF3"> <td class="addr"><a href="#addr-BBF3">BBF3</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.copy_nmi_code_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BBFA">← BBFA BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&BC79 – NMI handler code (copied to &0D00)">nmi_handler_rom</span>,y</span> <span class="comment">; Read NMI handler byte from ROM</span></td> </tr> <tr id="addr-BBF6"> <td class="addr"><a href="#addr-BBF6">BBF6</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><a href="#addr-0D00" data-tip="&0D00">nmi_workspace</a>,y</span> <span class="comment">; Write to NMI workspace</span></td> </tr> <tr id="addr-BBF9"> <td class="addr"><a href="#addr-BBF9">BBF9</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte (loop back)</span></td> </tr> <tr id="addr-BBFA"> <td class="addr"><a href="#addr-BBFA">BBFA</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BBF3" data-tip="&BBF3">copy_nmi_code_loop</a></span> <span class="comment">; Loop until all bytes copied</span></td> </tr> <tr id="addr-BBFC"> <td class="addr"><a href="#addr-BBFC">BBFC</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: get memory address low from blk</span></td> </tr> <tr id="addr-BBFE"> <td class="addr"><a href="#addr-BBFE">BBFE</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get transfer address low byte</span></td> </tr> <tr id="addr-BC00"> <td class="addr"><a href="#addr-BC00">BC00</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0E">nmi_read_addr_lo</span></span> <span class="comment">; Patch NMI handler with address low</span></td> </tr> <tr id="addr-BC03"> <td class="addr"><a href="#addr-BC03">BC03</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-BC04"> <td class="addr"><a href="#addr-BC04">BC04</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get transfer address high byte</span></td> </tr> <tr id="addr-BC06"> <td class="addr"><a href="#addr-BC06">BC06</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0F">nmi_read_addr_hi</span></span> <span class="comment">; Patch NMI handler with address high</span></td> </tr> <tr id="addr-BC09"> <td class="addr"><a href="#addr-BC09">BC09</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check control flags</span></td> </tr> <tr id="addr-BC0B"> <td class="addr"><a href="#addr-BC0B">BC0B</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BC12" data-tip="&BC12">check_tube_for_nmi</a></span> <span class="comment">; Bit 7 set: reading from disc</span></td> </tr> <tr id="addr-BC0D"> <td class="addr"><a href="#addr-BC0D">BC0D</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="95 &5F %01011111 '_'">#&5f</span></span> <span class="comment">; Writing: patch NMI with STA opcode</span></td> </tr> <tr id="addr-BC0F"> <td class="addr"><a href="#addr-BC0F">BC0F</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D05">nmi_rw_opcode</span></span> <span class="comment">; Store at NMI read/write instruction</span></td> </tr> <tr id="addr-BC12"> <td class="addr"><a href="#addr-BC12">BC12</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.check_tube_for_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC0B">← BC0B BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Tube in use?</span></td> </tr> <tr id="addr-BC14"> <td class="addr"><a href="#addr-BC14">BC14</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-BC21" data-tip="&BC21">setup_direct_nmi</a></span> <span class="comment">; No, use direct memory NMI handler</span></td> </tr> <tr id="addr-BC16"> <td class="addr"><a href="#addr-BC16">BC16</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Get control flags for Tube setup</span></td> </tr> <tr id="addr-BC18"> <td class="addr"><a href="#addr-BC18">BC18</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="253 &FD %11111101">#&fd</span></span> <span class="comment">; Clear bit 1 (read/write direction)</span></td> </tr> <tr id="addr-BC1A"> <td class="addr"><a href="#addr-BC1A">BC1A</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Store updated control flags</span></td> </tr> <tr id="addr-BC1C"> <td class="addr"><a href="#addr-BC1C">BC1C</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BC2D" data-tip="&BC2D">setup_tube_nmi_transfer</a></span> <span class="comment">; Set up Tube transfer parameters</span></td> </tr> <tr id="addr-BC1F"> <td class="addr"><a href="#addr-BC1F">BC1F</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BC24" data-tip="&BC24">store_nmi_completion</a></span> <span class="comment">; Tube read: use read NMI handler</span></td> </tr> <tr id="addr-BC21"> <td class="addr"><a href="#addr-BC21">BC21</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.setup_direct_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC14">← BC14 BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BC5C" data-tip="&BC5C">setup_direct_write_nmi</a></span> <span class="comment">; Set up direct memory NMI handler</span></td> </tr> <tr id="addr-BC24"> <td class="addr"><a href="#addr-BC24">BC24</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.store_nmi_completion<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC1F">← BC1F BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5F">nmi_completion</span></span> <span class="comment">; Store NMI completion flag</span></td> </tr> <tr id="addr-BC27"> <td class="addr"><a href="#addr-BC27">BC27</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00F4">romsel_copy</span></span> <span class="comment">; Get current ROM number</span></td> </tr> <tr id="addr-BC29"> <td class="addr"><a href="#addr-BC29">BC29</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D34">nmi_saved_rom</span></span> <span class="comment">; Patch NMI handler with ROM number</span></td> </tr> <tr id="addr-BC2C"> <td class="addr"><a href="#addr-BC2C">BC2C</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BC2D"> <td class="addr"><a href="#addr-BC2D">BC2D</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.setup_tube_nmi_transfer<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC1C">← BC1C JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Get control flags</span></td> </tr> <tr id="addr-BC2F"> <td class="addr"><a href="#addr-BC2F">BC2F</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">ROL</span> <span class="comment">; Rotate bit 7 into carry</span></td> </tr> <tr id="addr-BC30"> <td class="addr"><a href="#addr-BC30">BC30</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0 (will become direction flag)</span></td> </tr> <tr id="addr-BC32"> <td class="addr"><a href="#addr-BC32">BC32</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">ROL</span> <span class="comment">; Rotate carry into bit 0</span></td> </tr> <tr id="addr-BC33"> <td class="addr"><a href="#addr-BC33">BC33</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Y=&10: Tube workspace page</span></td> </tr> <tr id="addr-BC35"> <td class="addr"><a href="#addr-BC35">BC35</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="39 &27 %00100111 '''">#&27</span></span> <span class="comment">; X=&27: Tube workspace offset</span></td> </tr> <tr id="addr-BC37"> <td class="addr"><a href="#addr-BC37">BC37</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><span class="ext-label" data-tip="&0406">tube_entry</span></span> <span class="comment">; Start Tube transfer</span></td> </tr> <tr id="addr-BC3A"> <td class="addr"><a href="#addr-BC3A">BC3A</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Get control flags again</span></td> </tr> <tr id="addr-BC3C"> <td class="addr"><a href="#addr-BC3C">BC3C</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Bit 4 set (sector count specified)?</span></td> </tr> <tr id="addr-BC3E"> <td class="addr"><a href="#addr-BC3E">BC3E</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BC4F" data-tip="&BC4F">return_44</a></span> <span class="comment">; No, return (single sector)</span></td> </tr> <tr id="addr-BC40"> <td class="addr"><a href="#addr-BC40">BC40</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check read/write direction</span></td> </tr> <tr id="addr-BC42"> <td class="addr"><a href="#addr-BC42">BC42</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BC50" data-tip="&BC50">setup_tube_read_nmi</a></span> <span class="comment">; Bit 7 set: reading from disc</span></td> </tr> <tr id="addr-BC44"> <td class="addr"><a href="#addr-BC44">BC44</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; Y=7: copy 8 bytes of Tube write NMI</span></td> </tr> <tr id="addr-BC46"> <td class="addr"><a href="#addr-BC46">BC46</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.copy_tube_write_nmi_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC4D">← BC4D BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&BCED – NMI patch: write Tube to disc">nmi_tube_write_code</span>,y</span> <span class="comment">; Get Tube write NMI handler byte</span></td> </tr> <tr id="addr-BC49"> <td class="addr"><a href="#addr-BC49">BC49</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><a href="#addr-0D0A" data-tip="&0D0A">nmi_rw_code</a>,y</span> <span class="comment">; Copy to NMI workspace</span></td> </tr> <tr id="addr-BC4C"> <td class="addr"><a href="#addr-BC4C">BC4C</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-BC4D"> <td class="addr"><a href="#addr-BC4D">BC4D</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BC46" data-tip="&BC46">copy_tube_write_nmi_loop</a></span> <span class="comment">; Loop for 8 bytes</span></td> </tr> <tr id="addr-BC4F"> <td class="addr"><a href="#addr-BC4F">BC4F</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.return_44<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC3E">← BC3E BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BC50"> <td class="addr"><a href="#addr-BC50">BC50</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.setup_tube_read_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC42">← BC42 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; Y=7: copy 8 bytes of Tube read NMI</span></td> </tr> <tr id="addr-BC52"> <td class="addr"><a href="#addr-BC52">BC52</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.copy_tube_read_nmi_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC59">← BC59 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&BCF5 – NMI patch: read disc to Tube">nmi_tube_read_code</span>,y</span> <span class="comment">; Get Tube read NMI handler byte</span></td> </tr> <tr id="addr-BC55"> <td class="addr"><a href="#addr-BC55">BC55</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><a href="#addr-0D0A" data-tip="&0D0A">nmi_rw_code</a>,y</span> <span class="comment">; Copy to NMI workspace</span></td> </tr> <tr id="addr-BC58"> <td class="addr"><a href="#addr-BC58">BC58</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-BC59"> <td class="addr"><a href="#addr-BC59">BC59</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BC52" data-tip="&BC52">copy_tube_read_nmi_loop</a></span> <span class="comment">; Loop for 8 bytes</span></td> </tr> <tr id="addr-BC5B"> <td class="addr"><a href="#addr-BC5B">BC5B</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BC5C"> <td class="addr"><a href="#addr-BC5C">BC5C</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.setup_direct_write_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC21">← BC21 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check read/write direction</span></td> </tr> <tr id="addr-BC5E"> <td class="addr"><a href="#addr-BC5E">BC5E</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BC78" data-tip="&BC78">return_45</a></span> <span class="comment">; Reading: use default NMI handler</span></td> </tr> <tr id="addr-BC60"> <td class="addr"><a href="#addr-BC60">BC60</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="13 &0D %00001101 CR">#&0d</span></span> <span class="comment">; Y=&0D: copy 14 bytes of write NMI</span></td> </tr> <tr id="addr-BC62"> <td class="addr"><a href="#addr-BC62">BC62</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.copy_write_nmi_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC69">← BC69 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&BCDF – NMI patch: write memory to disc">nmi_write_code</span>,y</span> <span class="comment">; Get direct memory write NMI byte</span></td> </tr> <tr id="addr-BC65"> <td class="addr"><a href="#addr-BC65">BC65</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><a href="#addr-0D0A" data-tip="&0D0A">nmi_rw_code</a>,y</span> <span class="comment">; Copy to NMI workspace</span></td> </tr> <tr id="addr-BC68"> <td class="addr"><a href="#addr-BC68">BC68</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">DEY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-BC69"> <td class="addr"><a href="#addr-BC69">BC69</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BC62" data-tip="&BC62">copy_write_nmi_loop</a></span> <span class="comment">; Loop for 14 bytes</span></td> </tr> <tr id="addr-BC6B"> <td class="addr"><a href="#addr-BC6B">BC6B</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; Y=1: patch transfer address</span></td> </tr> <tr id="addr-BC6D"> <td class="addr"><a href="#addr-BC6D">BC6D</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get transfer addr low from block</span></td> </tr> <tr id="addr-BC6F"> <td class="addr"><a href="#addr-BC6F">BC6F</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0B">nmi_write_addr_lo</span></span> <span class="comment">; Patch NMI handler with addr low</span></td> </tr> <tr id="addr-BC72"> <td class="addr"><a href="#addr-BC72">BC72</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">INY</span></td> </tr> <tr id="addr-BC73"> <td class="addr"><a href="#addr-BC73">BC73</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get transfer addr high from block</span></td> </tr> <tr id="addr-BC75"> <td class="addr"><a href="#addr-BC75">BC75</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0C">nmi_write_addr_hi</span></span> <span class="comment">; Patch NMI handler with addr high</span></td> </tr> <tr id="addr-BC78"> <td class="addr"><a href="#addr-BC78">BC78</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.return_45<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BC5E">← BC5E BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; NMI handler code (copied to &0D00)</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; NMI handler for floppy disc byte-by-byte data transfer.</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; Copied from ROM to the NMI workspace at &0D00 before ; each</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; floppy operation. The WD1770 fires an NMI on each byte</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; transferred (DRQ) and on command completion.</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; The handler has three paths:</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <ol> <li>DRQ (status & &1F = 3): transfer one byte between</li> </ol> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> the WD1770 data register and memory. The code at </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> &0D0A-&0D17 is patched with one of three variants: </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> nmi_write_code (direct memory write to disc), </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> nmi_tube_write_code (Tube to disc), or </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> nmi_tube_read_code (disc to Tube). The default </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> (nmi_code_rw) is direct memory read from disc. </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <ol start="2"> <li>Error (status & &58 != 0): store the error status</li> </ol> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> and set bit 0 of zp_floppy_control and </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> zp_floppy_state to signal the error to the caller. </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <ol start="3"> <li>Completion (no DRQ, no error): if multi-sector mode</li> </ol> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> is active (bit 6 of zp_floppy_state), switch to </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> ROM 0 and call the track-stepping routine to set up </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; <pre><code> the next sector. Otherwise mark transfer complete. </code></pre> </span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr id="addr-0D00"> <td class="addr"><span id="addr-BC79"></span><a href="#addr-BC79">BC79</a></td> <td class="addr runtime-addr"><a href="#addr-0D00">0D00</a></td> <td><span class="label">.nmi_workspace<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BBF6">← BBF6 STA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">PHA</span> <span class="comment">; Save A (NMI must preserve all regs)</span></td> </tr> <tr id="addr-0D01"> <td class="addr"><span id="addr-BC7A"></span><a href="#addr-BC7A">BC7A</a></td> <td class="addr runtime-addr"><a href="#addr-0D01">0D01</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Read WD1770 status register</span></td> </tr> <tr id="addr-0D04"> <td class="addr"><span id="addr-BC7D"></span><a href="#addr-BC7D">BC7D</a></td> <td class="addr runtime-addr"><a href="#addr-0D04">0D04</a></td> <td><span class="label">.sub_c0d04</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="31 &1F %00011111 US">#&1f</span></span> <span class="comment">; Mask to low 5 status bits</span></td> </tr> <tr id="addr-0D06"> <td class="addr"><span id="addr-BC7F"></span><a href="#addr-BC7F">BC7F</a></td> <td class="addr runtime-addr"><a href="#addr-0D06">0D06</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="3 &03 %00000011 ETX">#3</span></span> <span class="comment">; Status = 3 (data request)?</span></td> </tr> <tr id="addr-0D08"> <td class="addr"><span id="addr-BC81"></span><a href="#addr-BC81">BC81</a></td> <td class="addr runtime-addr"><a href="#addr-0D08">0D08</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-0D1A" data-tip="&0D1A – NMI status/error handler">nmi_check_status_error</a></span> <span class="comment">; No: check for error or completion</span></td> </tr> <tr id="addr-0D0A"> <td class="addr"><span id="addr-BC83"></span><a href="#addr-BC83">BC83</a></td> <td class="addr runtime-addr"><a href="#addr-0D0A">0D0A</a></td> <td><span class="label">.nmi_rw_code<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BC49">← BC49 STA</a><a href="#addr-BC55">← BC55 STA</a><a href="#addr-BC65">← BC65 STA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FE87">fdc_1770_data</span></span> <span class="comment">; Read byte from WD1770 data register</span></td> </tr> <tr id="addr-0D0D"> <td class="addr"><span id="addr-BC86"></span><a href="#addr-BC86">BC86</a></td> <td class="addr runtime-addr"><a href="#addr-0D0D">0D0D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FFFF">nmi_patched_addr</span></span> <span class="comment">; Store at transfer address (patched)</span></td> </tr> <tr id="addr-0D10"> <td class="addr"><span id="addr-BC89"></span><a href="#addr-BC89">BC89</a></td> <td class="addr runtime-addr"><a href="#addr-0D10">0D10</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&0D0E">nmi_read_addr_lo</span></span> <span class="comment">; Increment transfer address low byte</span></td> </tr> <tr id="addr-0D13"> <td class="addr"><span id="addr-BC8C"></span><a href="#addr-BC8C">BC8C</a></td> <td class="addr runtime-addr"><a href="#addr-0D13">0D13</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-0D18" data-tip="&0D18">nmi_transfer_done</a></span> <span class="comment">; No page crossing: skip high byte</span></td> </tr> <tr id="addr-0D15"> <td class="addr"><span id="addr-BC8E"></span><a href="#addr-BC8E">BC8E</a></td> <td class="addr runtime-addr"><a href="#addr-0D15">0D15</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&0D0F">nmi_read_addr_hi</span></span> <span class="comment">; Increment transfer address high byte</span></td> </tr> <tr id="addr-0D18"> <td class="addr"><span id="addr-BC91"></span><a href="#addr-BC91">BC91</a></td> <td class="addr runtime-addr"><a href="#addr-0D18">0D18</a></td> <td><span class="label">.nmi_transfer_done<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-0D10">← 0D10 BCS</a><a href="#addr-0D13">← 0D13 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore A</span></td> </tr> <tr id="addr-0D19"> <td class="addr"><span id="addr-BC92"></span><a href="#addr-BC92">BC92</a></td> <td class="addr runtime-addr"><a href="#addr-0D19">0D19</a></td> <td> <span class="opcode">RTI</span> <span class="comment">; Return from NMI</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> </table> <table class="relocated"> <tr id="addr-0D1A"> <td colspan="3"><div class="sub-header"> <h3>NMI status/error handler</h3> <div class="sub-desc"><p>Not a DRQ: check WD1770 status for error bits. Bits 6 (write protect), 4 (record not found), and 3 (CRC error) are tested via AND #&58. If any are set, store the error code and set the error flag in the control byte.</p> </div> </div></td> </tr> <tr class="addr-header"><th>ROM</th><th>Exec</th><th></th></tr> <tr> <td class="addr"><span id="addr-BC93"></span><a href="#addr-BC93">BC93</a></td> <td class="addr runtime-addr"><a href="#addr-0D1A">0D1A</a></td> <td><span class="label">.nmi_check_status_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-0D08">← 0D08 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="88 &58 %01011000 'X'">#&58</span></span> <span class="comment">; Test error bits: WP, RNF, CRC</span></td> </tr> <tr id="addr-0D1C"> <td class="addr"><span id="addr-BC95"></span><a href="#addr-BC95">BC95</a></td> <td class="addr runtime-addr"><a href="#addr-0D1C">0D1C</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-0D2C" data-tip="&0D2C – NMI end-of-operation handler">nmi_check_end_of_operation</a></span> <span class="comment">; No errors: check for end of operation</span></td> </tr> <tr id="addr-0D1E"> <td class="addr"><span id="addr-BC97"></span><a href="#addr-BC97">BC97</a></td> <td class="addr runtime-addr"><a href="#addr-0D1E">0D1E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Store error status for caller</span></td> </tr> <tr id="addr-0D20"> <td class="addr"><span id="addr-BC99"></span><a href="#addr-BC99">BC99</a></td> <td class="addr runtime-addr"><a href="#addr-0D20">0D20</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Rotate control flags right</span></td> </tr> <tr id="addr-0D22"> <td class="addr"><span id="addr-BC9B"></span><a href="#addr-BC9B">BC9B</a></td> <td class="addr runtime-addr"><a href="#addr-0D22">0D22</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for error flag</span></td> </tr> <tr id="addr-0D23"> <td class="addr"><span id="addr-BC9C"></span><a href="#addr-BC9C">BC9C</a></td> <td class="addr runtime-addr"><a href="#addr-0D23">0D23</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Set bit 0: error occurred</span></td> </tr> <tr id="addr-0D25"> <td class="addr"><span id="addr-BC9E"></span><a href="#addr-BC9E">BC9E</a></td> <td class="addr runtime-addr"><a href="#addr-0D25">0D25</a></td> <td><span class="label">.nmi_set_transfer_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-0D2E">← 0D2E BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Rotate state flags right</span></td> </tr> <tr id="addr-0D27"> <td class="addr"><span id="addr-BCA0"></span><a href="#addr-BCA0">BCA0</a></td> <td class="addr runtime-addr"><a href="#addr-0D27">0D27</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for complete flag</span></td> </tr> <tr id="addr-0D28"> <td class="addr"><span id="addr-BCA1"></span><a href="#addr-BCA1">BCA1</a></td> <td class="addr runtime-addr"><a href="#addr-0D28">0D28</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Set bit 0: transfer complete</span></td> </tr> <tr id="addr-0D2A"> <td class="addr"><span id="addr-BCA3"></span><a href="#addr-BCA3">BCA3</a></td> <td class="addr runtime-addr"><a href="#addr-0D2A">0D2A</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore A</span></td> </tr> <tr id="addr-0D2B"> <td class="addr"><span id="addr-BCA4"></span><a href="#addr-BCA4">BCA4</a></td> <td class="addr runtime-addr"><a href="#addr-0D2B">0D2B</a></td> <td> <span class="opcode">RTI</span> <span class="comment">; Return from NMI</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> </table> <table class="relocated"> <tr id="addr-0D2C"> <td colspan="3"><div class="sub-header"> <h3>NMI end-of-operation handler</h3> <div class="sub-desc"><p>No error and no DRQ: the WD1770 command has completed. If multi-sector mode (bit 6 of zp_floppy_state) is not active, just mark the transfer complete. Otherwise, save the current ROM state, switch to ROM 0, and call the track-stepping routine to prepare the next sector for transfer.</p> </div> </div></td> </tr> <tr class="addr-header"><th>ROM</th><th>Exec</th><th></th></tr> <tr> <td class="addr"><span id="addr-BCA5"></span><a href="#addr-BCA5">BCA5</a></td> <td class="addr runtime-addr"><a href="#addr-0D2C">0D2C</a></td> <td><span class="label">.nmi_check_end_of_operation<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-0D1C">← 0D1C BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Multi-sector mode active? (bit 6)</span></td> </tr> <tr id="addr-0D2E"> <td class="addr"><span id="addr-BCA7"></span><a href="#addr-BCA7">BCA7</a></td> <td class="addr runtime-addr"><a href="#addr-0D2E">0D2E</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-0D25" data-tip="&0D25">nmi_set_transfer_complete</a></span> <span class="comment">; No: mark complete and return</span></td> </tr> <tr id="addr-0D30"> <td class="addr"><span id="addr-BCA9"></span><a href="#addr-BCA9">BCA9</a></td> <td class="addr runtime-addr"><a href="#addr-0D30">0D30</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00F4">romsel_copy</span></span> <span class="comment">; Save current ROM bank number</span></td> </tr> <tr id="addr-0D32"> <td class="addr"><span id="addr-BCAB"></span><a href="#addr-BCAB">BCAB</a></td> <td class="addr runtime-addr"><a href="#addr-0D32">0D32</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push onto stack</span></td> </tr> <tr id="addr-0D33"> <td class="addr"><span id="addr-BCAC"></span><a href="#addr-BCAC">BCAC</a></td> <td class="addr runtime-addr"><a href="#addr-0D33">0D33</a></td> <td><span class="label">.sub_c0d33</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Select ROM bank 0</span></td> </tr> <tr id="addr-0D35"> <td class="addr"><span id="addr-BCAE"></span><a href="#addr-BCAE">BCAE</a></td> <td class="addr runtime-addr"><a href="#addr-0D35">0D35</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00F4">romsel_copy</span></span> <span class="comment">; Update ROM select shadow copy</span></td> </tr> <tr id="addr-0D37"> <td class="addr"><span id="addr-BCB0"></span><a href="#addr-BCB0">BCB0</a></td> <td class="addr runtime-addr"><a href="#addr-0D37">0D37</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE30">romsel</span></span> <span class="comment">; Switch to ROM bank 0</span></td> </tr> <tr id="addr-0D3A"> <td class="addr"><span id="addr-BCB3"></span><a href="#addr-BCB3">BCB3</a></td> <td class="addr runtime-addr"><a href="#addr-0D3A">0D3A</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Save X register</span></td> </tr> <tr id="addr-0D3B"> <td class="addr"><span id="addr-BCB4"></span><a href="#addr-BCB4">BCB4</a></td> <td class="addr runtime-addr"><a href="#addr-0D3B">0D3B</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push onto stack</span></td> </tr> <tr id="addr-0D3C"> <td class="addr"><span id="addr-BCB5"></span><a href="#addr-BCB5">BCB5</a></td> <td class="addr runtime-addr"><a href="#addr-0D3C">0D3C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BE69" data-tip="&BE69 – Advance multi-sector transfer to next sector">floppy_next_sector</a></span> <span class="comment">; Advance to next sector</span></td> </tr> <tr id="addr-0D3F"> <td class="addr"><span id="addr-BCB8"></span><a href="#addr-BCB8">BCB8</a></td> <td class="addr runtime-addr"><a href="#addr-0D3F">0D3F</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore X register</span></td> </tr> <tr id="addr-0D40"> <td class="addr"><span id="addr-BCB9"></span><a href="#addr-BCB9">BCB9</a></td> <td class="addr runtime-addr"><a href="#addr-0D40">0D40</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Pull from stack</span></td> </tr> <tr id="addr-0D41"> <td class="addr"><span id="addr-BCBA"></span><a href="#addr-BCBA">BCBA</a></td> <td class="addr runtime-addr"><a href="#addr-0D41">0D41</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore original ROM bank</span></td> </tr> <tr id="addr-0D42"> <td class="addr"><span id="addr-BCBB"></span><a href="#addr-BCBB">BCBB</a></td> <td class="addr runtime-addr"><a href="#addr-0D42">0D42</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00F4">romsel_copy</span></span> <span class="comment">; Update ROM select shadow copy</span></td> </tr> <tr id="addr-0D44"> <td class="addr"><span id="addr-BCBD"></span><a href="#addr-BCBD">BCBD</a></td> <td class="addr runtime-addr"><a href="#addr-0D44">0D44</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE30">romsel</span></span> <span class="comment">; Switch back to original ROM bank</span></td> </tr> <tr id="addr-0D47"> <td class="addr"><span id="addr-BCC0"></span><a href="#addr-BCC0">BCC0</a></td> <td class="addr runtime-addr"><a href="#addr-0D47">0D47</a></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore A</span></td> </tr> <tr id="addr-0D48"> <td class="addr"><span id="addr-BCC1"></span><a href="#addr-BCC1">BCC1</a></td> <td class="addr runtime-addr"><a href="#addr-0D48">0D48</a></td> <td> <span class="opcode">RTI</span> <span class="comment">; Return from NMI</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> </table> <table class="relocated"> <tr id="addr-BCC2"> <td colspan="3"><div class="sub-header"> <h3>Wait for floppy NMI transfer to complete</h3> <div class="sub-desc"><p>Wait for the WD1770 floppy disc controller to complete a data transfer. Polls the controller status register.</p> </div> </div></td> </tr> <tr class="addr-header"><th>ROM</th><th>Exec</th><th></th></tr> <tr> <td class="addr"><a href="#addr-BCC2">BCC2</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.floppy_wait_nmi_finish<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-BAE9">← BAE9 JSR</a><a href="#addr-BCCD">← BCCD BEQ</a><a href="#addr-BCD1">← BCD1 BPL</a><a href="#addr-BD16">← BD16 JMP</a><a href="#addr-BD49">← BD49 JMP</a><a href="#addr-BE33">← BE33 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Check if transfer already complete</span></td> </tr> <tr id="addr-BCC4"> <td class="addr"><a href="#addr-BCC4">BCC4</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">ROR</span> <span class="comment">; Bit 0 of zp_a2 into carry</span></td> </tr> <tr id="addr-BCC5"> <td class="addr"><a href="#addr-BCC5">BCC5</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BCC8" data-tip="&BCC8">poll_nmi_complete</a></span> <span class="comment">; Carry set: already done</span></td> </tr> <tr id="addr-BCC7"> <td class="addr"><a href="#addr-BCC7">BCC7</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Transfer already complete, return</span></td> </tr> <tr id="addr-BCC8"> <td class="addr"><a href="#addr-BCC8">BCC8</a></td> <td class="addr runtime-addr"></td> <td><span class="label">.poll_nmi_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BCC5">← BCC5 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5D">nmi_adfs_flags</span></span> <span class="comment">; Read NMI completion flag</span></td> </tr> <tr id="addr-BCCB"> <td class="addr"><a href="#addr-BCCB">BCCB</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Bit 4 set = DRQ complete?</span></td> </tr> <tr id="addr-BCCD"> <td class="addr"><a href="#addr-BCCD">BCCD</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BCC2" data-tip="&BCC2 – Wait for floppy NMI transfer to complete">floppy_wait_nmi_finish</a></span> <span class="comment">; Not yet, keep waiting</span></td> </tr> <tr id="addr-BCCF"> <td class="addr"><a href="#addr-BCCF">BCCF</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00FF">zp_escape_flag</span></span> <span class="comment">; Check for Escape condition</span></td> </tr> <tr id="addr-BCD1"> <td class="addr"><a href="#addr-BCD1">BCD1</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BCC2" data-tip="&BCC2 – Wait for floppy NMI transfer to complete">floppy_wait_nmi_finish</a></span> <span class="comment">; Bit 7 clear: no Escape</span></td> </tr> <tr id="addr-BCD3"> <td class="addr"><a href="#addr-BCD3">BCD3</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Escape pressed: stop drive</span></td> </tr> <tr id="addr-BCD5"> <td class="addr"><a href="#addr-BCD5">BCD5</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE80">fdc_1770_drive_control</span></span> <span class="comment">; Write 0 to FDC control</span></td> </tr> <tr id="addr-BCD8"> <td class="addr"><a href="#addr-BCD8">BCD8</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="111 &6F %01101111 'o'">#&6f</span></span> <span class="comment">; Error &6F: drive overrun/Escape</span></td> </tr> <tr id="addr-BCDA"> <td class="addr"><a href="#addr-BCDA">BCDA</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Store drive overrun error code</span></td> </tr> <tr id="addr-BCDC"> <td class="addr"><a href="#addr-BCDC">BCDC</a></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span> <span class="comment">; Handle floppy error</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; NMI patch: write memory to disc</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; Patched into &0D0A when writing to floppy disc from host</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; memory. Reads a byte from the self-modifying transfer</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; address and writes it to the WD1770 data register.</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr id="addr-0D0A"> <td class="addr"><span id="addr-BCDF"></span><a href="#addr-BCDF">BCDF</a></td> <td class="addr runtime-addr"><a href="#addr-0D0A">0D0A</a></td> <td><span class="label">.nmi_rw_code<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BC49">← BC49 STA</a><a href="#addr-BC55">← BC55 STA</a><a href="#addr-BC65">← BC65 STA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FFFF">nmi_patched_addr</span></span> <span class="comment">; Read byte from transfer address</span></td> </tr> <tr id="addr-0D0D"> <td class="addr"><span id="addr-BCE2"></span><a href="#addr-BCE2">BCE2</a></td> <td class="addr runtime-addr"><a href="#addr-0D0D">0D0D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE87">fdc_1770_data</span></span> <span class="comment">; Write to WD1770 data register</span></td> </tr> <tr id="addr-0D10"> <td class="addr"><span id="addr-BCE5"></span><a href="#addr-BCE5">BCE5</a></td> <td class="addr runtime-addr"><a href="#addr-0D10">0D10</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&0D0B">nmi_write_addr_lo</span></span> <span class="comment">; Increment transfer address low</span></td> </tr> <tr id="addr-0D13"> <td class="addr"><span id="addr-BCE8"></span><a href="#addr-BCE8">BCE8</a></td> <td class="addr runtime-addr"><a href="#addr-0D13">0D13</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-0D18" data-tip="&0D18">nmi_transfer_done</a></span> <span class="comment">; No page crossing: done</span></td> </tr> <tr id="addr-0D15"> <td class="addr"><span id="addr-BCEA"></span><a href="#addr-BCEA">BCEA</a></td> <td class="addr runtime-addr"><a href="#addr-0D15">0D15</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&0D0C">nmi_write_addr_hi</span></span> <span class="comment">; Increment transfer address high</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; NMI patch: write Tube to disc</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; Patched into &0D0A when writing to floppy disc via the</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; Tube. Reads a byte from Tube data register 3 and writes</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; it to the WD1770 data register.</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr id="addr-0D0A"> <td class="addr"><span id="addr-BCED"></span><a href="#addr-BCED">BCED</a></td> <td class="addr runtime-addr"><a href="#addr-0D0A">0D0A</a></td> <td><span class="label">.nmi_rw_code<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BC49">← BC49 STA</a><a href="#addr-BC55">← BC55 STA</a><a href="#addr-BC65">← BC65 STA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FEE5">tube_data_register_3</span></span> <span class="comment">; Read byte from Tube R3</span></td> </tr> <tr id="addr-0D0D"> <td class="addr"><span id="addr-BCF0"></span><a href="#addr-BCF0">BCF0</a></td> <td class="addr runtime-addr"><a href="#addr-0D0D">0D0D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE87">fdc_1770_data</span></span> <span class="comment">; Write to WD1770 data register</span></td> </tr> <tr id="addr-0D10"> <td class="addr"><span id="addr-BCF3"></span><a href="#addr-BCF3">BCF3</a></td> <td class="addr runtime-addr"><a href="#addr-0D10">0D10</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-0D18" data-tip="&0D18">nmi_transfer_done</a></span> <span class="comment">; Always branch: done</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; NMI patch: read disc to Tube</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; Patched into &0D0A when reading from floppy disc via the</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; Tube. Reads a byte from the WD1770 data register and</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td><span class="comment">; writes it to Tube data register 3.</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> <tr id="addr-0D0A"> <td class="addr"><span id="addr-BCF5"></span><a href="#addr-BCF5">BCF5</a></td> <td class="addr runtime-addr"><a href="#addr-0D0A">0D0A</a></td> <td><span class="label">.nmi_rw_code<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BC49">← BC49 STA</a><a href="#addr-BC55">← BC55 STA</a><a href="#addr-BC65">← BC65 STA</a></span></span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FE87">fdc_1770_data</span></span> <span class="comment">; Read byte from WD1770</span></td> </tr> <tr id="addr-0D0D"> <td class="addr"><span id="addr-BCF8"></span><a href="#addr-BCF8">BCF8</a></td> <td class="addr runtime-addr"><a href="#addr-0D0D">0D0D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FEE5">tube_data_register_3</span></span> <span class="comment">; Write to Tube R3</span></td> </tr> <tr id="addr-0D10"> <td class="addr"><span id="addr-BCFB"></span><a href="#addr-BCFB">BCFB</a></td> <td class="addr runtime-addr"><a href="#addr-0D10">0D10</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-0D18" data-tip="&0D18">nmi_transfer_done</a></span> <span class="comment">; Always branch: done</span></td> </tr> <tr> <td class="addr"></td> <td class="addr runtime-addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BCFD"> <td colspan="2"><div class="sub-header"> <h3>Select and issue FDC read/write command</h3> <div class="sub-desc"><p>Choose WD1770 read (&80) or write (&A0) command based on transfer direction. Apply head load delay and step rate, then issue the command.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BCFD">BCFD</a></td> <td><span class="label">.select_fdc_rw_command<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BAC0">← BAC0 JSR</a><a href="#addr-BD66">← BD66 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check read/write direction</span></td> </tr> <tr id="addr-BCFF"> <td class="addr"><a href="#addr-BCFF">BCFF</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BD0E" data-tip="&BD0E">set_read_command</a></span> <span class="comment">; Reading: use read command</span></td> </tr> <tr id="addr-BD01"> <td class="addr"><a href="#addr-BD01">BD01</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Get current track</span></td> </tr> <tr id="addr-BD03"> <td class="addr"><a href="#addr-BD03">BD03</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="20 &14 %00010100 DC4">#&14</span></span> <span class="comment">; Track >= 20?</span></td> </tr> <tr id="addr-BD05"> <td class="addr"><a href="#addr-BD05">BD05</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="160 &A0 %10100000">#&a0</span></span> <span class="comment">; A=&A0: write command base</span></td> </tr> <tr id="addr-BD07"> <td class="addr"><a href="#addr-BD07">BD07</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BD10" data-tip="&BD10">issue_fdc_command</a></span> <span class="comment">; Track < 20: no step rate delay</span></td> </tr> <tr id="addr-BD09"> <td class="addr"><a href="#addr-BD09">BD09</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0D56">nmi_step_rate</span></span> <span class="comment">; OR in step rate from settings</span></td> </tr> <tr id="addr-BD0C"> <td class="addr"><a href="#addr-BD0C">BD0C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BD10" data-tip="&BD10">issue_fdc_command</a></span> <span class="comment">; Always branch (non-zero result)</span></td> </tr> <tr id="addr-BD0E"> <td class="addr"><a href="#addr-BD0E">BD0E</a></td> <td><span class="label">.set_read_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BCFF">← BCFF BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; A=&80: read command base</span></td> </tr> <tr id="addr-BD10"> <td class="addr"><a href="#addr-BD10">BD10</a></td> <td><span class="label">.issue_fdc_command<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BD07">← BD07 BCC</a><a href="#addr-BD0C">← BD0C BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD4C" data-tip="&BD4C – Apply head load delay to FDC command">apply_head_load_flag</a></span></td> </tr> <tr id="addr-BD13"> <td class="addr"><a href="#addr-BD13">BD13</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue FDC command</span></td> </tr> <tr id="addr-BD16"> <td class="addr"><a href="#addr-BD16">BD16</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BCC2" data-tip="&BCC2 – Wait for floppy NMI transfer to complete">floppy_wait_nmi_finish</a></span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD19"> <td colspan="2"><div class="sub-header"> <h3>Unused: select floppy disc side 0</h3> <div class="sub-desc"><p>Unreferenced routine that clears bit 2 of the NMI drive control byte at &0D5E, selecting side 0 of a double-sided floppy disc. The inverse of floppy_set_side_1 which sets bit 2. Dead code — side 0 is the default so no explicit selection is needed.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD19">BD19</a></td> <td><span class="label">.floppy_set_side_0_unused</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Get NMI drive control byte</span></td> </tr> <tr id="addr-BD1C"> <td class="addr"><a href="#addr-BD1C">BD1C</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="251 &FB %11111011">#&fb</span></span> <span class="comment">; Clear bit 2 (select side 0)</span></td> </tr> <tr id="addr-BD1E"> <td class="addr"><a href="#addr-BD1E">BD1E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Store updated control byte</span></td> </tr> <tr id="addr-BD21"> <td class="addr"><a href="#addr-BD21">BD21</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD22"> <td colspan="2"><div class="sub-header"> <h3>Select floppy disc side 1</h3> <div class="sub-desc"><p>Select side 1 (the second side) of a double-sided floppy disc by setting the appropriate control register bit.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD22">BD22</a></td> <td><span class="label">.floppy_set_side_1<span class="ref-badge">←4</span><span class="ref-popup"><a href="#addr-BA98">← BA98 JSR</a><a href="#addr-BE5C">← BE5C JSR</a><a href="#addr-BEC0">← BEC0 JSR</a><a href="#addr-BF9F">← BF9F JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Set side select flag</span></td> </tr> <tr id="addr-BD25"> <td class="addr"><a href="#addr-BD25">BD25</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Set bit 2 (side 1 flag)</span></td> </tr> <tr id="addr-BD27"> <td class="addr"><a href="#addr-BD27">BD27</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Store in NMI drive control byte</span></td> </tr> <tr id="addr-BD2A"> <td class="addr"><a href="#addr-BD2A">BD2A</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD2B"> <td colspan="2"><div class="sub-header"> <h3>Clear floppy transfer complete flag</h3> <div class="sub-desc"><p>Clear bit 0 of the floppy transfer state byte.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD2B">BD2B</a></td> <td><span class="label">.clear_transfer_complete<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-BAC6">← BAC6 JSR</a><a href="#addr-BB06">← BB06 JMP</a><a href="#addr-BE2B">← BE2B JSR</a><a href="#addr-BE3C">← BE3C JSR</a><a href="#addr-BE54">← BE54 JSR</a><a href="#addr-BE69">← BE69 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Clear bit 0 of transfer state</span></td> </tr> <tr id="addr-BD2D"> <td class="addr"><a href="#addr-BD2D">BD2D</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry</span></td> </tr> <tr id="addr-BD2E"> <td class="addr"><a href="#addr-BD2E">BD2E</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Restore bit 0 cleared</span></td> </tr> <tr id="addr-BD30"> <td class="addr"><a href="#addr-BD30">BD30</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BD31"> <td class="addr"><a href="#addr-BD31">BD31</a></td> <td><span class="label">.clear_side_flag<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE57">← BE57 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Get transfer state</span></td> </tr> <tr id="addr-BD33"> <td class="addr"><a href="#addr-BD33">BD33</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="247 &F7 %11110111">#&f7</span></span> <span class="comment">; Clear bit 3 (side flag)</span></td> </tr> <tr id="addr-BD35"> <td class="addr"><a href="#addr-BD35">BD35</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Store updated state</span></td> </tr> <tr id="addr-BD37"> <td class="addr"><a href="#addr-BD37">BD37</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD38"> <td colspan="2"><div class="sub-header"> <h3>Clear floppy seek-in-progress flag</h3> <div class="sub-desc"><p>Clear bit 1 of the floppy transfer state byte.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD38">BD38</a></td> <td><span class="label">.clear_seek_flag<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BE3F">← BE3F JSR</a><a href="#addr-BE78">← BE78 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Get transfer state</span></td> </tr> <tr id="addr-BD3A"> <td class="addr"><a href="#addr-BD3A">BD3A</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="253 &FD %11111101">#&fd</span></span> <span class="comment">; Clear bit 1 (seek flag)</span></td> </tr> <tr id="addr-BD3C"> <td class="addr"><a href="#addr-BD3C">BD3C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Store updated state</span></td> </tr> <tr id="addr-BD3E"> <td class="addr"><a href="#addr-BD3E">BD3E</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD3F"> <td colspan="2"><div class="sub-header"> <h3>Seek floppy head to track 0</h3> <div class="sub-desc"><p>Issue a restore command to the WD1770 to seek the read/write head to track 0.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD3F">BD3F</a></td> <td><span class="label">.floppy_restore_track_0<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BABA">← BABA JSR</a><a href="#addr-BF51">← BF51 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: target track number = 0</span></td> </tr> <tr id="addr-BD41"> <td class="addr"><a href="#addr-BD41">BD41</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store as target track</span></td> </tr> <tr id="addr-BD43"> <td class="addr"><a href="#addr-BD43">BD43</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0D5C">nmi_drive_cmd</span></span> <span class="comment">; OR with drive select bits</span></td> </tr> <tr id="addr-BD46"> <td class="addr"><a href="#addr-BD46">BD46</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue restore command to WD1770</span></td> </tr> <tr id="addr-BD49"> <td class="addr"><a href="#addr-BD49">BD49</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BCC2" data-tip="&BCC2 – Wait for floppy NMI transfer to complete">floppy_wait_nmi_finish</a></span> <span class="comment">; Wait for command to complete</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD4C"> <td colspan="2"><div class="sub-header"> <h3>Apply head load delay to FDC command</h3> <div class="sub-desc"><p>If the head-loaded flag is set in the transfer state, OR bit 2 into A (the head load delay bit in WD1770 step/seek commands).</p> </div> <div class="sub-registers"><table> <tr><th rowspan="1">On Entry</th><td>A</td><td>FDC command byte</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>command with bit 2 set if head loaded</td></tr> <tr><td>X</td><td>preserved</td></tr> <tr><td>Y</td><td>preserved</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD4C">BD4C</a></td> <td><span class="label">.apply_head_load_flag<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BD10">← BD10 JSR</a><a href="#addr-BE7D">← BE7D JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Rotate head-loaded flag to carry</span></td> </tr> <tr id="addr-BD4F"> <td class="addr"><a href="#addr-BD4F">BD4F</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BD54" data-tip="&BD54">restore_head_flag</a></span> <span class="comment">; Not loaded: skip</span></td> </tr> <tr id="addr-BD51"> <td class="addr"><a href="#addr-BD51">BD51</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Set bit 2: head load delay</span></td> </tr> <tr id="addr-BD53"> <td class="addr"><a href="#addr-BD53">BD53</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry (was set by SEC)</span></td> </tr> <tr id="addr-BD54"> <td class="addr"><a href="#addr-BD54">BD54</a></td> <td><span class="label">.restore_head_flag<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BD4F">← BD4F BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Restore head-loaded flag</span></td> </tr> <tr id="addr-BD57"> <td class="addr"><a href="#addr-BD57">BD57</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BD58"> <td colspan="2"><div class="sub-header"> <h3>Format a floppy disc track</h3> <div class="sub-desc"><p>Set up NMI handler addresses for a format operation, then write the track format data to disc.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BD58">BD58</a></td> <td><span class="label">.floppy_format_track<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB3C">← BB3C JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E2">wksp_format_page</span></span> <span class="comment">; Get format page number</span></td> </tr> <tr id="addr-BD5B"> <td class="addr"><a href="#addr-BD5B">BD5B</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0F">nmi_read_addr_hi</span></span> <span class="comment">; Store as NMI buffer high byte</span></td> </tr> <tr id="addr-BD5E"> <td class="addr"><a href="#addr-BD5E">BD5E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: NMI buffer low byte</span></td> </tr> <tr id="addr-BD60"> <td class="addr"><a href="#addr-BD60">BD60</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D0E">nmi_read_addr_lo</span></span> <span class="comment">; Store as NMI buffer low byte</span></td> </tr> <tr id="addr-BD63"> <td class="addr"><a href="#addr-BD63">BD63</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BAC6" data-tip="&BAC6 – Set up FDC registers and seek to track">setup_fdc_and_seek</a></span> <span class="comment">; Set up FDC registers for operation</span></td> </tr> <tr id="addr-BD66"> <td class="addr"><a href="#addr-BD66">BD66</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BCFD" data-tip="&BCFD – Select and issue FDC read/write command">select_fdc_rw_command</a></span> <span class="comment">; Set up FDC command and issue</span></td> </tr> <tr id="addr-BD69"> <td class="addr"><a href="#addr-BD69">BD69</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Save current track</span></td> </tr> <tr id="addr-BD6B"> <td class="addr"><a href="#addr-BD6B">BD6B</a></td> <td> <span class="opcode">PHA</span> <span class="comment">; Push on stack</span></td> </tr> <tr id="addr-BD6C"> <td class="addr"><a href="#addr-BD6C">BD6C</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1016">wksp_disc_op_mem_addr</span></span> <span class="comment">; Get transfer address low</span></td> </tr> <tr id="addr-BD6F"> <td class="addr"><a href="#addr-BD6F">BD6F</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span></span> <span class="comment">; Store as dest address low</span></td> </tr> <tr id="addr-BD71"> <td class="addr"><a href="#addr-BD71">BD71</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&1017">wksp_disc_op_mem_addr_1</span></span> <span class="comment">; Get transfer address high</span></td> </tr> <tr id="addr-BD74"> <td class="addr"><a href="#addr-BD74">BD74</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Store as dest address high</span></td> </tr> <tr id="addr-BD76"> <td class="addr"><a href="#addr-BD76">BD76</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Source address low = 0</span></td> </tr> <tr id="addr-BD78"> <td class="addr"><a href="#addr-BD78">BD78</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store source low</span></td> </tr> <tr id="addr-BD7A"> <td class="addr"><a href="#addr-BD7A">BD7A</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E2">wksp_format_page</span></span> <span class="comment">; Get format buffer page</span></td> </tr> <tr id="addr-BD7D"> <td class="addr"><a href="#addr-BD7D">BD7D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; Store source high (format data page)</span></td> </tr> <tr id="addr-BD7F"> <td class="addr"><a href="#addr-BD7F">BD7F</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00CD">zp_adfs_flags</span></span> <span class="comment">; Is Tube active?</span></td> </tr> <tr id="addr-BD81"> <td class="addr"><a href="#addr-BD81">BD81</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-BD97" data-tip="&BD97">direct_format_copy</a></span> <span class="comment">; No Tube: use direct memory copy</span></td> </tr> <tr id="addr-BD83"> <td class="addr"><a href="#addr-BD83">BD83</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Y=0: Tube transfer byte index</span></td> </tr> <tr id="addr-BD85"> <td class="addr"><a href="#addr-BD85">BD85</a></td> <td><span class="label">.tube_format_xfer_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BD93">← BD93 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00A3">zp_floppy_track</span>),y</span> <span class="comment">; Get format data byte from source</span></td> </tr> <tr id="addr-BD87"> <td class="addr"><a href="#addr-BD87">BD87</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; X=7: timing delay loop</span></td> </tr> <tr id="addr-BD89"> <td class="addr"><a href="#addr-BD89">BD89</a></td> <td><span class="label">.tube_format_delay_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BD8A">← BD8A BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEX</span> <span class="comment">; Delay</span></td> </tr> <tr id="addr-BD8A"> <td class="addr"><a href="#addr-BD8A">BD8A</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BD89" data-tip="&BD89">tube_format_delay_loop</a></span> <span class="comment">; Loop for delay</span></td> </tr> <tr id="addr-BD8C"> <td class="addr"><a href="#addr-BD8C">BD8C</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FEE5">tube_data_register_3</span></span> <span class="comment">; Send byte to Tube R3</span></td> </tr> <tr id="addr-BD8F"> <td class="addr"><a href="#addr-BD8F">BD8F</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Next byte</span></td> </tr> <tr id="addr-BD90"> <td class="addr"><a href="#addr-BD90">BD90</a></td> <td> <span class="opcode">CPY</span> <span class="operand"><span class="ext-label" data-tip="&101E">wksp_disc_op_sector_count</span></span> <span class="comment">; Transferred all bytes?</span></td> </tr> <tr id="addr-BD93"> <td class="addr"><a href="#addr-BD93">BD93</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BD85" data-tip="&BD85">tube_format_xfer_loop</a></span> <span class="comment">; No, continue transfer</span></td> </tr> <tr id="addr-BD95"> <td class="addr"><a href="#addr-BD95">BD95</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BDA2" data-tip="&BDA2">format_track_data_ready</a></span></td> </tr> <tr id="addr-BD97"> <td class="addr"><a href="#addr-BD97">BD97</a></td> <td><span class="label">.direct_format_copy<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BD81">← BD81 BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&101E">wksp_disc_op_sector_count</span></span> <span class="comment">; Direct copy: get byte count</span></td> </tr> <tr id="addr-BD9A"> <td class="addr"><a href="#addr-BD9A">BD9A</a></td> <td><span class="label">.direct_format_copy_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BDA0">← BDA0 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEY</span> <span class="comment">; Adjust for 0-based index</span></td> </tr> <tr id="addr-BD9B"> <td class="addr"><a href="#addr-BD9B">BD9B</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00A3">zp_floppy_track</span>),y</span> <span class="comment">; Get last byte from source</span></td> </tr> <tr id="addr-BD9D"> <td class="addr"><a href="#addr-BD9D">BD9D</a></td> <td> <span class="opcode">STA</span> <span class="operand">(<span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span>),y</span> <span class="comment">; Store at dest</span></td> </tr> <tr id="addr-BD9F"> <td class="addr"><a href="#addr-BD9F">BD9F</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Transfer Y to A</span></td> </tr> <tr id="addr-BDA0"> <td class="addr"><a href="#addr-BDA0">BDA0</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BD9A" data-tip="&BD9A">direct_format_copy_loop</a></span> <span class="comment">; Loop until all bytes copied</span></td> </tr> <tr id="addr-BDA2"> <td class="addr"><a href="#addr-BDA2">BDA2</a></td> <td><span class="label">.format_track_data_ready<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BD95">← BD95 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">PLA</span> <span class="comment">; Restore saved track</span></td> </tr> <tr id="addr-BDA3"> <td class="addr"><a href="#addr-BDA3">BDA3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store back as current track</span></td> </tr> <tr id="addr-BDA5"> <td class="addr"><a href="#addr-BDA5">BDA5</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BDA6"> <td class="addr"><a href="#addr-BDA6">BDA6</a></td> <td><span class="label">.issue_fdc_track_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB20">← BB20 JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BAC6" data-tip="&BAC6 – Set up FDC registers and seek to track">setup_fdc_and_seek</a></span> <span class="comment">; Set up FDC registers</span></td> </tr> <tr id="addr-BDA9"> <td class="addr"><a href="#addr-BDA9">BDA9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Get transfer state flags</span></td> </tr> <tr id="addr-BDAB"> <td class="addr"><a href="#addr-BDAB">BDAB</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; Set bit 6 (multi-sector flag)</span></td> </tr> <tr id="addr-BDAD"> <td class="addr"><a href="#addr-BDAD">BDAD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Store updated state</span></td> </tr> <tr id="addr-BDAF"> <td class="addr"><a href="#addr-BDAF">BDAF</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; Y=7: get sector address from block</span></td> </tr> <tr id="addr-BDB1"> <td class="addr"><a href="#addr-BDB1">BDB1</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector address mid byte</span></td> </tr> <tr id="addr-BDB3"> <td class="addr"><a href="#addr-BDB3">BDB3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Store in NMI workspace</span></td> </tr> <tr id="addr-BDB6"> <td class="addr"><a href="#addr-BDB6">BDB6</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=8: sector address low</span></td> </tr> <tr id="addr-BDB7"> <td class="addr"><a href="#addr-BDB7">BDB7</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector address low</span></td> </tr> <tr id="addr-BDB9"> <td class="addr"><a href="#addr-BDB9">BDB9</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=9: sector count</span></td> </tr> <tr id="addr-BDBA"> <td class="addr"><a href="#addr-BDBA">BDBA</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-BDBB"> <td class="addr"><a href="#addr-BDBB">BDBB</a></td> <td> <span class="opcode">ADC</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Add sector count to start sector</span></td> </tr> <tr id="addr-BDBD"> <td class="addr"><a href="#addr-BDBD">BDBD</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D59">nmi_secs_last_track</span></span> <span class="comment">; Store end sector in NMI workspace</span></td> </tr> <tr id="addr-BDC0"> <td class="addr"><a href="#addr-BDC0">BDC0</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BDC5" data-tip="&BDC5">wait_format_track_complete</a></span> <span class="comment">; No carry: no wrap</span></td> </tr> <tr id="addr-BDC2"> <td class="addr"><a href="#addr-BDC2">BDC2</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Increment mid byte on carry</span></td> </tr> <tr id="addr-BDC5"> <td class="addr"><a href="#addr-BDC5">BDC5</a></td> <td><span class="label">.wait_format_track_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BDC0">← BDC0 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Get end sector mid byte</span></td> </tr> <tr id="addr-BDC8"> <td class="addr"><a href="#addr-BDC8">BDC8</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer to X</span></td> </tr> <tr id="addr-BDC9"> <td class="addr"><a href="#addr-BDC9">BDC9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D59">nmi_secs_last_track</span></span> <span class="comment">; Get end sector low byte</span></td> </tr> <tr id="addr-BDCC"> <td class="addr"><a href="#addr-BDCC">BDCC</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: init for divide</span></td> </tr> <tr id="addr-BDCE"> <td class="addr"><a href="#addr-BDCE">BDCE</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BFA2" data-tip="&BFA2 – Divide X:A by 16, result in Y:A">xa_div_16_to_ya</a></span> <span class="comment">; Divide end sector by 16</span></td> </tr> <tr id="addr-BDD1"> <td class="addr"><a href="#addr-BDD1">BDD1</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Remainder = 0?</span></td> </tr> <tr id="addr-BDD3"> <td class="addr"><a href="#addr-BDD3">BDD3</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BDD7" data-tip="&BDD7">format_next_track</a></span> <span class="comment">; No: adjust sectors per track</span></td> </tr> <tr id="addr-BDD5"> <td class="addr"><a href="#addr-BDD5">BDD5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Yes: use full 16 sectors/track</span></td> </tr> <tr id="addr-BDD7"> <td class="addr"><a href="#addr-BDD7">BDD7</a></td> <td><span class="label">.format_next_track<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BDD3">← BDD3 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: get sector count from block</span></td> </tr> <tr id="addr-BDD9"> <td class="addr"><a href="#addr-BDD9">BDD9</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-BDDA"> <td class="addr"><a href="#addr-BDDA">BDDA</a></td> <td> <span class="opcode">SBC</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Subtract sector count</span></td> </tr> <tr id="addr-BDDC"> <td class="addr"><a href="#addr-BDDC">BDDC</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-BDFB" data-tip="&BDFB">format_double_sided</a></span> <span class="comment">; Result >= 0: fits in remaining</span></td> </tr> <tr id="addr-BDDE"> <td class="addr"><a href="#addr-BDDE">BDDE</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Need to cross track boundary</span></td> </tr> <tr id="addr-BDE0"> <td class="addr"><a href="#addr-BDE0">BDE0</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-BDE1"> <td class="addr"><a href="#addr-BDE1">BDE1</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; Subtract start sector position</span></td> </tr> <tr id="addr-BDE3"> <td class="addr"><a href="#addr-BDE3">BDE3</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Store sectors remaining this track</span></td> </tr> <tr id="addr-BDE6"> <td class="addr"><a href="#addr-BDE6">BDE6</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector count from block</span></td> </tr> <tr id="addr-BDE8"> <td class="addr"><a href="#addr-BDE8">BDE8</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-BDE9"> <td class="addr"><a href="#addr-BDE9">BDE9</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Subtract sectors done this track</span></td> </tr> <tr id="addr-BDEC"> <td class="addr"><a href="#addr-BDEC">BDEC</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: init result</span></td> </tr> <tr id="addr-BDEE"> <td class="addr"><a href="#addr-BDEE">BDEE</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: init for divide</span></td> </tr> <tr id="addr-BDF0"> <td class="addr"><a href="#addr-BDF0">BDF0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BFA2" data-tip="&BFA2 – Divide X:A by 16, result in Y:A">xa_div_16_to_ya</a></span> <span class="comment">; Divide remaining by 16</span></td> </tr> <tr id="addr-BDF3"> <td class="addr"><a href="#addr-BDF3">BDF3</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&0D57">nmi_tracks_remaining</span></span> <span class="comment">; Store full tracks to process</span></td> </tr> <tr id="addr-BDF6"> <td class="addr"><a href="#addr-BDF6">BDF6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D59">nmi_secs_last_track</span></span> <span class="comment">; Store partial sectors on last track</span></td> </tr> <tr id="addr-BDF9"> <td class="addr"><a href="#addr-BDF9">BDF9</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BE0C" data-tip="&BE0C">set_format_sector_id</a></span> <span class="comment">; Branch always (positive)</span></td> </tr> <tr id="addr-BDFB"> <td class="addr"><a href="#addr-BDFB">BDFB</a></td> <td><span class="label">.format_double_sided<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BDDC">← BDDC BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: get sector count</span></td> </tr> <tr id="addr-BDFD"> <td class="addr"><a href="#addr-BDFD">BDFD</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector count from block</span></td> </tr> <tr id="addr-BDFF"> <td class="addr"><a href="#addr-BDFF">BDFF</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Store in NMI workspace</span></td> </tr> <tr id="addr-BE02"> <td class="addr"><a href="#addr-BE02">BE02</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; A=&FF: no additional tracks</span></td> </tr> <tr id="addr-BE04"> <td class="addr"><a href="#addr-BE04">BE04</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D57">nmi_tracks_remaining</span></span> <span class="comment">; Store track count</span></td> </tr> <tr id="addr-BE07"> <td class="addr"><a href="#addr-BE07">BE07</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; A=0: no partial sectors</span></td> </tr> <tr id="addr-BE09"> <td class="addr"><a href="#addr-BE09">BE09</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D59">nmi_secs_last_track</span></span> <span class="comment">; Store partial count</span></td> </tr> <tr id="addr-BE0C"> <td class="addr"><a href="#addr-BE0C">BE0C</a></td> <td><span class="label">.set_format_sector_id<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BDF9">← BDF9 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Clear sector position counter</span></td> </tr> <tr id="addr-BE0E"> <td class="addr"><a href="#addr-BE0E">BE0E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5A">nmi_sec_position</span></span> <span class="comment">; Store in NMI workspace</span></td> </tr> <tr id="addr-BE11"> <td class="addr"><a href="#addr-BE11">BE11</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&0D57">nmi_tracks_remaining</span></span> <span class="comment">; Increment full track count</span></td> </tr> <tr id="addr-BE14"> <td class="addr"><a href="#addr-BE14">BE14</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Decrement sectors this track</span></td> </tr> <tr id="addr-BE17"> <td class="addr"><a href="#addr-BE17">BE17</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; X=1: write sector register</span></td> </tr> <tr id="addr-BE19"> <td class="addr"><a href="#addr-BE19">BE19</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BB09" data-tip="&BB09 – Write to WD1770 register with readback verify">fdc_write_register_verify</a></span> <span class="comment">; Write sector to FDC with verify</span></td> </tr> <tr id="addr-BE1C"> <td class="addr"><a href="#addr-BE1C">BE1C</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check read/write direction</span></td> </tr> <tr id="addr-BE1E"> <td class="addr"><a href="#addr-BE1E">BE1E</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BE27" data-tip="&BE27">check_format_complete</a></span> <span class="comment">; Reading: use read command</span></td> </tr> <tr id="addr-BE20"> <td class="addr"><a href="#addr-BE20">BE20</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="160 &A0 %10100000">#&a0</span></span> <span class="comment">; A=&A0: write command base</span></td> </tr> <tr id="addr-BE22"> <td class="addr"><a href="#addr-BE22">BE22</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0D56">nmi_step_rate</span></span> <span class="comment">; OR in step rate</span></td> </tr> <tr id="addr-BE25"> <td class="addr"><a href="#addr-BE25">BE25</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BE29" data-tip="&BE29">format_track_loop</a></span> <span class="comment">; Branch (always non-zero)</span></td> </tr> <tr id="addr-BE27"> <td class="addr"><a href="#addr-BE27">BE27</a></td> <td><span class="label">.check_format_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE1E">← BE1E BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="128 &80 %10000000">#&80</span></span> <span class="comment">; A=&80: read command base</span></td> </tr> <tr id="addr-BE29"> <td class="addr"><a href="#addr-BE29">BE29</a></td> <td><span class="label">.format_track_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE25">← BE25 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Store FDC command in workspace</span></td> </tr> <tr id="addr-BE2B"> <td class="addr"><a href="#addr-BE2B">BE2B</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD2B" data-tip="&BD2B – Clear floppy transfer complete flag">clear_transfer_complete</a></span> <span class="comment">; Clear seek flag</span></td> </tr> <tr id="addr-BE2E"> <td class="addr"><a href="#addr-BE2E">BE2E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Get FDC command</span></td> </tr> <tr id="addr-BE30"> <td class="addr"><a href="#addr-BE30">BE30</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue command to FDC</span></td> </tr> <tr id="addr-BE33"> <td class="addr"><a href="#addr-BE33">BE33</a></td> <td><span class="label">.wait_format_nmi_complete<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BE4C">← BE4C BNE</a><a href="#addr-BE67">← BE67 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BCC2" data-tip="&BCC2 – Wait for floppy NMI transfer to complete">floppy_wait_nmi_finish</a></span> <span class="comment">; Wait for NMI completion</span></td> </tr> <tr id="addr-BE36"> <td class="addr"><a href="#addr-BE36">BE36</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Get transfer state</span></td> </tr> <tr id="addr-BE38"> <td class="addr"><a href="#addr-BE38">BE38</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Bit 1 set: need track step</span></td> </tr> <tr id="addr-BE3A"> <td class="addr"><a href="#addr-BE3A">BE3A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BE4E" data-tip="&BE4E">format_verify_pass</a></span> <span class="comment">; No step needed: check side switch</span></td> </tr> <tr id="addr-BE3C"> <td class="addr"><a href="#addr-BE3C">BE3C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD2B" data-tip="&BD2B – Clear floppy transfer complete flag">clear_transfer_complete</a></span> <span class="comment">; Clear seek flag</span></td> </tr> <tr id="addr-BE3F"> <td class="addr"><a href="#addr-BE3F">BE3F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD38" data-tip="&BD38 – Clear floppy seek-in-progress flag">clear_seek_flag</a></span> <span class="comment">; Clear track-step flag</span></td> </tr> <tr id="addr-BE42"> <td class="addr"><a href="#addr-BE42">BE42</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="84 &54 %01010100 'T'">#&54</span></span> <span class="comment">; FDC step-in command (&54)</span></td> </tr> <tr id="addr-BE44"> <td class="addr"><a href="#addr-BE44">BE44</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0D5C">nmi_drive_cmd</span></span> <span class="comment">; OR in drive select bits</span></td> </tr> <tr id="addr-BE47"> <td class="addr"><a href="#addr-BE47">BE47</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue step-in command</span></td> </tr> <tr id="addr-BE4A"> <td class="addr"><a href="#addr-BE4A">BE4A</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Increment current track</span></td> </tr> <tr id="addr-BE4C"> <td class="addr"><a href="#addr-BE4C">BE4C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BE33" data-tip="&BE33">wait_format_nmi_complete</a></span> <span class="comment">; Continue multi-sector loop</span></td> </tr> <tr id="addr-BE4E"> <td class="addr"><a href="#addr-BE4E">BE4E</a></td> <td><span class="label">.format_verify_pass<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE3A">← BE3A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Check bit 3: side switch needed?</span></td> </tr> <tr id="addr-BE50"> <td class="addr"><a href="#addr-BE50">BE50</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; Check if set</span></td> </tr> <tr id="addr-BE52"> <td class="addr"><a href="#addr-BE52">BE52</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BE83" data-tip="&BE83">return_46</a></span> <span class="comment">; Not set: operation complete</span></td> </tr> <tr id="addr-BE54"> <td class="addr"><a href="#addr-BE54">BE54</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD2B" data-tip="&BD2B – Clear floppy transfer complete flag">clear_transfer_complete</a></span> <span class="comment">; Clear seek flag for side switch</span></td> </tr> <tr id="addr-BE57"> <td class="addr"><a href="#addr-BE57">BE57</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD31" data-tip="&BD31">clear_side_flag</a></span> <span class="comment">; Clear side-switch flag</span></td> </tr> <tr id="addr-BE5A"> <td class="addr"><a href="#addr-BE5A">BE5A</a></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Increment track for side 1</span></td> </tr> <tr id="addr-BE5C"> <td class="addr"><a href="#addr-BE5C">BE5C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD22" data-tip="&BD22 – Select floppy disc side 1">floppy_set_side_1</a></span> <span class="comment">; Select side 1</span></td> </tr> <tr id="addr-BE5F"> <td class="addr"><a href="#addr-BE5F">BE5F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; FDC restore command (seek to trk 0)</span></td> </tr> <tr id="addr-BE61"> <td class="addr"><a href="#addr-BE61">BE61</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&0D5C">nmi_drive_cmd</span></span> <span class="comment">; OR in drive select</span></td> </tr> <tr id="addr-BE64"> <td class="addr"><a href="#addr-BE64">BE64</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue restore command</span></td> </tr> <tr id="addr-BE67"> <td class="addr"><a href="#addr-BE67">BE67</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BE33" data-tip="&BE33">wait_format_nmi_complete</a></span> <span class="comment">; Continue loop (always branches)</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BE69"> <td colspan="2"><div class="sub-header"> <h3>Advance multi-sector transfer to next sector</h3> <div class="sub-desc"><p>Called from the NMI end-of-operation handler during multi-sector transfers. Clears the transfer-complete flag, then calls the FDC seek routine to check whether a track boundary has been crossed and step the head if needed. If the seek routine returns zero, all sectors have been transferred and the completion flag is set.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BE69">BE69</a></td> <td><span class="label">.floppy_next_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-0D3C">← 0D3C JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD2B" data-tip="&BD2B – Clear floppy transfer complete flag">clear_transfer_complete</a></span> <span class="comment">; Clear seek flag</span></td> </tr> <tr id="addr-BE6C"> <td class="addr"><a href="#addr-BE6C">BE6C</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BE84" data-tip="&BE84">execute_fdc_seek</a></span> <span class="comment">; Check for next track boundary</span></td> </tr> <tr id="addr-BE6F"> <td class="addr"><a href="#addr-BE6F">BE6F</a></td> <td> <span class="opcode">TXA</span> <span class="comment">; Transfer result to A</span></td> </tr> <tr id="addr-BE70"> <td class="addr"><a href="#addr-BE70">BE70</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BE78" data-tip="&BE78">clear_verify_seek_flag</a></span> <span class="comment">; Non-zero: more sectors to transfer</span></td> </tr> <tr id="addr-BE72"> <td class="addr"><a href="#addr-BE72">BE72</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Set completion flag bit 0</span></td> </tr> <tr id="addr-BE74"> <td class="addr"><a href="#addr-BE74">BE74</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-BE75"> <td class="addr"><a href="#addr-BE75">BE75</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Store completion flag</span></td> </tr> <tr id="addr-BE77"> <td class="addr"><a href="#addr-BE77">BE77</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return (operation complete)</span></td> </tr> <tr id="addr-BE78"> <td class="addr"><a href="#addr-BE78">BE78</a></td> <td><span class="label">.clear_verify_seek_flag<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE70">← BE70 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD38" data-tip="&BD38 – Clear floppy seek-in-progress flag">clear_seek_flag</a></span> <span class="comment">; Clear track-step flag</span></td> </tr> <tr id="addr-BE7B"> <td class="addr"><a href="#addr-BE7B">BE7B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Get FDC command</span></td> </tr> <tr id="addr-BE7D"> <td class="addr"><a href="#addr-BE7D">BE7D</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD4C" data-tip="&BD4C – Apply head load delay to FDC command">apply_head_load_flag</a></span> <span class="comment">; Apply head load delay</span></td> </tr> <tr id="addr-BE80"> <td class="addr"><a href="#addr-BE80">BE80</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE84">fdc_1770_command_or_status</span></span> <span class="comment">; Issue FDC command</span></td> </tr> <tr id="addr-BE83"> <td class="addr"><a href="#addr-BE83">BE83</a></td> <td><span class="label">.return_46<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE52">← BE52 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BE84"> <td class="addr"><a href="#addr-BE84">BE84</a></td> <td><span class="label">.execute_fdc_seek<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE6C">← BE6C JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Get sectors remaining this track</span></td> </tr> <tr id="addr-BE87"> <td class="addr"><a href="#addr-BE87">BE87</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BEED" data-tip="&BEED">issue_step_command</a></span> <span class="comment">; Non-zero: not at boundary</span></td> </tr> <tr id="addr-BE89"> <td class="addr"><a href="#addr-BE89">BE89</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D57">nmi_tracks_remaining</span></span> <span class="comment">; Get full tracks remaining</span></td> </tr> <tr id="addr-BE8C"> <td class="addr"><a href="#addr-BE8C">BE8C</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BE9D" data-tip="&BE9D">check_seek_error</a></span> <span class="comment">; Non-zero: need track step</span></td> </tr> <tr id="addr-BE8E"> <td class="addr"><a href="#addr-BE8E">BE8E</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D59">nmi_secs_last_track</span></span> <span class="comment">; Get partial sectors on last track</span></td> </tr> <tr id="addr-BE91"> <td class="addr"><a href="#addr-BE91">BE91</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BE97" data-tip="&BE97">wait_seek_complete</a></span> <span class="comment">; Non-zero: still have partial track</span></td> </tr> <tr id="addr-BE93"> <td class="addr"><a href="#addr-BE93">BE93</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: all done</span></td> </tr> <tr id="addr-BE95"> <td class="addr"><a href="#addr-BE95">BE95</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BEFE" data-tip="&BEFE">return_47</a></span> <span class="comment">; Branch to return</span></td> </tr> <tr id="addr-BE97"> <td class="addr"><a href="#addr-BE97">BE97</a></td> <td><span class="label">.wait_seek_complete<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE91">← BE91 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&0D59">nmi_secs_last_track</span></span> <span class="comment">; Decrement partial sector count</span></td> </tr> <tr id="addr-BE9A"> <td class="addr"><a href="#addr-BE9A">BE9A</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BEF0" data-tip="&BEF0">step_track_counter</a></span> <span class="comment">; Jump to update sector position</span></td> </tr> <tr id="addr-BE9D"> <td class="addr"><a href="#addr-BE9D">BE9D</a></td> <td><span class="label">.check_seek_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE8C">← BE8C BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5A">nmi_sec_position</span></span> <span class="comment">; Get sector position counter</span></td> </tr> <tr id="addr-BEA0"> <td class="addr"><a href="#addr-BEA0">BEA0</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BEE7" data-tip="&BEE7">step_inward</a></span> <span class="comment">; Non-zero: continue processing</span></td> </tr> <tr id="addr-BEA2"> <td class="addr"><a href="#addr-BEA2">BEA2</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Set head-loaded flag</span></td> </tr> <tr id="addr-BEA5"> <td class="addr"><a href="#addr-BEA5">BEA5</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-BEA6"> <td class="addr"><a href="#addr-BEA6">BEA6</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Restore head-loaded flag</span></td> </tr> <tr id="addr-BEA9"> <td class="addr"><a href="#addr-BEA9">BEA9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&FE85">fdc_1770_track</span></span> <span class="comment">; Read current track from FDC</span></td> </tr> <tr id="addr-BEAC"> <td class="addr"><a href="#addr-BEAC">BEAC</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="79 &4F %01001111 'O'">#&4f</span></span> <span class="comment">; Track >= 79 (&4F)?</span></td> </tr> <tr id="addr-BEAE"> <td class="addr"><a href="#addr-BEAE">BEAE</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BECF" data-tip="&BECF">begin_step_sequence</a></span> <span class="comment">; No: normal track step</span></td> </tr> <tr id="addr-BEB0"> <td class="addr"><a href="#addr-BEB0">BEB0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Get NMI control byte</span></td> </tr> <tr id="addr-BEB3"> <td class="addr"><a href="#addr-BEB3">BEB3</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="4 &04 %00000100 EOT">#4</span></span> <span class="comment">; Bit 2 set (double-sided)?</span></td> </tr> <tr id="addr-BEB5"> <td class="addr"><a href="#addr-BEB5">BEB5</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BEBC" data-tip="&BEBC">seek_with_stepping</a></span> <span class="comment">; Not set: single-sided disc</span></td> </tr> <tr id="addr-BEB7"> <td class="addr"><a href="#addr-BEB7">BEB7</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: operation ending</span></td> </tr> <tr id="addr-BEB9"> <td class="addr"><a href="#addr-BEB9">BEB9</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BEF2" data-tip="&BEF2">steps_remaining_check</a></span> <span class="comment">; Jump to track position update</span></td> </tr> <tr id="addr-BEBC"> <td class="addr"><a href="#addr-BEBC">BEBC</a></td> <td><span class="label">.seek_with_stepping<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BEB5">← BEB5 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Track &4F: switch to side 1</span></td> </tr> <tr id="addr-BEBE"> <td class="addr"><a href="#addr-BEBE">BEBE</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Set track to &FF (will be 0 after inc)</span></td> </tr> <tr id="addr-BEC0"> <td class="addr"><a href="#addr-BEC0">BEC0</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD22" data-tip="&BD22 – Select floppy disc side 1">floppy_set_side_1</a></span> <span class="comment">; Select side 1</span></td> </tr> <tr id="addr-BEC3"> <td class="addr"><a href="#addr-BEC3">BEC3</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Get NMI drive control byte</span></td> </tr> <tr id="addr-BEC6"> <td class="addr"><a href="#addr-BEC6">BEC6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE80">fdc_1770_drive_control</span></span> <span class="comment">; Write to FDC control register</span></td> </tr> <tr id="addr-BEC9"> <td class="addr"><a href="#addr-BEC9">BEC9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Get transfer state</span></td> </tr> <tr id="addr-BECB"> <td class="addr"><a href="#addr-BECB">BECB</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="8 &08 %00001000 BS">#8</span></span> <span class="comment">; Set bit 3 (side switch flag)</span></td> </tr> <tr id="addr-BECD"> <td class="addr"><a href="#addr-BECD">BECD</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BED3" data-tip="&BED3">check_step_direction</a></span> <span class="comment">; Branch (always non-zero)</span></td> </tr> <tr id="addr-BECF"> <td class="addr"><a href="#addr-BECF">BECF</a></td> <td><span class="label">.begin_step_sequence<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BEAE">← BEAE BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Get transfer state</span></td> </tr> <tr id="addr-BED1"> <td class="addr"><a href="#addr-BED1">BED1</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="2 &02 %00000010 STX">#2</span></span> <span class="comment">; Set bit 1 (track step flag)</span></td> </tr> <tr id="addr-BED3"> <td class="addr"><a href="#addr-BED3">BED3</a></td> <td><span class="label">.check_step_direction<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BECD">← BECD BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A2">zp_floppy_state</span></span> <span class="comment">; Store updated state</span></td> </tr> <tr id="addr-BED5"> <td class="addr"><a href="#addr-BED5">BED5</a></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&0D57">nmi_tracks_remaining</span></span> <span class="comment">; Decrement full track count</span></td> </tr> <tr id="addr-BED8"> <td class="addr"><a href="#addr-BED8">BED8</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BEDF" data-tip="&BEDF">step_outward</a></span> <span class="comment">; Zero: check for partial track</span></td> </tr> <tr id="addr-BEDA"> <td class="addr"><a href="#addr-BEDA">BEDA</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Sectors per track = &10 (16)</span></td> </tr> <tr id="addr-BEDC"> <td class="addr"><a href="#addr-BEDC">BEDC</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5A">nmi_sec_position</span></span> <span class="comment">; Store in sector counter</span></td> </tr> <tr id="addr-BEDF"> <td class="addr"><a href="#addr-BEDF">BEDF</a></td> <td><span class="label">.step_outward<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BED8">← BED8 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="254 &FE %11111110">#&fe</span></span> <span class="comment">; A=&FE: sector position reset</span></td> </tr> <tr id="addr-BEE1"> <td class="addr"><a href="#addr-BEE1">BEE1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; Store sector position</span></td> </tr> <tr id="addr-BEE3"> <td class="addr"><a href="#addr-BEE3">BEE3</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; X=0: continue processing</span></td> </tr> <tr id="addr-BEE5"> <td class="addr"><a href="#addr-BEE5">BEE5</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BEF2" data-tip="&BEF2">steps_remaining_check</a></span> <span class="comment">; Branch to update (always)</span></td> </tr> <tr id="addr-BEE7"> <td class="addr"><a href="#addr-BEE7">BEE7</a></td> <td><span class="label">.step_inward<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BEA0">← BEA0 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&0D5A">nmi_sec_position</span></span> <span class="comment">; Decrement sector position counter</span></td> </tr> <tr id="addr-BEEA"> <td class="addr"><a href="#addr-BEEA">BEEA</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BEF0" data-tip="&BEF0">step_track_counter</a></span> <span class="comment">; Jump to update sector position</span></td> </tr> <tr id="addr-BEED"> <td class="addr"><a href="#addr-BEED">BEED</a></td> <td><span class="label">.issue_step_command<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE87">← BE87 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">DEC</span> <span class="operand"><span class="ext-label" data-tip="&0D58">nmi_secs_this_track</span></span> <span class="comment">; Decrement sectors this track</span></td> </tr> <tr id="addr-BEF0"> <td class="addr"><a href="#addr-BEF0">BEF0</a></td> <td><span class="label">.step_track_counter<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BE9A">← BE9A JMP</a><a href="#addr-BEEA">← BEEA JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; X=&FF: more sectors to do</span></td> </tr> <tr id="addr-BEF2"> <td class="addr"><a href="#addr-BEF2">BEF2</a></td> <td><span class="label">.steps_remaining_check<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BEB9">← BEB9 JMP</a><a href="#addr-BEE5">← BEE5 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">INC</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; Increment sector position</span></td> </tr> <tr id="addr-BEF4"> <td class="addr"><a href="#addr-BEF4">BEF4</a></td> <td><span class="label">.step_loop<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BEFC">← BEFC BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; Get current sector position</span></td> </tr> <tr id="addr-BEF6"> <td class="addr"><a href="#addr-BEF6">BEF6</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE86">fdc_1770_sector</span></span> <span class="comment">; Write to FDC sector register</span></td> </tr> <tr id="addr-BEF9"> <td class="addr"><a href="#addr-BEF9">BEF9</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="ext-label" data-tip="&FE86">fdc_1770_sector</span></span> <span class="comment">; Read back to verify</span></td> </tr> <tr id="addr-BEFC"> <td class="addr"><a href="#addr-BEFC">BEFC</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BEF4" data-tip="&BEF4">step_loop</a></span> <span class="comment">; Loop until value sticks</span></td> </tr> <tr id="addr-BEFE"> <td class="addr"><a href="#addr-BEFE">BEFE</a></td> <td><span class="label">.return_47<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BE95">← BE95 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr id="addr-BEFF"> <td class="addr"><a href="#addr-BEFF">BEFF</a></td> <td><span class="label">.setup_track_for_rw<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BB8F">← BB8F JMP</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="6 &06 %00000110 ACK">#6</span></span> <span class="comment">; Y=6: get drive+sector from block</span></td> </tr> <tr id="addr-BF01"> <td class="addr"><a href="#addr-BF01">BF01</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get drive+sector byte</span></td> </tr> <tr id="addr-BF03"> <td class="addr"><a href="#addr-BF03">BF03</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="ext-label" data-tip="&1117">wksp_current_drive</span></span> <span class="comment">; OR with current drive</span></td> </tr> <tr id="addr-BF06"> <td class="addr"><a href="#addr-BF06">BF06</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Store as drive control byte</span></td> </tr> <tr id="addr-BF08"> <td class="addr"><a href="#addr-BF08">BF08</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="31 &1F %00011111 US">#&1f</span></span> <span class="comment">; Isolate drive number bits</span></td> </tr> <tr id="addr-BF0A"> <td class="addr"><a href="#addr-BF0A">BF0A</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BF0F" data-tip="&BF0F">get_sector_from_block</a></span> <span class="comment">; Drive 0? OK</span></td> </tr> <tr id="addr-BF0C"> <td class="addr"><a href="#addr-BF0C">BF0C</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BF66" data-tip="&BF66">bad_address_error</a></span> <span class="comment">; Non-zero: bad drive error</span></td> </tr> <tr id="addr-BF0F"> <td class="addr"><a href="#addr-BF0F">BF0F</a></td> <td><span class="label">.get_sector_from_block<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF0A">← BF0A BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Check drive select bits</span></td> </tr> <tr id="addr-BF11"> <td class="addr"><a href="#addr-BF11">BF11</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-BF19" data-tip="&BF19">adjust_for_partial_sector</a></span> <span class="comment">; Bit 6: invalid drive?</span></td> </tr> <tr id="addr-BF13"> <td class="addr"><a href="#addr-BF13">BF13</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="101 &65 %01100101 'e'">#&65</span></span> <span class="comment">; Error &65: volume error (bad drive)</span></td> </tr> <tr id="addr-BF15"> <td class="addr"><a href="#addr-BF15">BF15</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Store error code</span></td> </tr> <tr id="addr-BF17"> <td class="addr"><a href="#addr-BF17">BF17</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BF6A" data-tip="&BF6A">branch_to_floppy_error</a></span> <span class="comment">; Branch to floppy error</span></td> </tr> <tr id="addr-BF19"> <td class="addr"><a href="#addr-BF19">BF19</a></td> <td><span class="label">.adjust_for_partial_sector<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF11">← BF11 BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A6">zp_floppy_dest_page</span></span> <span class="comment">; Get drive control byte</span></td> </tr> <tr id="addr-BF1B"> <td class="addr"><a href="#addr-BF1B">BF1B</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Check bit 5 (drive 1 select)</span></td> </tr> <tr id="addr-BF1D"> <td class="addr"><a href="#addr-BF1D">BF1D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BF23" data-tip="&BF23">check_sectors_remaining</a></span> <span class="comment">; Not set: drive 0, use &21</span></td> </tr> <tr id="addr-BF1F"> <td class="addr"><a href="#addr-BF1F">BF1F</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="33 &21 %00100001 '!'">#&21</span></span> <span class="comment">; Drive 1: control byte &21</span></td> </tr> <tr id="addr-BF21"> <td class="addr"><a href="#addr-BF21">BF21</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BF25" data-tip="&BF25">issue_multi_sector_rw</a></span> <span class="comment">; Branch (always)</span></td> </tr> <tr id="addr-BF23"> <td class="addr"><a href="#addr-BF23">BF23</a></td> <td><span class="label">.check_sectors_remaining<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF1D">← BF1D BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="34 &22 %00100010 '"'">#&22</span></span> <span class="comment">; Drive 0: control byte &22</span></td> </tr> <tr id="addr-BF25"> <td class="addr"><a href="#addr-BF25">BF25</a></td> <td><span class="label">.issue_multi_sector_rw<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF21">← BF21 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Store in NMI drive control</span></td> </tr> <tr id="addr-BF28"> <td class="addr"><a href="#addr-BF28">BF28</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Set head-loaded flag</span></td> </tr> <tr id="addr-BF2B"> <td class="addr"><a href="#addr-BF2B">BF2B</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry</span></td> </tr> <tr id="addr-BF2C"> <td class="addr"><a href="#addr-BF2C">BF2C</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Restore head-loaded flag</span></td> </tr> <tr id="addr-BF2F"> <td class="addr"><a href="#addr-BF2F">BF2F</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BF55" data-tip="&BF55 – Calculate track/sector from block with range check">floppy_ts_block_check_range</a></span> <span class="comment">; Calculate track/sector with range chk</span></td> </tr> <tr id="addr-BF32"> <td class="addr"><a href="#addr-BF32">BF32</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Get NMI drive control byte</span></td> </tr> <tr id="addr-BF35"> <td class="addr"><a href="#addr-BF35">BF35</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&FE80">fdc_1770_drive_control</span></span> <span class="comment">; Write to FDC control register</span></td> </tr> <tr id="addr-BF38"> <td class="addr"><a href="#addr-BF38">BF38</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate bit 0 to carry</span></td> </tr> <tr id="addr-BF39"> <td class="addr"><a href="#addr-BF39">BF39</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BF47" data-tip="&BF47">handle_sector_error</a></span> <span class="comment">; C=0: last access was other drive</span></td> </tr> <tr id="addr-BF3B"> <td class="addr"><a href="#addr-BF3B">BF3B</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E5">wksp_fdc_track_0</span></span> <span class="comment">; Get saved track for this drive</span></td> </tr> <tr id="addr-BF3E"> <td class="addr"><a href="#addr-BF3E">BF3E</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store as current track</span></td> </tr> <tr id="addr-BF40"> <td class="addr"><a href="#addr-BF40">BF40</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Check head-loaded state</span></td> </tr> <tr id="addr-BF43"> <td class="addr"><a href="#addr-BF43">BF43</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BF54" data-tip="&BF54">return_48</a></span> <span class="comment">; Head loaded: no seek needed</span></td> </tr> <tr id="addr-BF45"> <td class="addr"><a href="#addr-BF45">BF45</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BF51" data-tip="&BF51">restore_track_zero</a></span> <span class="comment">; Branch (always)</span></td> </tr> <tr id="addr-BF47"> <td class="addr"><a href="#addr-BF47">BF47</a></td> <td><span class="label">.handle_sector_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF39">← BF39 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E6">wksp_fdc_track_1</span></span> <span class="comment">; Get saved track for other drive</span></td> </tr> <tr id="addr-BF4A"> <td class="addr"><a href="#addr-BF4A">BF4A</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Store as current track</span></td> </tr> <tr id="addr-BF4C"> <td class="addr"><a href="#addr-BF4C">BF4C</a></td> <td> <span class="opcode">BIT</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Check head-loaded state</span></td> </tr> <tr id="addr-BF4F"> <td class="addr"><a href="#addr-BF4F">BF4F</a></td> <td> <span class="opcode">BVC</span> <span class="operand"><a href="#addr-BF54" data-tip="&BF54">return_48</a></span> <span class="comment">; Not loaded: need seek</span></td> </tr> <tr id="addr-BF51"> <td class="addr"><a href="#addr-BF51">BF51</a></td> <td><span class="label">.restore_track_zero<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF45">← BF45 BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BD3F" data-tip="&BD3F – Seek floppy head to track 0">floppy_restore_track_0</a></span> <span class="comment">; Seek to track 0 and re-seek</span></td> </tr> <tr id="addr-BF54"> <td class="addr"><a href="#addr-BF54">BF54</a></td> <td><span class="label">.return_48<span class="ref-badge">←2</span><span class="ref-popup"><a href="#addr-BF43">← BF43 BPL</a><a href="#addr-BF4F">← BF4F BVC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BF55"> <td colspan="2"><div class="sub-header"> <h3>Calculate track/sector from block with range check</h3> <div class="sub-desc"><p>Convert a logical block number to a physical track and sector number for the floppy disc, checking that the block is within the valid range for the disc.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BF55">BF55</a></td> <td><span class="label">.floppy_ts_block_check_range<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF2F">← BF2F JSR</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; Y=7: offset to sector mid byte</span></td> </tr> <tr id="addr-BF57"> <td class="addr"><a href="#addr-BF57">BF57</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector address mid byte</span></td> </tr> <tr id="addr-BF59"> <td class="addr"><a href="#addr-BF59">BF59</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="10 &0A %00001010 LF">#&0a</span></span> <span class="comment">; Sector mid >= &0A (2560 sectors)?</span></td> </tr> <tr id="addr-BF5B"> <td class="addr"><a href="#addr-BF5B">BF5B</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BF86" data-tip="&BF86 – Calculate track/sector from block at &B0">floppy_ts_b0_block</a></span> <span class="comment">; Below limit, calculate track/sector</span></td> </tr> <tr id="addr-BF5D"> <td class="addr"><a href="#addr-BF5D">BF5D</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BF66" data-tip="&BF66">bad_address_error</a></span> <span class="comment">; Above &0A: definitely out of range</span></td> </tr> <tr id="addr-BF5F"> <td class="addr"><a href="#addr-BF5F">BF5F</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Exactly &0A: check low byte too</span></td> </tr> <tr id="addr-BF60"> <td class="addr"><a href="#addr-BF60">BF60</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector address low byte</span></td> </tr> <tr id="addr-BF62"> <td class="addr"><a href="#addr-BF62">BF62</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="0 &00 %00000000 NUL">#0</span></span> <span class="comment">; Low byte < 0? (always false)</span></td> </tr> <tr id="addr-BF64"> <td class="addr"><a href="#addr-BF64">BF64</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BF6C" data-tip="&BF6C">check_multi_sector_range</a></span> <span class="comment">; Compare always false (A >= 0)</span></td> </tr> <tr id="addr-BF66"> <td class="addr"><a href="#addr-BF66">BF66</a></td> <td><span class="label">.bad_address_error<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BA60">← BA60 JMP</a><a href="#addr-BF0C">← BF0C JMP</a><a href="#addr-BF5D">← BF5D BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="97 &61 %01100001 'a'">#&61</span></span> <span class="comment">; Error &61: bad address</span></td> </tr> <tr id="addr-BF68"> <td class="addr"><a href="#addr-BF68">BF68</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Store error code</span></td> </tr> <tr id="addr-BF6A"> <td class="addr"><a href="#addr-BF6A">BF6A</a></td> <td><span class="label">.branch_to_floppy_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF17">← BF17 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span> <span class="comment">; Branch to floppy error handler</span></td> </tr> <tr id="addr-BF6C"> <td class="addr"><a href="#addr-BF6C">BF6C</a></td> <td><span class="label">.check_multi_sector_range<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF64">← BF64 BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A1">zp_floppy_control</span></span> <span class="comment">; Check if multi-sector operation</span></td> </tr> <tr id="addr-BF6E"> <td class="addr"><a href="#addr-BF6E">BF6E</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Bit 4 set: sector count specified?</span></td> </tr> <tr id="addr-BF70"> <td class="addr"><a href="#addr-BF70">BF70</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BF86" data-tip="&BF86 – Calculate track/sector from block at &B0">floppy_ts_b0_block</a></span> <span class="comment">; No, just calculate track/sector</span></td> </tr> <tr id="addr-BF72"> <td class="addr"><a href="#addr-BF72">BF72</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="9 &09 %00001001 HT">#9</span></span> <span class="comment">; Y=9: offset to sector count</span></td> </tr> <tr id="addr-BF74"> <td class="addr"><a href="#addr-BF74">BF74</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector count</span></td> </tr> <tr id="addr-BF76"> <td class="addr"><a href="#addr-BF76">BF76</a></td> <td> <span class="opcode">DEY</span> <span class="comment">; Y=8: back to sector low byte</span></td> </tr> <tr id="addr-BF77"> <td class="addr"><a href="#addr-BF77">BF77</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry for addition</span></td> </tr> <tr id="addr-BF78"> <td class="addr"><a href="#addr-BF78">BF78</a></td> <td> <span class="opcode">ADC</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Add sector count to start sector</span></td> </tr> <tr id="addr-BF7A"> <td class="addr"><a href="#addr-BF7A">BF7A</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-BF80" data-tip="&BF80">volume_error</a></span> <span class="comment">; Carry set: overflow, error</span></td> </tr> <tr id="addr-BF7C"> <td class="addr"><a href="#addr-BF7C">BF7C</a></td> <td> <span class="opcode">CMP</span> <span class="operand"><span class="imm" data-tip="1 &01 %00000001 SOH">#1</span></span> <span class="comment">; End sector < 1? (no sectors)</span></td> </tr> <tr id="addr-BF7E"> <td class="addr"><a href="#addr-BF7E">BF7E</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BF86" data-tip="&BF86 – Calculate track/sector from block at &B0">floppy_ts_b0_block</a></span> <span class="comment">; OK, calculate track/sector</span></td> </tr> <tr id="addr-BF80"> <td class="addr"><a href="#addr-BF80">BF80</a></td> <td><span class="label">.volume_error<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF7A">← BF7A BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="imm" data-tip="99 &63 %01100011 'c'">#&63</span></span> <span class="comment">; Error &63: volume error</span></td> </tr> <tr id="addr-BF82"> <td class="addr"><a href="#addr-BF82">BF82</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Store error code</span></td> </tr> <tr id="addr-BF84"> <td class="addr"><a href="#addr-BF84">BF84</a></td> <td> <span class="opcode">BNE</span> <span class="operand"><a href="#addr-BFAE" data-tip="&BFAE – Handle floppy disc error">floppy_error</a></span> <span class="comment">; Branch to floppy error handler</span></td> </tr> <tr> <td class="addr"></td> <td><span class="fall-through">fall through ↓</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BF86"> <td colspan="2"><div class="sub-header"> <h3>Calculate track/sector from block at &B0</h3> <div class="sub-desc"><p>Convert the logical block number at (&B0) to a physical track and sector number.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BF86">BF86</a></td> <td><span class="label">.floppy_ts_b0_block<span class="ref-badge">←3</span><span class="ref-popup"><a href="#addr-BF5B">← BF5B BCC</a><a href="#addr-BF70">← BF70 BEQ</a><a href="#addr-BF7E">← BF7E BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="7 &07 %00000111 BEL">#7</span></span> <span class="comment">; Y=7: offset to sector mid byte</span></td> </tr> <tr id="addr-BF88"> <td class="addr"><a href="#addr-BF88">BF88</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector address mid byte (X)</span></td> </tr> <tr id="addr-BF8A"> <td class="addr"><a href="#addr-BF8A">BF8A</a></td> <td> <span class="opcode">TAX</span> <span class="comment">; Transfer low byte to X</span></td> </tr> <tr id="addr-BF8B"> <td class="addr"><a href="#addr-BF8B">BF8B</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Y=8: offset to sector low byte</span></td> </tr> <tr id="addr-BF8C"> <td class="addr"><a href="#addr-BF8C">BF8C</a></td> <td> <span class="opcode">LDA</span> <span class="operand">(<span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span>),y</span> <span class="comment">; Get sector address low byte (A)</span></td> </tr> <tr id="addr-BF8E"> <td class="addr"><a href="#addr-BF8E">BF8E</a></td> <td><span class="label">.floppy_ts_xa</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: init quotient to 0 (+1 later)</span></td> </tr> <tr id="addr-BF90"> <td class="addr"><a href="#addr-BF90">BF90</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BFA2" data-tip="&BFA2 – Divide X:A by 16, result in Y:A">xa_div_16_to_ya</a></span> <span class="comment">; Divide X:A by 16 sectors/track</span></td> </tr> <tr id="addr-BF93"> <td class="addr"><a href="#addr-BF93">BF93</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A4">zp_floppy_sector</span></span> <span class="comment">; A = sector within track</span></td> </tr> <tr id="addr-BF95"> <td class="addr"><a href="#addr-BF95">BF95</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span></span> <span class="comment">; Y = track number</span></td> </tr> <tr id="addr-BF97"> <td class="addr"><a href="#addr-BF97">BF97</a></td> <td> <span class="opcode">TYA</span> <span class="comment">; Copy track to A</span></td> </tr> <tr id="addr-BF98"> <td class="addr"><a href="#addr-BF98">BF98</a></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-BF99"> <td class="addr"><a href="#addr-BF99">BF99</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="80 &50 %01010000 'P'">#&50</span></span> <span class="comment">; Subtract 80 tracks (side 0)</span></td> </tr> <tr id="addr-BF9B"> <td class="addr"><a href="#addr-BF9B">BF9B</a></td> <td> <span class="opcode">BMI</span> <span class="operand"><a href="#addr-BFAD" data-tip="&BFAD">return_49</a></span> <span class="comment">; Track < 80: side 0, done</span></td> </tr> <tr id="addr-BF9D"> <td class="addr"><a href="#addr-BF9D">BF9D</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&00A5">zp_floppy_track_num</span></span> <span class="comment">; Track >= 80: save adjusted track</span></td> </tr> <tr id="addr-BF9F"> <td class="addr"><a href="#addr-BF9F">BF9F</a></td> <td> <span class="opcode">JMP</span> <span class="operand"><a href="#addr-BD22" data-tip="&BD22 – Select floppy disc side 1">floppy_set_side_1</a></span> <span class="comment">; Select side 1</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BFA2"> <td colspan="2"><div class="sub-header"> <h3>Divide X:A by 16, result in Y:A</h3> <div class="sub-desc"><p>Divide the 16-bit value X:A by 16 (shift right 4 places). Result quotient in Y, remainder in A.</p> </div> <div class="sub-registers"><table> <tr><th rowspan="3">On Entry</th><td>X</td><td>dividend high byte</td></tr> <tr><td>A</td><td>dividend low byte</td></tr> <tr><td>Y</td><td>must be &FF (initial quotient)</td></tr> <tr><th rowspan="3">On Exit</th><td>A</td><td>remainder (0-15)</td></tr> <tr><td>X</td><td>corrupted</td></tr> <tr><td>Y</td><td>quotient</td></tr> </table></div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BFA2">BFA2</a></td> <td><span class="label">.xa_div_16_to_ya<span class="ref-badge">←6</span><span class="ref-popup"><a href="#addr-BA89">← BA89 JSR</a><a href="#addr-BDCE">← BDCE JSR</a><a href="#addr-BDF0">← BDF0 JSR</a><a href="#addr-BF90">← BF90 JSR</a><a href="#addr-BFA6">← BFA6 BCS</a><a href="#addr-BFA9">← BFA9 BPL</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">SEC</span> <span class="comment">; Set carry for subtraction</span></td> </tr> <tr id="addr-BFA3"> <td class="addr"><a href="#addr-BFA3">BFA3</a></td> <td> <span class="opcode">SBC</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Subtract 16</span></td> </tr> <tr id="addr-BFA5"> <td class="addr"><a href="#addr-BFA5">BFA5</a></td> <td> <span class="opcode">INY</span> <span class="comment">; Increment quotient</span></td> </tr> <tr id="addr-BFA6"> <td class="addr"><a href="#addr-BFA6">BFA6</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-BFA2" data-tip="&BFA2 – Divide X:A by 16, result in Y:A">xa_div_16_to_ya</a></span> <span class="comment">; No underflow, subtract again</span></td> </tr> <tr id="addr-BFA8"> <td class="addr"><a href="#addr-BFA8">BFA8</a></td> <td> <span class="opcode">DEX</span> <span class="comment">; Underflow: borrow from high byte</span></td> </tr> <tr id="addr-BFA9"> <td class="addr"><a href="#addr-BFA9">BFA9</a></td> <td> <span class="opcode">BPL</span> <span class="operand"><a href="#addr-BFA2" data-tip="&BFA2 – Divide X:A by 16, result in Y:A">xa_div_16_to_ya</a></span> <span class="comment">; High byte >= 0, continue subtracting</span></td> </tr> <tr id="addr-BFAB"> <td class="addr"><a href="#addr-BFAB">BFAB</a></td> <td> <span class="opcode">ADC</span> <span class="operand"><span class="imm" data-tip="16 &10 %00010000 DLE">#&10</span></span> <span class="comment">; Add back the last 16 (remainder)</span></td> </tr> <tr id="addr-BFAD"> <td class="addr"><a href="#addr-BFAD">BFAD</a></td> <td><span class="label">.return_49<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BF9B">← BF9B BMI</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BFAE"> <td colspan="2"><div class="sub-header"> <h3>Handle floppy disc error</h3> <div class="sub-desc"><p>Process an error from the WD1770 floppy disc controller. Translates the controller error code into an ADFS error code and stores the error information in workspace.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BFAE">BFAE</a></td> <td><span class="label">.floppy_error<span class="ref-badge">←7</span><span class="ref-popup"><a href="#addr-BAC3">← BAC3 JMP</a><a href="#addr-BAF1">← BAF1 JMP</a><a href="#addr-BB3F">← BB3F JMP</a><a href="#addr-BB7F">← BB7F JMP</a><a href="#addr-BCDC">← BCDC JMP</a><a href="#addr-BF6A">← BF6A BNE</a><a href="#addr-BF84">← BF84 BNE</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&10E7">wksp_stack_save</span></span> <span class="comment">; Restore stack pointer from saved val</span></td> </tr> <tr id="addr-BFB1"> <td class="addr"><a href="#addr-BFB1">BFB1</a></td> <td> <span class="opcode">TXS</span> <span class="comment">; Restore stack from saved pointer</span></td> </tr> <tr id="addr-BFB2"> <td class="addr"><a href="#addr-BFB2">BFB2</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E0">wksp_fdc_xfer_mode</span></span> <span class="comment">; Check if NMI was in use</span></td> </tr> <tr id="addr-BFB5"> <td class="addr"><a href="#addr-BFB5">BFB5</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="32 &20 %00100000 SP">#&20</span></span> <span class="comment">; Bit 5: NMI active?</span></td> </tr> <tr id="addr-BFB7"> <td class="addr"><a href="#addr-BFB7">BFB7</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BFE0" data-tip="&BFE0">release_tube_after_floppy</a></span> <span class="comment">; No NMI, skip to Tube release</span></td> </tr> <tr id="addr-BFB9"> <td class="addr"><a href="#addr-BFB9">BFB9</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&0D5E">nmi_drive_ctrl</span></span> <span class="comment">; Get NMI status byte</span></td> </tr> <tr id="addr-BFBC"> <td class="addr"><a href="#addr-BFBC">BFBC</a></td> <td> <span class="opcode">ROR</span> <span class="comment">; Rotate bit 0 into carry</span></td> </tr> <tr id="addr-BFBD"> <td class="addr"><a href="#addr-BFBD">BFBD</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A3">zp_floppy_track</span></span> <span class="comment">; Get partial transfer count</span></td> </tr> <tr id="addr-BFBF"> <td class="addr"><a href="#addr-BFBF">BFBF</a></td> <td> <span class="opcode">BCC</span> <span class="operand"><a href="#addr-BFCD" data-tip="&BFCD">store_second_partial</a></span> <span class="comment">; C=0: store as second count</span></td> </tr> <tr id="addr-BFC1"> <td class="addr"><a href="#addr-BFC1">BFC1</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E5">wksp_fdc_track_0</span></span> <span class="comment">; C=1: store as first count</span></td> </tr> <tr id="addr-BFC4"> <td class="addr"><a href="#addr-BFC4">BFC4</a></td> <td> <span class="opcode">ROL</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Set bit 6 of transfer flag</span></td> </tr> <tr id="addr-BFC7"> <td class="addr"><a href="#addr-BFC7">BFC7</a></td> <td> <span class="opcode">CLC</span> <span class="comment">; Clear carry</span></td> </tr> <tr id="addr-BFC8"> <td class="addr"><a href="#addr-BFC8">BFC8</a></td> <td> <span class="opcode">ROR</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Clear bit 0 of transfer flag</span></td> </tr> <tr id="addr-BFCB"> <td class="addr"><a href="#addr-BFCB">BFCB</a></td> <td> <span class="opcode">BCS</span> <span class="operand"><a href="#addr-BFD8" data-tip="&BFD8">save_error_and_release_nmi</a></span> <span class="comment">; Branch if first partial sector</span></td> </tr> <tr id="addr-BFCD"> <td class="addr"><a href="#addr-BFCD">BFCD</a></td> <td><span class="label">.store_second_partial<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BFBF">← BFBF BCC</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E6">wksp_fdc_track_1</span></span> <span class="comment">; Store as second count</span></td> </tr> <tr id="addr-BFD0"> <td class="addr"><a href="#addr-BFD0">BFD0</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Get transfer state flags</span></td> </tr> <tr id="addr-BFD3"> <td class="addr"><a href="#addr-BFD3">BFD3</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="191 &BF %10111111">#&bf</span></span> <span class="comment">; Clear bit 6</span></td> </tr> <tr id="addr-BFD5"> <td class="addr"><a href="#addr-BFD5">BFD5</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Store updated flags</span></td> </tr> <tr id="addr-BFD8"> <td class="addr"><a href="#addr-BFD8">BFD8</a></td> <td><span class="label">.save_error_and_release_nmi<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BFCB">← BFCB BCS</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&00A0">zp_floppy_error</span></span> <span class="comment">; Get error code from zp_a0</span></td> </tr> <tr id="addr-BFDA"> <td class="addr"><a href="#addr-BFDA">BFDA</a></td> <td> <span class="opcode">STA</span> <span class="operand"><span class="ext-label" data-tip="&10E3">wksp_err_number</span></span> <span class="comment">; Save as error number</span></td> </tr> <tr id="addr-BFDD"> <td class="addr"><a href="#addr-BFDD">BFDD</a></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-BBE7" data-tip="&BBE7 – Release NMI via service call 11">release_nmi</a></span> <span class="comment">; Release NMI</span></td> </tr> <tr id="addr-BFE0"> <td class="addr"><a href="#addr-BFE0">BFE0</a></td> <td><span class="label">.release_tube_after_floppy<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BFB7">← BFB7 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">JSR</span> <span class="operand"><a href="#addr-8043" data-tip="&8043 – Release Tube if in use">release_tube</a></span> <span class="comment">; Release Tube if in use</span></td> </tr> <tr id="addr-BFE3"> <td class="addr"><a href="#addr-BFE3">BFE3</a></td> <td> <span class="opcode">LDX</span> <span class="operand"><span class="ext-label" data-tip="&00B0">zp_ctrl_blk_lo</span></span> <span class="comment">; Restore control block ptr low</span></td> </tr> <tr id="addr-BFE5"> <td class="addr"><a href="#addr-BFE5">BFE5</a></td> <td> <span class="opcode">LDA</span> <span class="operand"><span class="ext-label" data-tip="&10E3">wksp_err_number</span></span> <span class="comment">; Get error number</span></td> </tr> <tr id="addr-BFE8"> <td class="addr"><a href="#addr-BFE8">BFE8</a></td> <td> <span class="opcode">BEQ</span> <span class="operand"><a href="#addr-BFF1" data-tip="&BFF1">return_error_code</a></span> <span class="comment">; Zero = no error, return success</span></td> </tr> <tr id="addr-BFEA"> <td class="addr"><a href="#addr-BFEA">BFEA</a></td> <td> <span class="opcode">ORA</span> <span class="operand"><span class="imm" data-tip="64 &40 %01000000 '@'">#&40</span></span> <span class="comment">; Set bit 6: disc error flag</span></td> </tr> <tr id="addr-BFEC"> <td class="addr"><a href="#addr-BFEC">BFEC</a></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="imm" data-tip="255 &FF %11111111">#&ff</span></span> <span class="comment">; Y=&FF: mark transfer incomplete</span></td> </tr> <tr id="addr-BFEE"> <td class="addr"><a href="#addr-BFEE">BFEE</a></td> <td> <span class="opcode">STY</span> <span class="operand"><span class="ext-label" data-tip="&10E4">wksp_fdc_head_state</span></span> <span class="comment">; Mark transfer as incomplete</span></td> </tr> <tr id="addr-BFF1"> <td class="addr"><a href="#addr-BFF1">BFF1</a></td> <td><span class="label">.return_error_code<span class="ref-badge">←1</span><span class="ref-popup"><a href="#addr-BFE8">← BFE8 BEQ</a></span></span></td> </tr> <tr> <td class="addr"></td> <td> <span class="opcode">LDY</span> <span class="operand"><span class="ext-label" data-tip="&00B1">zp_ctrl_blk_hi</span></span> <span class="comment">; Restore control block ptr high</span></td> </tr> <tr id="addr-BFF3"> <td class="addr"><a href="#addr-BFF3">BFF3</a></td> <td> <span class="opcode">AND</span> <span class="operand"><span class="imm" data-tip="127 &7F %01111111 DEL">#&7f</span></span> <span class="comment">; Mask to 7-bit error code</span></td> </tr> <tr id="addr-BFF5"> <td class="addr"><a href="#addr-BFF5">BFF5</a></td> <td> <span class="opcode">RTS</span> <span class="comment">; Return</span></td> </tr> <tr> <td class="addr"></td> <td></td> </tr> </table> <table> <tr id="addr-BFF6"> <td colspan="2"><div class="sub-header"> <h3>ROM footer text</h3> <div class="sub-desc"><p>The text 'and Hugo.' followed by CR. This fills the last 10 bytes of the ROM, a credit to Hugo Tyson who wrote ADFS. The 'Hugo' string also serves as the 4-byte magic number at both ends of every ADFS directory structure.</p> </div> </div></td> </tr> <tr> <td class="addr"><a href="#addr-BFF6">BFF6</a></td> <td><span class="label">.str_rom_footer</span></td> </tr> <tr> <td class="addr"></td> <td> <span class="directive">EQUS</span> <span class="string">"and Hugo."</span>, <span data-tip="13 &0D %00001101 CR">&0D</span> <span class="comment">; "and Hugo." + CR: ROM footer text</span></td> </tr> </table> </div> </div> <script> (function () { var linkIcon = '<svg viewBox="0 0 16 16" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M6.75 9.25a3.25 3.25 0 0 0 4.596.443l1.904-1.904a3.25 3.25 0 0 0-4.596-4.596L7.5 4.347"/><path fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M9.25 6.75a3.25 3.25 0 0 0-4.596-.443L2.75 8.211a3.25 3.25 0 0 0 4.596 4.596L8.5 11.653"/></svg>'; var checkIcon = '<svg viewBox="0 0 16 16" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M3 8.5 6.5 12 13 4"/></svg>'; var btn = document.createElement("button"); btn.className = "copy-link"; btn.title = "Copy permalink"; btn.innerHTML = linkIcon; var toast = document.createElement("div"); toast.className = "copy-toast"; toast.textContent = "Link copied"; document.body.appendChild(toast); var toastTimer = null; var currentTd = null; var hideTimer = null; var listing = document.querySelector(".listing"); if (!listing) return; listing.addEventListener("mouseover", function (e) { var a = e.target.closest("td.addr a, td.runtime-addr a"); if (!a) return; var td = a.closest("td"); if (td === currentTd) return; clearTimeout(hideTimer); currentTd = td; btn.innerHTML = linkIcon; td.appendChild(btn); }); listing.addEventListener("mouseout", function (e) { var td = e.target.closest("td.addr, td.runtime-addr"); if (!td) return; var related = e.relatedTarget; if (related && td.contains(related)) return; hideTimer = setTimeout(function () { if (btn.parentNode) btn.parentNode.removeChild(btn); currentTd = null; }, 100); }); btn.addEventListener("mouseenter", function () { clearTimeout(hideTimer); }); btn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); var td = btn.closest("td"); var a = td && td.querySelector("a[href^='#']"); if (!a) return; var url = location.origin + location.pathname + a.getAttribute("href"); navigator.clipboard.writeText(url).then(function () { btn.innerHTML = checkIcon; setTimeout(function () { btn.innerHTML = linkIcon; }, 1500); clearTimeout(toastTimer); toast.classList.add("visible"); toastTimer = setTimeout(function () { toast.classList.remove("visible"); }, 1500); }); }); })(); </script> <button class="back-to-top" aria-label="Back to top"><svg viewBox="0 0 16 16" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M3 10 8 5 13 10"/></svg></button> <script> (function () { var btn = document.querySelector(".back-to-top"); if (!btn) return; window.addEventListener("scroll", function () { btn.classList.toggle("visible", window.scrollY > window.innerHeight); }, {passive: true}); btn.addEventListener("click", function () { window.scrollTo({top: 0, behavior: "smooth"}); }); })(); </script> <script> // Memory-map cross-window links: the browser handles reuse of the // named "memory-map" window natively (repeat clicks on any mm-link // navigate the same window), but some browsers leave the window // in the background instead of raising it. `window.open` + `.focus()` // fixes that without changing the navigation: the named target still // gets updated, and the existing window surfaces. (function () { document.querySelectorAll("a.mm-link").forEach(function (a) { a.addEventListener("click", function (e) { // Let modifier-clicks (cmd/ctrl/shift/middle) fall through // to the browser's default new-tab / background handling. if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) { return; } e.preventDefault(); var win = window.open(a.href, a.target || "memory-map"); if (win) win.focus(); }); }); })(); </script> </main> <footer class="site-footer"> <div class="container"> acornaeology.uk </div> </footer> </body> </html>