Setup, Claim, and Reset ACIA; Cancel tape operation; Activate / deactivate RTS; Zero checksum; Copy filename; Motor on/off; Check file is open; Send data to second processor; Setup for cassette write - 280 bytes (1.7%)


§1. beepAndCancelTapeOperation.

.beepAndCancelTapeOperation = $fae8
    JSR .shouldPrintMessage                             check if free to print message
    BEQ .cancelTapeOperationAndMotor                    
    LDA #.charBELL                                      beep
    JSR .OSWRCH                                         
    fall through...

§2. cancelTapeOperationAndMotor.

 Cancel the tape operation:
       relinquish the Tube
       switch off the cassette motor
       reset ACIA
.cancelTapeOperationAndMotor = $faf2
    LDA #$80                                            
    JSR .secondProcessorTransfer                        relinquish the Tube (NAUG Section
                                                        18.8, Page 339)
    LDX #0                                              
    JSR .controlMotor                                   switch off motor
.cancelTapeOperation = $fafc
    PHP                                                 save flags on stack
    SEI                                                 prevent IRQ interrupts
    LDA .serialULARegisterCopy                          get serial ULA control register
                                                        setting
    STA .serialULAControlRegister                       write to serial ULA control register
                                                        setting
    LDA #0                                              A=0
    STA .rs423TimeoutCounter                            zero RS-423 timeout counter
    BEQ +                                               ALWAYS branch

§3. setupACIA.

.setupACIA = $fb0a
    PHP                                                 save flags on stacksave flags
+
    JSR .resetACIA                                      reset the ACIA
    LDA .rs423ControlRegisterCopy                       get last setting of ACIA
    JMP .storeACIAAndPull                               set ACIA and OS Copy from A before
                                                        exit

§4. checkForEscapeFlag.

.checkForEscapeFlag = $fb14
    PLP                                                 get back flags
    BIT .escapeFlag                                     check bit 7 of ESCAPE flag
    BPL .waitForRS423ToTimeout                          if (ESCAPE flag not set) then branch
    RTS                                                 return (ESCAPE is pending)

§5. claimSerialSystemForSequentialAccess.

.claimSerialSystemForSequentialAccess = $fb1a
    LDA .tapeOptionsByte                                get cassette filing system options
                                                        byte
                                                        high nybble used for LOAD and SAVE
                                                        operations
                                                        low nybble used for sequential access
                                                        See .tapeOptByteTable

    ASL                                                 }
    ASL                                                 }
    ASL                                                 } move low nybble into high nybble
    ASL                                                 }
    STA .tapeCurrentOptionsByte                         save current OPTions
    LDA .tapeSequentialAccessInterBlockGap              get sequential block gap (>0)
    BNE .claimSerialSystem                              ALWAYS branch

§6. claimSerialSystemForLoadSave.

.claimSerialSystemForLoadSave = $fb27
    LDA .tapeOptionsByte                                get cassette filing system options
                                                        byte
                                                        high nybble used for LOAD and SAVE
                                                        operations
                                                        low nybble used for sequential access
                                                        See .tapeOptByteTable

    AND #%11110000                                      clear low nybble
    STA .tapeCurrentOptionsByte                         store as current OPTions
    LDA #6                                              set current interblock gap to
                                                        6/10ths of a second

.claimSerialSystem = $fb2f
    STA .tapeInterBlockGap                              set current interblock gap

    wait for the RS-423 system to timeout, so that the cassette system can take over
.waitForRS423ToTimeout = $fb31
    CLI                                                 allow interrupts briefly
    PHP                                                 save flags on stack (with interrupts
                                                        enabled)
    SEI                                                 disable interrupts
    BIT .rs423ReadyFlag                                 
    BPL .checkForEscapeFlag                             if (RS-423 is busy) then branch
                                                        (check escape flag and try again)
    LDA .rs423TimeoutCounter                            
    BMI .checkForEscapeFlag                             if (RS-423 has NOT timed out) then
                                                        branch (check for escape flag and
                                                        try again)

    LDA #1                                              } store RS-423 timeout counter with
    STA .rs423TimeoutCounter                            } one to indicate that cassette has
                                                        } control of the ACIA 6850

    JSR .resetACIA                                      reset ACIA
    PLP                                                 get back flags (enabling interrupts)
    RTS                                                 

§7. resetACIA.

.resetACIA = $fb46
    LDA #%00000011                                      A=3 (bits CR0 and CR1 set means
                                                             'master reset')
    BNE .storeAToACIARegister                           ALWAYS branch (to reset ACIA)

§8. activateRequestToSend.

.activateRequestToSend = $fb4a
    LDA #%00110000                                      set current ACIA control register
                                                        8 bit word, 2 stop bits, RTS low
                                                        (this is the active state), transmit
                                                        interrupt enabled
    STA .tapeSendingFlag                                set non-zero value to indicate
                                                        sending to tape
    BNE .setACIA6850ControlRegister                     ALWAYS branch

§9. flipRelayOffAndOnThenSetToReadFromTape.

.flipRelayOffAndOnThenSetToReadFromTape = $fb50
    LDA #%00000101                                      set .serialULAControlRegister
    STA .serialULAControlRegister                       which sets transmit to 300 baud
                                                                  receive to 19200 baud
                                                                     tape system in use
                                                                              motor off
    LDX #$FF                                            loop counter
-
    DEX                                                 } delay loop
    BNE -                                               }
    STX .tapeSendingFlag                                .tapeSendingFlag = 0
    LDA #%10000101                                          transmit baud rate 300 baud
                                                                  receive to 19200 baud
                                                                     tape system in use
                                                                               motor on
    STA .serialULAControlRegister                       

    LDA #%11010000                                      A=%11010000
                                                        bit 7     - enable interrupts
                                                        bit 6/5   - RTS high, transmit
                                                                    interrupt disabled
                                                        bit 4/3/2 - 8 bit word, 2 stop bits
                                                        bit 1/0   - divide counter by 1
.setACIA6850ControlRegister = $fb63
    ORA .tapeBaudRate                                   set bits for baud rate:
                                                        5 = %101 means 1200 baud
                                                        (divide by 16; 1 stop bit)
                                                        6 = %110 means  300 baud
                                                        (divide by 64; 1 stop bit)
.storeAToACIARegister = $fb65
    STA .acia6850ControlRegister                        set up ACIA control register
    RTS                                                 

§10. incrementBlockNumbers.

.incrementBlockNumbers = $fb69
    LDX .fsBlockNumberLow                               block number
    LDY .fsBlockNumberHigh                              block number high
    INX                                                 X=X+1
    STX .currentBlockNumberLow                          current block number low
    BNE +                                               
    INY                                                 Y=Y+1
+
    STY .currentBlockNumberHigh                         current block number high
    RTS                                                 

§11. zeroChecksumAndFSFlag.

.zeroChecksumAndFSFlag = $fb78
    LDY #0                                              
    STY .fsGotACharacterToReadOrWriteFlag               clear 'just read character' flag
    fall through...

§12. setChecksumBytesToY.

 Set (zero) checksum bytes

 On Entry:
       Y = value to store (always zero in practice)
.setChecksumBytesToY = $fb7c
    STY .tapeChecksumLow                                
    STY .tapeChecksumHigh                               
    RTS                                                 

§13. Copy zero terminated string to .filenameToSearchFor.

 On Entry:
       X is offset from $301 for source string
.copyToSoughtFilename = $fb81
    LDY #$FF                                            Y=$FF
-
    INY                                                 Y=Y+1
    INX                                                 X=X+1
    LDA .vduVariablesStart,X                            
    STA .filenameToSearchFor,Y                          sought filename
    BNE -                                               until end of filename (0)
    RTS                                                 

§14. zeroFileHandleAndMotorOn.

.zeroFileHandleAndMotorOn = $fb8e
    LDY #0                                              Y=0
    fall through...

§15. storeFileHandleAndMotorOn.

.storeFileHandleAndMotorOn = $fb90
    CLI                                                 enable interrupts
    LDX #1                                              X=1 (relay on)
    STY .tapeCurrentFileHandle                          store Y as current file handle
    fall through...

§16. controlMotor.

 Control Tape Relay (On / Off)

 On Entry:
       X = 0 for relay off
       X = 1 for relay on
.controlMotor = $fb95
    LDA #137                                            do OSBYTE 137
    LDY .tapeCurrentFileHandle                          get back file handle (preserved
                                                        through OSBYTE)
    JMP .OSBYTE                                         turn on motor

§17. Check file is open.

 On Entry:
       A = 1 means check file is open for reading (check bit 0 of tape status byte)
       A = 2 means check file is open for writing (check bit 1 of tape status byte)
       A = 3 means check file is open for reading or writing
       Y = file handle ('channel')

 On Exit:
       If file is not open then a 'Channel' error occurs
.checkFileIsOpen = $fb9c
    STA .fsTempStorage                                  remember action type (1-3)
    TYA                                                 A=Y=channel:
                                                          1 = input file on tape
                                                          2 = output file on tape
                                                          3 = input file from ROMFS

    EOR .tapeOrROMSwitch                                EOR with filing system flag
    TAY                                                 Y=A
                                                        now, Y = 1 for input
                                                             Y = 2 for output
    LDA .fsStatusByte                                   tape / ROM FS status byte
    AND .fsTempStorage                                  file status or temporary store
    LSR                                                 set carry if open for input
    DEY                                                 Y = Y - 1
    BEQ +                                               if (checking open for input) then
                                                        branch
    LSR                                                 set carry if open for output
    DEY                                                 Y = Y - 1
    BNE .channelError                                   if (not checking open for output)
                                                        then branch
+
    BCS .exit36                                         if (file is open as expected) then
                                                        branch (exit)

.channelError = $fbb1
    BRK                                                 
    !byte $DE                                           error number
    !text "Channel"                                     message
    !byte 0                                             terminator

§18. startSendToSecondProcessor.

 See NAUG Section 18.8, Page 340
.startSendToSecondProcessor = $fbbb
    LDA #1                                              A=1 (Multiple single byte transfer:
                                                        main processor to second processor)
.secondProcessorTransfer = $fbbd
    JSR .checkLoadAddressIsForSecondProcessor           check if load address is valid
    BEQ .exit36                                         if (load address is for main
                                                        processor) then branch (exit)
    TXA                                                 A=1
    LDX #<.loadAddressLow                               }
    LDY #>.loadAddressLow                               } set up parameter block address
.secondProcessorCall = $fbc7
    PHA                                                 save A=1 on stack
    LDA #.tubeClaimReasonCode + .tubeCallerIDCassetteFS Claim the Tube, caller is the
                                                        Cassette Filing System
-
    JSR .tubeTransferData                               call out to Tube
    BCC -                                               keep trying to claim until claim is
                                                        done
    PLA                                                 pull A=1 (Multiple single byte
                                                        transfer: main processor to second
                                                        processor)
    JMP .tubeTransferData                               

§19. Check load address for destination.

 Check the top two bytes of the 32 bit load address. If they are both $FF, then it is for
 the main processor and we return with Z set. If the Tube is not present, then we also
 return with Z set. Otherwise it's for the Tube, we return with Z clear.

 On Exit:
       Z set if load address is for the second processor
       X is value of A on entry
.checkLoadAddressIsForSecondProcessor = $fbd3
    TAX                                                 X=A
    LDA .loadAddressMid2                                current load address high word
    AND .loadAddressHigh                                current load address high word
    CMP #$FF                                            
    BEQ +                                               if ($FF, i.e. it's for base
                                                        processor) then branch
    LDA .tubePresentFlag                                $FF if Tube is present
    AND #%10000000                                      Set bit 7 alone if Tube is present
+
    RTS                                                 

§20.

 This routine is actually a bugfix. Commented from the original source code:

 BUGFIX

 When the 6850 counter divide bits are reset, it is possible for the SERPROC to get out
 of synch. for a few bits. This has the effect of corrupting the first character of the
 first block of a SAVE, or the first character of ANY block during sequential access
 (since the 6850 is reset for every block during putbytes).

 The cure is to write a dummy byte to tape at the start of a SAVE, and at the start of
 every block during putbytes. This must be done by polling, since there must be a period
 of high-tone after the dummy byte, which is difficult to accomplish if the 6850 is
 interrupting all the time. BUGFIX is thus called after the SERPROC is set and before the
 6850 is set to interrupt during a block write operation.

 ["SERPROC" refers to .serialULAControlRegister, and "BUGFIX" is this routine]

§21. setupForCassetteWrite.

.setupForCassetteWrite = $fbe2
    LDA #%10000101                                      } switch on the cassette motor and
                                                        } relay
    STA .serialULAControlRegister                       } and set 300 baud
    JSR .resetACIA                                      reset ACIA
    LDA #%00010000                                      } even parity
                                                        } 2 stop bits
                                                        } 8 bit word
                                                        } 'Request To Send' ('RTS') low
                                                        } (active)
                                                        } disable interrupts
    JSR .setACIA6850ControlRegister                     set ACIA to tape filing system baud
                                                        rate

-
    JSR .checkForEscapeDuringCassetteOperation          check for ESCAPE
    LDA .acia6850StatusRegister                         read ACIA status register
    AND #%00000010                                      check bit 1 (transmit interrupt
                                                        generated)
    BEQ -                                               if (bit 1 clear, i.e. transmit
                                                        interrupt generated) then branch
                                                        (loop back and try again)

    LDA #%10101010                                      A=$AA
    STA .acia6850DataRegister                           send byte to tape
.exit36 = $fbfe
    RTS                                                 

    !byte 0                                             [unused]