Update ACIA; Check EOF; Search for file; Spool or Exec file; Load and save block; Load block header; Load and save byte; Update CRC - 801 bytes (4.8%)


§1. Update ACIA.

 If necessary, read or write a byte to cassette, or read a byte from ROMFS.
 Can either be called from interrupt routine, or when loading from cassette.
.updateACIA = $f588
    DEC .fsGotACharacterToReadOrWriteFlag               Used when writing to tape,
                                                        to decrement the value from zero to
                                                        255 indicating the byte has been
                                                        written (which happens below)

    LDA .tapeOrROMSwitch                                filing system flag 0=tape; 2=ROMFS
    BEQ .updateACIATape                                 if (tape filing system active) then
                                                        branch

    deal with reading a byte from ROM / PHROM
    JSR .readByteFromROMFSorPHROM                       read ROMFS data ROM or PHROM
    TAY                                                 Y=A
    CLC                                                 clear carry flag
    BCC .postReadByte                                   ALWAYS branch

.updateACIATape = $f596
    deal with reading or writing to tape
    LDA .acia6850StatusRegister                         ACIA status register
    PHA                                                 save A on stack
    AND #2                                              clear all but bit 1
    BEQ .readByteFromTape                               if (transmit data register full,
                                                        i.e. not ready to send) then branch
                                                        (read from tape)
    LDY .tapeSendingFlag                                check we are sending to tape
    BEQ .readByteFromTape                               if (not sending to tape) then
                                                        branch (read from tape)

    send a byte to tape
    PLA                                                 get back A
    LDA .fsCharacterJustReadOrCharToWrite               get character to send
    STA .acia6850DataRegister                           ACIA transmit data register
    RTS                                                 

    read a byte from tape
.readByteFromTape = $f5a9
    LDY .acia6850DataRegister                           read ACIA receive data register
    PLA                                                 get back A
    LSR                                                 }
    LSR                                                 } shift bit 2 to carry
    LSR                                                 } (data carrier detect)
    fall through

§2. Process byte after being read from tape or ROMFS.

 This is called from the 100Hz interrupt routine. It updates the read progress state as
 needed, and stores the byte for later processing in .readBlockHeader and
 .readTapeBlockHeader.
 See .readBlockHeader.
 See .readTapeBlockHeader.

 The progress in reading a block is tracked by a progress byte (0-5):

 See .fsReadProgressState

     0 - exit
     1 - looking for carrier tone
     2 - found carrier tone, waiting for sync byte $2A
     3 - found sync byte $2A, now reading header
     4 - have read the header with non-zero block data length, now reading actual block data
     5 - finished reading data in block

 On Entry:
       Y = byte just read
       Carry set if it's the tape filing system and 'data carrier detect' bit is set
.postReadByte = $f5b0
    LDX .fsReadProgressState                            read progress state
    BEQ .exit31                                         if (.fsReadProgressState = 0) then
                                                        branch (exit)
    DEX                                                 X=X-1
    BNE .fsReadProgress2FoundCarrierTone                if (.fsReadProgressState > 1) then
                                                        branch

    deal with progress state = 1 (looking for carrier tone)
    BCC .exit31                                         if (reading from ROM OR carrier
                                                        tone from cassette not detected)
                                                        then branch (exit)
    LDY #2                                              Y=2
    BNE .storeTapeProgress                              ALWAYS branch (store progress and
                                                        return)

§3. fsReadProgress2FoundCarrierTone.

.fsReadProgress2FoundCarrierTone = $f5bd
    DEX                                                 X=X-1
    BNE .fsReadProgress3FoundSyncByte                   if (.fsReadProgressState > 2) then
                                                        branch

    deal with progress state = 2 (found carrier tone, waiting for it to finish)
    BCS .exit31                                         if (carrier tone from cassette
                                                        still detected) then branch (exit)

    TYA                                                 A=Y=byte read
    JSR .zeroChecksumAndFSFlag                          initialise CRC checksum to zero
    LDY #3                                              Y=3
    CMP #.fsSynchronisationByte                         check for the sync byte $2A
    BEQ .storeTapeProgress                              if (found sync byte) then branch
                                                        (store state and return)

    sync byte not found, revert back to progress state 1
    JSR .flipRelayOffAndOnThenSetToReadFromTape         control cassette system
    LDY #1                                              Y=1
    BNE .storeTapeProgress                              ALWAYS branch

§4. fsReadProgress3FoundSyncByte.

.fsReadProgress3FoundSyncByte = $f5d3
    DEX                                                 X=X-1
    BNE .fsReadProgress4FoundBlockData                  if (.fsReadProgressState > 3) then
                                                        branch

    deal with progress state = 3 (found sync byte $2A, reading block header)
    BCS +                                               if (carrier detected) then branch
    STY .fsCharacterJustReadOrCharToWrite               store byte just read
    BEQ .exit31                                         if (zero) then branch (exit)
+
    LDA #$80                                            A=$80
    STA .fsGotACharacterToReadOrWriteFlag               note that we have just read a byte
    BNE .exit31                                         ALWAYS branch (exit)

§5. fsReadProgress4FoundBlockData.

.fsReadProgress4FoundBlockData = $f5e2
    DEX                                                 X=X-1
    BNE .fsReadProgress5FinishedReadingBlockData        if (.fsReadProgressState > 4) then
                                                        branch

    deal with progress state = 4 (found non-zero length block, reading block data)

    if carrier detected then reset progress to zero
    BCS .resetACIAAndResetProgress                      if (carry set) then branch (reset
                                                        and return)

    TYA                                                 A=Y=byte just read
    JSR .updateCRC                                      perform CRC
    LDY .fsTempStorage                                  get length of block read
    INC .fsTempStorage                                  increment length of block read
    BIT .fsCharacterJustReadOrCharToWrite               check if bit 7 set (meaning
                                                        searching, not loading)
    BMI .checkForEndOfBlock                             if (searching) then branch

    send byte to second processor if needed, or store byte in regular memory
    JSR .checkLoadAddressIsForSecondProcessor           check load address is ok
                                                        also puts value of A (the byte
                                                        just read) into X
    BEQ +                                               if (load address is not for second
                                                        processor) then branch
    STX .tubeULADataRegister3                           Write byte to Tube. This triggers
                                                        an NMI on the co-processor.
    BNE .checkForEndOfBlock                             ALWAYS branch

+
    TXA                                                 A=X restore value
    STA (.loadAddressLow),Y                             store byte at current load address

.checkForEndOfBlock = $f600
    INY                                                 Y=Y+1
    CPY .fsBlockLengthLow                               block length
    BNE .exit31                                         if (not equal) then branch (exit)

    reached end of block data
    LDA #1                                              A=1
    STA .fsTempStorage                                  loop counter
    LDY #5                                              Y=5
    BNE .storeTapeProgress                              ALWAYS branch (store progress and
                                                        return)

§6. fsReadProgress5FinishedReadingBlockData.

.fsReadProgress5FinishedReadingBlockData = $f60e
    this first section reads two more bytes (one byte at a time) by using .fsTempStorage
    (which was initialised to 1 on the previous progress state above) as a loop counter.
    TYA                                                 A=Y=byte read
    JSR .updateCRC                                      update CRC
    DEC .fsTempStorage                                  decrement loop counter (1,0)
    BPL .exit31                                         exit if loop is not done

.resetACIAAndResetProgress = $f616
    JSR .resetACIA                                      reset ACIA
    LDY #0                                              Y=0
.storeTapeProgress = $f61b
    STY .fsReadProgressState                            progress state
.exit31 = $f61d
    RTS                                                 

§7. Check for EOF on an open file.

 On Entry:
       X = file handle
 On Exit:
       Preserves A,Y
       X is 0 if EOF reached
.checkEOFEntryPoint = $f61e
    PHA                                                 save A on stack
    TYA                                                 
    PHA                                                 save Y on stack
    TXA                                                 }
    TAY                                                 } Y=X=file handle
    LDA #3                                              A=3 (meaning check for file being
                                                        open for read or write)
    JSR .checkFileIsOpen                                confirm file is open (otherwise
                                                        BRK with 'Channel' error)
    LDA .fsStatusByte                                   tape / ROM FS status byte
    AND #%01000000                                      mask for just the EOF flag
    TAX                                                 X=A
    PLA                                                 get back A
    TAY                                                 Y=A
    PLA                                                 get back A
    RTS                                                 

§8. searchForFile.

.searchForFile = $f631
    LDA #0                                              }
    STA .currentBlockNumberLow                          } reset current block number
    STA .currentBlockNumberHigh                         }

.searchForSpecifiedBlock = $f637
    LDA .currentBlockNumberLow                          current block number (low byte)
    PHA                                                 save A on stack
    STA .nextBlockNumberLow                             save as next block number (low byte)
    LDA .currentBlockNumberHigh                         current block number (high byte)
    PHA                                                 save A on stack
    STA .nextBlockNumberHigh                            save as next block number (high byte)

    JSR .safePrintFollowingMessage                      print message following call
    !text "Searching"                                   
    !text .charRETURN,0                                 

    LDA #$FF                                            A=$FF (Meaning 'Must match filename
                                                        and block number')
    JSR .searchForBlockCheckFilingSystem                read data from tape filing
                                                        system/ROMFS
    PLA                                                 get back A
    STA .currentBlockNumberHigh                         current block number high
    PLA                                                 get back A
    STA .currentBlockNumberLow                          current block number low
    LDA .nextBlockNumberLow                             next block number low
    ORA .nextBlockNumberHigh                            next block number high
    BNE +                                               
    STA .currentBlockNumberLow                          zero current block number low
    STA .currentBlockNumberHigh                         zero current block number high
    LDA .checksumIsValidFlag                            checksum result
    BNE +                                               
    LDX #(.fsFilename-1) - .vduVariablesStart           
    JSR .copyToSoughtFilename                           copy filename from .fsFilename to
                                                        .filenameToSearchFor
+
    LDA .tapeOrROMSwitch                                filing system flag 0=tape filing
                                                        system; 2=ROMFS
    BEQ .setBlockFlagsAndExit                           if (tape filing system active) then
                                                        branch
    BVS .setBlockFlagsAndExit                           if (V set) then branch

.fileNotFoundError = $f674
    BRK                                                 
    !byte $D6                                           Error number
    !text "File not found",0                            

§9. setBlockFlagsAndExit.

.setBlockFlagsAndExit = $f685
    LDY #$FF                                            Y=$FF
    STY .fsLastBlockReadFlagsCopy                       copy of last read block flag
    RTS                                                 

§10. Close a SPOOL / EXEC file.

.closeSpoolOrExecFile = $f68b
    LDA #0                                              A=0, Z=0
    fall through...

§11. Close EXEC file and optionally open a new EXEC file.

 On Entry:
       A = 0
       Z = 0 means close any open EXEC file.
       Z = 1 means open the exec file whose filename is in address XY.
.closeAndOptionallyOpenANewEXECFile = $f68d
    PHP                                                 save flags on stack
    STY .tempWorkspaceE6                                .tempWorkspaceE6 = Y
    LDY .execFileHandle                                 file handle of EXEC file to be closed
    STA .execFileHandle                                 record EXEC file closed (A=0)
    BEQ +                                               if (closing an exec file) then branch
    JSR .OSFIND                                         close EXEC file (A=0; Y=file handle)
+
    LDY .tempWorkspaceE6                                Y = original Y
    PLP                                                 get back flags
    BEQ +                                               if (zero flag set on entry) then
                                                        branch (exit)
    LDA #$40                                            A is value for opening an input file
    JSR .OSFIND                                         open an EXEC file
    TAY                                                 Y=A
    BEQ .fileNotFoundError                              If (Y ===0) then branch ('File not
                                                        found')
    STA .execFileHandle                                 store EXEC file handle
+
    RTS                                                 

§12. bgetReadBlockAndHeader.

.bgetReadBlockAndHeader = $f6ac
    LDX #(.bgetFilename-1) - .vduVariablesStart         
    JSR .copyToSoughtFilename                           copy filename from .bgetFilename to
                                                        .filenameToSearchFor
    JSR .readBlockHeader                                read block header. On exit V is
                                                        clear for last block of ROMFS.
.checkLockedFlag = $f6b4
    LDA .fsBlockFlagByte                                block flag
    LSR                                                 move bit 0 into carry to check for a
                                                        'locked' file
    BCC .setLoadAddressAndLoadBlock                     if (not locked) then branch (skip
                                                        next instruction)
    JMP .fileLocked                                     'locked' file routine

§13. Set Load Address and Load Block.

.setLoadAddressAndLoadBlock = $f6bd
    LDA .nextBGETBlockLow                               Expected BGET file block number low
    STA .currentBlockNumberLow                          current block number low
    LDA .nextBGETBlockHigh                              expected BGET file block number high
    STA .currentBlockNumberHigh                         current block number high
    LDA #<.tapeOrRS423InputBuffer                       }
    STA .loadAddressLow                                 }
    LDA #>.tapeOrRS423InputBuffer                       }
    STA .loadAddressMid1                                } set load address to $FFFF0A00
    LDA #$FF                                            } (i.e. .tapeOrRS423InputBuffer)
    STA .loadAddressMid2                                }
    STA .loadAddressHigh                                }
    JSR .setStateForLoadingBlockDataOrReset             set progress to read block data
    JSR .loadBlock                                      load block from tape
    BNE .searchForBlock                                 if (non zero) then branch
    LDA .tapeOrRS423InputBuffer + $FF                   get last character from input buffer
    STA .lastCharacterOfCurrentlyResidentBlock          last character currently resident
                                                        block
    JSR .incrementBlockNumbers                          inc. current block number
    STX .nextBGETBlockLow                               expected BGET file block number low
    STY .nextBGETBlockHigh                              expected BGET file block number high

    LDX #2                                              X = loop counter
-
    LDA .fsBlockLengthLow,X                             read bytes from block flag/block
                                                        length
    STA .tapeInputCurrentBlockSizeLow,X                 store into current values of above
    DEX                                                 X=X-1
    BPL -                                               until X=-1 ($FF)

    BIT .blockFlagOfCurrentlyResidentBlock              block flag of currently resident
                                                        block
    BPL +                                               if (not last block) then branch
                                                        (don't print newline)
    JSR .printReturnSafely                              print newline if needed
+
    JMP .cancelTapeOperationAndMotor                    

§14. searchForBlock.

.searchForBlock = $f702
    JSR .searchForSpecifiedBlock                        search for a specified block
    BNE .checkLockedFlag                                ALWAYS branch (check for locked
                                                        condition)

§15. checkForROMBlockMarker.

.checkForROMBlockMarker = $f707
    CMP #.fsSynchronisationByte                         check for synchronising byte $2A (at
                                                        start of first block)
    BEQ .startOfBlock                                   if (found synchronising byte for
                                                        first block) then branch
    CMP #.romFSMiddleBlockHeaderByte                    check for header byte for a middle
                                                        block
    BNE .clearCatalogueStatusBadROM                     if (middle block header not found)
                                                        then branch (error 'Bad ROM')

    INC .fsBlockNumberLow                               block number
    BNE +                                               
    INC .fsBlockNumberHigh                              block number high
+
    LDX #$FF                                            X=$FF
    BIT .allBitsSet                                     set N and V flags
    BNE .clearChecksumTapeProgress                      ALWAYS branch

§16. clearCatalogueStatusBadROM.

.clearCatalogueStatusBadROM = $f71e
    LDA #%11110111                                      }
    JSR .clearTapeStatusBits                            } clear current catalogue status
                                                        [this could just call
                                                         JSR .clearCatalogueStatus
                                                         instead of the LDA/JSR instructions
                                                         above, for a 2 byte saving]

.badROMError = $f723
    BRK                                                 cause error
    !byte $D7                                           error number
    !text "Bad ROM"                                     message
    !byte 0                                             terminator

§17. readTapeBlockHeader.

.readTapeBlockHeader = $f72d
    LDY #$FF                                            
    JSR .storeFileHandleAndMotorOn                      switch Motor on
    LDA #1                                              A=1
    STA .fsReadProgressState                            start the read progress state
    JSR .flipRelayOffAndOnThenSetToReadFromTape         control serial system

-
    JSR .checkForEscapeDuringCassetteOperation          confirm ESC not set and tape filing
                                                        system not executing
    LDA #3                                              A=3
    CMP .fsReadProgressState                            progress state
    BNE -                                               back until .fsReadProgressState=3

.startOfBlock = $f742
    LDY #0                                              Y=0
    JSR .setChecksumBytesToY                            zero checksum bytes

    read filename
-
    JSR .readByteFromTapeOrROM                          get character from file and do CRC
    BVC .reachedEndOfHeader                             if (V clear) then branch
    STA .fsFilename,Y                                   store
    BEQ +                                               or if A=0 then branch
    INY                                                 Y=Y+1
    CPY #11                                             check Y against 11
    BNE -                                               if (Y != 11) then branch (go back
                                                        for next character)
    DEY                                                 Y=10

+
    read rest of header
    LDX #12                                             X=loop counter (from 12 to 31) to
                                                        read rest of header
-
    JSR .readByteFromTapeOrROM                          get character from file and do CRC
    BVC .reachedEndOfHeader                             if (V clear) then branch
    STA .fsFilename,X                                   store byte
    INX                                                 X=X+1
    CPX #$1F                                            check X for 31
    BNE -                                               if (X is not 31) then loop back

.reachedEndOfHeader = $f766
    TYA                                                 }
    TAX                                                 } X=Y=length of filename
    LDA #0                                              A=0
    STA .fsFilename,Y                                   store terminator
    LDA .tapeChecksumLow                                }
    ORA .tapeChecksumHigh                               } Check if the CRC value is zero
    STA .checksumIsValidFlag                            Checksum result is zero if valid

.clearChecksumTapeProgress = $f773
    JSR .zeroChecksumAndFSFlag                          reset checksum etc
    STY .fsReadProgressState                            reset progress (Y=0)
    TXA                                                 A=X=length of filename
    BNE .exit32                                         The tape filename is always non-zero
                                                        in length so:
                                                        ALWAYS branch (exit)
                                                        [In which case, RTS would be shorter]

§18. Read block header.

 On Exit:
       V = clear if final block
.readBlockHeader = $f77b
    LDA .tapeOrROMSwitch                                filing system flag 0=tape; 2=ROMFS
    BEQ .readTapeBlockHeader                            if (tape filing system active) then
                                                        branch

    Read ROM file header
    See NAUG Section 17.5.6, Page 317 for ROM data format
-
    JSR .readByteFromROMFSorPHROM                       read ROMFS data ROM or PHROM
    CMP #.romFSFinalBlockHeaderByte                     check for ROM file terminator
    BNE .checkForROMBlockMarker                         if (not terminator) then branch

    ROM file terminator found
    LDA #%00001000                                      A=isolating bit 3 (catalogue status)
    AND .fsStatusByte                                   check catalogue status
    BEQ +                                               if (clear) then branch (skip next
                                                        instruction)
    JSR .saveFlagsPrintCR                               print CR safely
+
    JSR .readByteFromROMOrPHROM                         get byte from data ROM
    BCC -                                               if (carry clear) then branch (loop
                                                        back and try again)
    CLV                                                 clear overflow flag
    RTS                                                 

§19. Read byte from tape or ROM.

.readByteFromTapeOrROM = $f797
    LDA .tapeOrROMSwitch                                filing system flag 0=tape; 2=ROMFS
    BEQ +                                               if (tape filing system active) then
                                                        branch

    deal with ROMFS read byte
    TXA                                                 }
    PHA                                                 }
    TYA                                                 } save X and Y
    PHA                                                 }
    JSR .readByteFromROMFSorPHROM                       read ROMFS data ROM or PHROM
    STA .fsCharacterJustReadOrCharToWrite               store character just read
    LDA #$FF                                            Top bit set
    STA .fsGotACharacterToReadOrWriteFlag               note that we have just read a byte
                                                        set 'flag' to $FF to avoid waiting
                                                        for
                                                        byte to be read (c.f. from tape)
    PLA                                                 }
    TAY                                                 }
    PLA                                                 } restore X and Y
    TAX                                                 }
+
    JSR .waitForByteToReadOrWrite                       check for Escape and loop till bit 7
                                                        of FS buffer flag=1
    fall through...

§20. Update CRC.

 A cyclic redundancy check is updated one byte at a time.

 Pseudo-code is as follows. HL = high and low bytes of the CRC. A = byte on input.

   H = A EOR H
   FOR X = 1 TO 8
       Carry=bit 7 of H
       IF (Carry = 1) THEN HL=HL EOR $0810
       HL=(HL*2 + Carry) AND $FFFF
   NEXT X

 On Entry:
       A = byte just written or read
       Preserves A, X and Y
.updateCRC = $f7b0
    PHP                                                 save flags on stack
    PHA                                                 save A on stack
    SEC                                                 } Set top bit of CRC bit counter
    ROR .tapeCRCBitCounter                              } This acts as a loop counter

    EOR .tapeChecksumHigh                               }
    STA .tapeChecksumHigh                               } H = A EOR H
-
    LDA .tapeChecksumHigh                               
    ROL                                                 Carry = bit 7 (A = A * 2)
    BCC +                                               
    ROR                                                 A = A / 2 = H

    EOR #$08                                            }
    STA .tapeChecksumHigh                               }
    LDA .tapeChecksumLow                                } HL=HL EOR $0810
    EOR #$10                                            }
    STA .tapeChecksumLow                                }
    SEC                                                 set carry flag

+
    ROL .tapeChecksumLow                                }
    ROL .tapeChecksumHigh                               } HL=(HL*2 + Carry) AND &FFFF

    LSR .tapeCRCBitCounter                              shift loop counter right
    BNE -                                               if (eight bits not processed) then
                                                        branch

    PLA                                                 get back A
    PLP                                                 get back flags
.exit32 = $f7d4
    RTS                                                 

§21. Set state ready for loading block data (or reset).

 Set the character just read to zero, and the progress state to 4.
 Together this signifies we are ready to load block data.
 If V is clear, then we instead reset to progress state zero.

 On Entry:
       If V clear, then reset to state zero
       otherwise set to state 4, ready to read the block data bytes
.setStateForLoadingBlockDataOrReset = $f7d5
    LDA #0                                              A=0
    fall through...

§22. Set state ready for loading block data or searching (or reset state).

 Store the character just read. If all is well (V set and non-zero block length), then
 progress state is set to 4. Otherwise we revert back to progress state zero.

 On Entry:
       A is $FF when searching, $00 when loading
       V clear means reset progress state to zero
       .fsBlockLengthLow/High holds the block length
.setCharAThenProgressState4OrReset = $f7d7
    STA .fsCharacterJustReadOrCharToWrite               store character just read
    LDX #0                                              X=0
    STX .fsTempStorage                                  
    BVC +                                               
    LDA .fsBlockLengthLow                               block length
    ORA .fsBlockLengthHigh                              block length high
    BEQ +                                               if (block length is zero) then branch
    LDX #4                                              X=4 (block length not zero)
+
    STX .fsReadProgressState                            save progress state
    RTS                                                 

§23. Save a block to tape.

 When saving to tape, we record a five second delay before the first block, then a delay
 specified by the interblock gap for all subsequent blocks. After all blocks are recorded
 there is a further five second delay.
.saveABlockToTape = $f7ec
    PHP                                                 save flags on stack
    LDX #3                                              X=loop counter
    LDA #0                                              A=value to store
-
    STA .fsSpareByteA,X                                 clear the four spare bytes of file
                                                        system block
    DEX                                                 X=X-1
    BPL -                                               

    LDA .fsBlockNumberLow                               block number
    ORA .fsBlockNumberHigh                              block number high
    BNE .waitForInterblockGap                           if (block number is not zero) then
                                                        branch
    JSR .waitFiveSeconds                                generate a 5 second delay
    BEQ +                                               ALWAYS branch

.waitForInterblockGap = $f804
    JSR .generateInterBlockGapDelay                     generate delay set by interblock gap

    initialise and save value $2A to tape
+
    LDA #.fsSynchronisationByte                         A=synchronisation byte (byte to save
                                                        to tape)
    STA .fsCharacterJustReadOrCharToWrite               store character to write
    JSR .zeroChecksumAndFSFlag                          initialise checksum and file system
                                                        buffer flag
    JSR .activateRequestToSend                          request send to tape
    JSR .waitForByteToReadOrWrite                       wait for byte to be saved to tape
    DEY                                                 Y=Y-1

    save filename
-
    INY                                                 Y=Y+1
    LDA .filenameToSearchFor,Y                          move sought filename
    STA .fsFilename,Y                                   into filename block
    JSR .saveByteAToTapeAndUpdateCRC                    transfer byte to tape filing system
                                                        and do CRC
    BNE -                                               if (filename not yet complete) then
                                                        branch (do it again)

    save rest of header:
      load address      (4 bytes)
      execution address (4 bytes)
      block number      (2 bytes)
      block length      (2 bytes)
      block flag byte   (1 byte)
      spare bytes       (4 bytes)
    LDX #.fsLoadAddressLow - .fsFilename                X=offset to load address
-
    LDA .fsFilename,X                                   get filename byte
    JSR .saveByteAToTapeAndUpdateCRC                    transfer byte to tape filing system
                                                        and do CRC
    INX                                                 X=X+1
    CPX #.fsChecksumLow - .fsFilename                   until X reaches the end of the
                                                        header block (just before the start
                                                        of the two checksum bytes)
    BNE -                                               

    JSR .saveChecksumToTape                             save checksum to TAPE reset buffer
                                                        flag
    LDA .fsBlockLengthLow                               block length
    ORA .fsBlockLengthHigh                              block length high
    BEQ .saveFinalTwoBytes                              if (block length is zero) then branch

    LDY #0                                              Y=0
    JSR .setChecksumBytesToY                            zero checksum bytes

-
    LDA (.tapeSaveStartAddressLow),Y                    get a data byte to save
    JSR .checkLoadAddressIsForSecondProcessor           check if load address is from second
                                                        processor
    BEQ +                                               if (not from second processor) then
                                                        branch
    LDX .tubeULADataRegister3                           read byte from second processor
+
    TXA                                                 A=X
    JSR .saveByteAToTapeAndUpdateCRC                    transfer byte to tape filing system
                                                        and do CRC
    INY                                                 Y=Y+1
    CPY .fsBlockLengthLow                               check block length
    BNE -                                               if (block not done yet) then branch
                                                        (loop back)

    JSR .saveChecksumToTape                             save checksum to TAPE

.saveFinalTwoBytes = $f855
    JSR .waitForByteToReadOrWrite                       save byte
    JSR .waitForByteToReadOrWrite                       save byte
    JSR .resetACIA                                      reset ACIA

    LDA #1                                              A=1
    JSR .generateDelay                                  generate 0.1 second delay
    PLP                                                 get back flags
    JSR .updateBlockFlagAndDisplayProgress              update block flag, PRINT filename
                                                        (and address if reqd)
    BIT .fsBlockFlagByte                                block flag
    BPL +                                               if (this is NOT last block) then
                                                        branch
    PHP                                                 save flags on stack
    JSR .waitFiveSeconds                                generate a 5 second delay
    JSR .beepCancelAndPrintReturn                       sound bell and cancel
    PLP                                                 get back flags
+
    RTS                                                 

§24. saveByteAToTapeAndUpdateCRC.

.saveByteAToTapeAndUpdateCRC = $f875
    JSR .saveByteAToTape                                save byte
    JMP .updateCRC                                      update CRC

§25. saveChecksumToTape.

.saveChecksumToTape = $f87b
    LDA .tapeChecksumHigh                               
    JSR .saveByteAToTape                                save byte to buffer, transfer to
                                                        tape filing system and reset flag
    LDA .tapeChecksumLow                                
    fall through...

§26. Save a byte to tape.

 On Entry:
       A = character to write
 On Exit:
       A is preserved
.saveByteAToTape = $f882
    STA .fsCharacterJustReadOrCharToWrite               store character to write

.waitForByteToReadOrWrite = $f884
    JSR .checkForEscapeDuringCassetteOperation          confirm ESC not set and tape filing
                                                        system not executing
    BIT .fsGotACharacterToReadOrWriteFlag               check if we have read / written a
                                                        byte
    BPL .waitForByteToReadOrWrite                       loop back until we have read /
                                                        written a byte

    byte is now read / written
    LDA #0                                              A=0
    STA .fsGotACharacterToReadOrWriteFlag               clear flag, we no longer have a byte
    LDA .fsCharacterJustReadOrCharToWrite               get character just read
    RTS                                                 

§27. waitFiveSeconds.

.waitFiveSeconds = $f892
    LDA #50                                             Set A for 50 tenths of a second
                                                        delay
    BNE .generateDelay                                  ALWAYS branch. generate delay
                                                        100ms * A (= 5 seconds)

§28. Delay for the (temporary) interblock gap time.

.generateInterBlockGapDelay = $f896
    LDA .tapeInterBlockGap                              get current interblock flag
    fall through...

§29. Wait for A/10 seconds, or until ESCAPE is pressed.

 On Entry:
       A = amount of delay in 10ths of a second
.generateDelay = $f898
    LDX #5                                              X=loop counter

.delayLoop = $f89a
    STA .verticalSyncCounter                            set vsync counter
-
    JSR .checkForEscapeDuringCassetteOperation          check for ESCAPE
    BIT .verticalSyncCounter                            check vsync counter (decremented 50
                                                        times a second)
    BPL -                                               if (not negative) then branch back
    DEX                                                 X--
    BNE .delayLoop                                      
    RTS