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
- §2. Display progress
- §3. Display Progress (post-filename)
- §4. Print four bytes from tape filing system block header
- §5. promptToRecordOnTape
- §6. Increment load or save address (by one page)
- §7. printSpaceThenHexByte
- §8. Print ASCII version of a hex value
- §9. printSpace
- §10. checkForEscapeDuringCassetteOperation
- §11. foundEscapeCondition
- §12. loadBlock
- §13. checksumOK
- §14. blockNumbersMatch
- §15. dontAbortOnError
- §16. safePrintFollowingMessage
- §17. Print the following message
- §18. compareFilenames
- §19. Filing System Data Errors
- §20. postFileError
- §21. waitForBlockToFinish
.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...
.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
.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
.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
.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
.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
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)
.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)
.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
.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
.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...
.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...
.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