Print progress; Prompt to record on tape; Print four byte hex; Check for ESCAPE during tape operations; Load block from tape; Print following message; Data? File? Block? error messages - 575 bytes (3.5%)


§1. generateScreenReports.

.generateScreenReports = $f8a9
    LDA .fsBlockNumberLow                               block number low
    ORA .fsBlockNumberHigh                              block number high
    BEQ .newlineAndContinue                             if (block number is zero) then
                                                        branch (print newline and continue)
    BIT .fsLastBlockReadFlagsCopy                       copy of last read block flags
    BPL .updateBlockFlagAndDisplayProgress              update block flag, PRINT filename
                                                        (and address if required)

.newlineAndContinue = $f8b6
    JSR .printReturnSafely                              print newline if needed
    fall through...

§2. Display progress.

.updateBlockFlagAndDisplayProgress = $f8b9
    LDY #0                                              Y=0
    STY .currentBlockHasDataErrorFlag                   reset data error flag to zero
    LDA .fsBlockFlagByte                                get block flag byte
    STA .fsLastBlockReadFlagsCopy                       store as copy of last read block flag
    JSR .shouldPrintMessage                             check if free to print message
    BEQ .exit33                                         if (not safe to print messages) then
                                                        branch (return)

    print just a carriage return, to return text cursor to the start of the current line
    (without emitting a linefeed to go down to the next line)
    LDA #.charRETURN                                    carriage return
    JSR .OSWRCH                                         print it (note no linefeed; so text
                                                        will overwrite any previous block's
                                                        message)

    display filename as printable characters, or '?' otherwise
.filenameLoop = $f8cd
    LDA .fsFilename,Y                                   get byte from filename (zero
                                                        terminated)
    BEQ .filenameDone                                   if (filename is ended) then branch
    CMP #.charSPACE                                     }
    BCC .displayQuestionMark                            } if (control code found) then
                                                        } branch (show '?')
    CMP #.charDELETE                                    check for being less than DELETE
    BCC .displayPrintableCharacter                      if (it's a printable character) then
                                                        branch
.displayQuestionMark = $f8da
    LDA #.charQUESTIONMARK                              A='?'
.displayPrintableCharacter = $f8dc
    JSR .OSWRCH                                         and print it
    INY                                                 Y=Y+1
    BNE .filenameLoop                                   ALWAYS branch (back to get rest of
                                                        filename)
    fall through...

§3. Display Progress (post-filename).

 John Kortink found a couple of bugs in the filing systems. See .reachedFinalBlock.

 The first is that in the ROM filing system, .tapeFileLengthLow / High only get set here
 if long messages are enabled, otherwise we early out of this routine. This means OSFILE
 can return an invalid size.

 See .reachedFinalBlock for more information
.filenameDone = $f8e2
    LDA .tapeOrROMSwitch                                filing system switch (0=cassette,
                                                        2=ROM filing system)
    BEQ +                                               if (tape filing system active) then
                                                        branch
    BIT .tapeCurrentOptionsByte                         check bit 6 of current options
    BVC .exit33                                         if (long messages not needed) then
                                                        branch (exit)

    print spaces after filename up to 11 characters total
+
    JSR .printSpace                                     print a space
    INY                                                 Y=Y+1
    CPY #11                                             
    BCC .filenameDone                                   if (Y<11) then branch (loop again to
                                                        fill out filename with spaces)

    print block number
    LDA .fsBlockNumberLow                               block number
    TAX                                                 remember block number in X
    JSR .printHexByte                                   print hex byte as ASCII
    BIT .fsBlockFlagByte                                block flag byte (bit 7 is 'last
                                                        block' flag)
    BPL .exit33                                         if (not end of file) then branch
                                                        (return)

    at this point we are on the last block of the file
    print the file length in hex
    TXA                                                 recall block number from X
    CLC                                                 
    ADC .fsBlockLengthHigh                              add block length high byte (to get
                                                        total file length high)
    STA .tapeFileLengthHigh                             store as file length (high byte)
    JSR .printSpaceThenHexByte                          print space then hex byte as ASCII
    LDA .fsBlockLengthLow                               get block length (low byte)
    STA .tapeFileLengthLow                              store as file length (low byte)
    JSR .printHexByte                                   print hex byte as ASCII
    BIT .tapeCurrentOptionsByte                         current options
    BVC .exit33                                         if (long messages not required) then
                                                        branch (return)

    print four spaces
    LDX #4                                              X=loop counter
-
    JSR .printSpace                                     print a space
    DEX                                                 
    BNE -                                               loop to print 4 spaces

    display load address, a space, then executable address
    LDX #.fsLoadAddressHigh - .fsFilename               X = offset to load address (high
                                                        byte)
    JSR .printFourByteAddress                           print 4 bytes from tape filing
                                                        system block header
    JSR .printSpace                                     print a space
    LDX #.fsExecutionAddressHigh - .fsFilename          X = offset to execution address
                                                        (high byte)
    fall through...

§4. Print four bytes from tape filing system block header.

 On Entry:
       X is offset to high byte of address from .fsFilename
.printFourByteAddress = $f927
    LDY #4                                              loop counter
-
    LDA .fsFilename,X                                   block header
    JSR .printHexByte                                   print ASCII equivalent of hex byte
    DEX                                                 X=X-1
    DEY                                                 Y=Y-1
    BNE -                                               
.exit33 = $f933
    RTS                                                 

§5. promptToRecordOnTape.

.promptToRecordOnTape = $f934
    LDA .tapeOrROMSwitch                                filing system flag 0=tape; 2=ROMFS
    BEQ +                                               if (tape filing system active) then
                                                        branch
    JMP .badCommandError                                'Bad command error message'
+
    JSR .zeroFileHandleAndMotorOn                       switch motor on
    JSR .setupForCassetteWrite                          set up tape filing system for write
                                                        operation
    JSR .shouldPrintMessage                             check if free to print message
    BEQ .exit33                                         if (not free to print message) exit
    JSR .safePrintFollowingMessage                      print message following call
    !text "RECORD then RETURN"                          message
    !byte 0                                             terminator

-
    JSR .checkForEscapeDuringCassetteOperation          check for ESCAPE
    JSR .OSRDCH                                         wait for keypress
    CMP #.charRETURN                                    check for the RETURN key
    BNE -                                               if (not RETURN key) then branch
                                                        (loop back to test again)
    JMP .OSNEWL                                         output Carriage RETURN and LINE FEED
                                                        then return

§6. Increment load or save address (by one page).

 Add a page onto the load address (.loadAddress) when loading,
 or save start address (.tapeSaveStartAddress) when saving.
.incrementLoadOrSaveAddress = $f96a
    INC .loadAddressMid1                                increment page
    BNE +                                               
    INC .loadAddressMid2                                increment higher bytes as needed
    BNE +                                               
    INC .loadAddressHigh                                increment higher bytes as needed
+
    RTS                                                 

§7. printSpaceThenHexByte.

.printSpaceThenHexByte = $f975
    PHA                                                 save A on stack
    JSR .printSpace                                     print a space
    PLA                                                 get back A
    fall through...

§8. Print ASCII version of a hex value.

 On Entry:
       A = value to print
       Preserves X and Y
.printHexByte = $f97a
    PHA                                                 save A on stack
    LSR                                                 }
    LSR                                                 }
    LSR                                                 } divide by 16 to put high nybble in
                                                        } low
    LSR                                                 }
    JSR .printHexDigit                                  print its ASCII equivalent
    PLA                                                 get back A
.printHexDigit = $f983
    CLC                                                 clear carry flag
    AND #%00001111                                      clear high nybble. A=0-15
    ADC #.charZERO                                      A = $30 to $3F
    CMP #.charNINE + 1                                  }
    BCC .writeCharJumper                                } if A <= ASC('9') then print
    ADC #.charA - (.charNINE + 1) - 1                   Convert $3A-$3F to A=$41-$46 ('A' to
                                                        'F')

.writeCharJumper = $f98e
    JMP .OSWRCH                                         print character and return

 This routine could be three bytes smaller...
printHexByte
   PHA                                                 ; save A on stack
   LSR                                                 ; }
   LSR                                                 ; }
   LSR                                                 ; } divide by 16 to put high nybble
in low
   LSR                                                 ; }
   JSR .printHexDigit                                  ; print its ASCII equivalent
   PLA                                                 ; get back A
   AND #%00001111                                      ; just the lower nybble
printHexDigit
   SED                                                 ; set decimal mode
   CMP #10                                             ; }
   ADC #.charZERO                                      ; } decimal mode arithmetic trick
   CLD                                                 ; clear decimal mode
   JMP .OSWRCH                                         ; print character and return

§9. printSpace.

.printSpace = $f991
    LDA #.charSPACE                                     A=' '
    BNE .writeCharJumper                                ALWAYS branch (to print it)

§10. checkForEscapeDuringCassetteOperation.

.checkForEscapeDuringCassetteOperation = $f995
    PHP                                                 save flags on stack
    BIT .tapeCritical                                   first read tape critical flag
    BMI .skipESCAPECheck                                if (tape critical) then branch
    BIT .escapeFlag                                     }
    BMI .foundEscapeCondition                           } if (ESCAPE condition found) then
                                                        } branch
.skipESCAPECheck = $f99e
    PLP                                                 get back flags
    RTS                                                 

§11. foundEscapeCondition.

.foundEscapeCondition = $f9a0
    JSR .clearCatalogueStatus                           clear catalogue status
    JSR .cancelTapeOperationAndMotor                    
    LDA #126                                            }
    JSR .OSBYTE                                         } Acknowledge ESCAPE condition
.escapeError = $f9ab
    BRK                                                 cause error
    !byte $11                                           error 17
    !text "Escape"                                      message
    !byte 0                                             terminator

§12. loadBlock.

 On Entry:
       Y = 0 for no messages
       otherwise show messages
.loadBlock = $f9b4
    TYA                                                 A=Y
    BEQ +                                               
    JSR .safePrintFollowingMessage                      print message following call

    !text .charRETURN,"Loading",.charRETURN             message text
    !byte 0                                             terminator

+
    STA .currentBlockHasDataErrorFlag                   data error = zero
    LDX #$FF                                            X=$FF
    LDA .checksumIsValidFlag                            Checksum result
    BNE .checkForChecksumError                          if (checksum failed) then branch

    check if we have the right filename
    JSR .compareFilenames                               check filename header block matches
                                                        searched filename.
    PHP                                                 save flags on stack
    LDX #$FF                                            X=$FF
    LDY #<.fileError                                    }
    LDA #>.fileError                                    } YA is set to point to 'File?' error
    PLP                                                 get back flags
    BNE .printNewlineThenReportError                    if (filenames don't match) then
                                                        branch (report a query unexpected
                                                        file name)

.checkForChecksumError = $f9d9
    LDY #<.dataError                                    making Y/A point to 'Data' for CRC
                                                        error
    LDA .checksumIsValidFlag                            Checksum result
    BEQ .checksumOK                                     if (no checksum error) then branch
    LDA #>.dataError                                    point to dataError
    BNE .printNewlineThenReportError                    ALWAYS branch (to report issue)

§13. checksumOK.

.checksumOK = $f9e3
    LDA .fsBlockNumberLow                               block number
    CMP .currentBlockNumberLow                          current block number low
    BNE .reportBlockError                               if (block numbers low byte dont
                                                        match) then branch
    LDA .fsBlockNumberHigh                              block number high
    CMP .currentBlockNumberHigh                         current block number high
    BEQ .blockNumbersMatch                              if (block numbers high byte matches)
                                                        then branch

.reportBlockError = $f9f1
    LDY #<.blockError                                   } an error has occurred
    LDA #>.blockError                                   } point AY to 'Block?' error
                                                        } (unexpected block number)
                                                        
.printNewlineThenReportError = $f9f5
    PHA                                                 }
    TYA                                                 }
    PHA                                                 } save registers AYX on stack
    TXA                                                 }
    PHA                                                 }
    JSR .newlineAndContinue                             print CR if indicated by current
                                                        block flag
    PLA                                                 }
    TAX                                                 }
    PLA                                                 } restore XYA from stack
    TAY                                                 }
    PLA                                                 }
    BNE .checkIfWeShouldShowError                       ALWAYS branch (since the high byte
                                                        of the error message address in A is
                                                        never zero)

§14. blockNumbersMatch.

.blockNumbersMatch = $fa04
    TXA                                                 A=X
    PHA                                                 save A on stack
    JSR .generateScreenReports                          report
    JSR .waitForBlockToFinish                           wait for block to finish
    PLA                                                 get back A
    TAX                                                 X=A
    LDA .tapeChecksumLow                                
    ORA .tapeChecksumHigh                               
    BEQ .exit34                                         if (checksum succeeded) then branch
                                                        (exit)

    checksum error
    LDY #<.dataError                                    }
    LDA #>.dataError                                    } AY points to 'Data?'

.checkIfWeShouldShowError = $fa18
    DEC .currentBlockHasDataErrorFlag                   set current block flag to $FF
    PHA                                                 save A on stack
    BIT .tapeCritical                                   read tape critical flag
    BMI .tidyUpAndDisplayError                          if (tape critical) then branch
    TXA                                                 A=X
    AND .tapeOrROMSwitch                                filing system flag 0=tape; 2=ROMFS
    BNE .tidyUpAndDisplayError                          if (ROMFS) then branch
    TXA                                                 A=X
    AND #%00010001                                      
    AND .tapeCurrentOptionsByte                         current options (check for abort)
    BEQ .dontAbortOnError                               if (not abort on error) then branch

.tidyUpAndDisplayError = $fa2c
    PLA                                                 get back A
    STA .printMessageAddressHigh                        store A
    STY .printMessageAddressLow                         store Y
    JSR .closeSpoolOrExecFile                           do *EXEC 0 to tidy up
    LSR .tapeCritical                                   clear tape critical flag
    JSR .beepAndCancelTapeOperation                     bell, reset ACIA and motor
    JMP (.printMessageAddressLow)                       display selected error report

§15. dontAbortOnError.

.dontAbortOnError = $fa3c
    PLA                                                 get A from stack
    INY                                                 }
    BNE +                                               } increment AY
    CLC                                                 }
    ADC #1                                              }
+
    PHA                                                 }
    TYA                                                 } save AY on stack. This is the
    PHA                                                 } (byte before) the error message
                                                        } which will be printed by
                                                        } .printFollowingMessage below
    fall through...

§16. safePrintFollowingMessage.

.safePrintFollowingMessage = $fa46
    JSR .shouldPrintMessage                             check if free to print message
    TAY                                                 Y=A
    fall through...

§17. Print the following message.

 Prints the message at the address on the top of the stack (this is usually the
 return address, so represents the data that follows a JSR to this function).
 The alternative route here is that we fall through from .dontAbortOnError above, which
 has put the appropriate address on the stack.

 On returning from this routine, execution continues from the byte directly after the
 message.

 On Entry:
       The top two bytes on the stack hold the address of the message - 1
       Y = zero means don't actually output the message
       otherwise print each character of the message using .OSASCI
 On Exit:
       A = 0
       Z is clear (not equal to zero)
.printFollowingMessage = $fa4a
    PLA                                                 }
    STA .printMessageAddressLow                         } get calling address
    PLA                                                 } and store it in
                                                        } .printMessageAddressLow/High
    STA .printMessageAddressHigh                        }
    TYA                                                 A=Y (used to set Z flag if Y=0)
    PHP                                                 save Z flag on stack
-
    INC .printMessageAddressLow                         
    BNE +                                               
    INC .printMessageAddressHigh                        
+
    LDY #0                                              Y=0
    LDA (.printMessageAddressLow),Y                     get byte of message to print
    BEQ .printingFinished                               if (terminator found) then branch
                                                        (jump back to calling function after
                                                        message)
    PLP                                                 restore flags from stack
    PHP                                                 save flags on stack again
    BEQ -                                               if (Z=1, i.e. don't output message)
                                                        then branch (loop back to get next
                                                        character)
    JSR .OSASCI                                         print character
    JMP -                                               jump back to get next character

.printingFinished = $fa68
    PLP                                                 get back flags
    INC .printMessageAddressLow                         increment pointers
    BNE +                                               
    INC .printMessageAddressHigh                        
+
    JMP (.printMessageAddressLow)                       return control to just after the
                                                        error message

§18. compareFilenames.

.compareFilenames = $fa72
    LDX #$FF                                            loop counter
.checkNextCharacter = $fa74
    INX                                                 X=X+1
    LDA .filenameToSearchFor,X                          get sought filename byte
    BNE .checkCharacter                                 if (non zero) then branch (check
                                                        this character)
    TXA                                                 A = filename length
    BEQ +                                               if (zero length) then branch (exit)
    LDA .fsFilename,X                                   A = filename byte
+
    RTS                                                 

.checkCharacter = $fa81
    JSR .isLetter                                       set carry if byte in A is NOT a
                                                        letter
    EOR .fsFilename,X                                   compare with filename character
    BCS +                                               if (carry set, i.e. not a letter)
                                                        then branch
    AND #$DF                                            convert to upper case
+
    BEQ .checkNextCharacter                             if (zero, i.e. filename characters
                                                        match) then branch (check the next
                                                        character)
.exit34 = $fa8d
    RTS                                                 

§19. Filing System Data Errors.

 Note that these error routines are called if the abort flag is set in the current options.
 If the abort flag is not set, then the routine is not called, but by manipulating the
 address of the routine, the message contained within is printed and execution continues
 after the message. See .blockNumbersMatch for an example which executes .dontAbortOnError
 See .dontAbortOnError.
.dataError = $fa8e
    BRK                                                 
    !byte $D8                                           error number
    !text .charRETURN, "Data?"                          message
    !byte 0                                             terminator
    BNE .postFileError                                  ALWAYs branch

.fileError = $fa99
    BRK                                                 
    !byte $DB                                           error number
    !text .charRETURN, "File?"                          message
    !byte 0                                             terminator
    BNE .postFileError                                  ALWAYs branch

.blockError = $faa4
    BRK                                                 
    !byte $DA                                           error number
    !text .charRETURN, "Block?"                         message
    !byte 0                                             terminator
    fall through...

§20. postFileError.

.postFileError = $faae
    LDA .currentBlockHasDataErrorFlag                   get current block flag
    BEQ .skipRewindTapeMessage                          if (zero) then branch
    TXA                                                 A=X
    BEQ .skipRewindTapeMessage                          if (X == 0) then branch (no retry)
    LDA #%00100010                                      A=$22
    BIT .tapeCurrentOptionsByte                         check current options
    BEQ .skipRewindTapeMessage                          if (not retry) then branch (no retry)

    rewind tape
    JSR .resetACIA                                      reset ACIA
    TAY                                                 Y=A
    JSR .printFollowingMessage                          print following message
    !byte .charRETURN                                   Carriage Return
    !byte .charBELL                                     Beep
    !text "Rewind tape"                                 Message
    !byte .charRETURN, .charRETURN                      Carriage Return, Carriage Return
    !byte 0                                             terminator
.exit35 = $fad2
    RTS                                                 

.skipRewindTapeMessage = $fad3
    JSR .saveFlagsPrintCR                               print CR if tape filing system not
                                                        critical
    fall through...

§21. waitForBlockToFinish.

.waitForBlockToFinish = $fad6
    LDA .fsReadProgressState                            get progress state
    BEQ .exit35                                         if (zero) then branch (return)
    JSR .checkForEscapeDuringCassetteOperation          check for ESCAPE
    LDA .tapeOrROMSwitch                                filing system flag 0=tape; 2=ROMFS
    BEQ .waitForBlockToFinish                           if (tape filing system active) then
                                                        branch (loop back)
    JSR .updateACIA                                     set up ACIA etc
    JMP .waitForBlockToFinish                           loop back again