tape.a65

     1:  TAPE_BUFFER_TYPE_BASIC          = $01
     2:  TAPE_BUFFER_TYPE_CONTINUATION   = $02
     3:  TAPE_BUFFER_TYPE_ABSOLUTE       = $03
     4:  TAPE_BUFFER_TYPE_DATA           = $04
     5:  TAPE_BUFFER_TYPE_EOT            = $05
     6:  
     7:  TAPE_BUFFER_OFFSET_TYPE         = $00
     8:  TAPE_BUFFER_OFFSET_SAL_LOW      = $01
     9:  TAPE_BUFFER_OFFSET_SAL_HIGH     = $02
    10:  TAPE_BUFFER_OFFSET_EAL_LOW      = $03
    11:  TAPE_BUFFER_OFFSET_EAL_HIGH     = $04
    12:  TAPE_BUFFER_OFFSET_FILENAME     = $05   ; start of file name in every tape buffer but TAPE_BUFFER_TYPE_CONTINUATION
    13:  TAPE_BUFFER_OFFSET_ENDFILENAME  = $15   ; one after the end of the file name in every tape buffer but TAPE_BUFFER_TYPE_CONTINUATION
    14:  
    15:  
    16:  TAPE_TIMER_CONSTANT_BIT0        := $60
    17:  TAPE_TIMER_CONSTANT_BIT1        := $B0
    18:  TAPE_TIMER_CONSTANT_PREAMBLE    := $78
    19:  TAPE_TIMER_CONSTANT_WRITE       := $0110
    20:  
    21:  TAPE_RIPRTY_69                  := $69
    22:  TAPE_RIPRTY_14                  := $14
    23:  
    24:  ; Define some aliases for understandability
    25:  
    26:  Pass1ReadErrors                 = zPTR1
    27:  Pass2ReadErrors                 = zPTR2
    28:  ReadCharacterError              = zRODATA
    29:  ReadCharacterIn                 = zROPRTY
    30:  WriteCharacterOut               = zROPRTY
    31:  NrBlocksRemaining               = zINBIT
    32:  ByteReceivedFlag                = zDPSW
    33:  SerialWordBuffer                = zMYCH
    34:  ReadBitSequenceErrors           = zRINONE
    35:  ErrorFlagOnTapeRead             = zBITC1
    36:  
    37:  
    38:  ; Tape buffer format on tape (cf. TapeCreateFileBuffer):
    39:  ; $00: type of block (cf. TAPE_BUFFER_TYPE_... constants above)
    40:  ;
    41:  ; if type of block == $01, $03, $04 or $05:
    42:  ; $01: start address low
    43:  ; $02: start address high
    44:  ; $03: end address low
    45:  ; $04: end address high
    46:  ; $05 - $14: name of file
    47:  ;
    48:  
    49:  
    50:  .if CompileComputer = C64_GS
    51:  
    52:          .include "../c64/c64gs.inc"
    53:  
    54:  .else ;  .if CompileComputer = C64_GS
    55:  
    56:  ; Find the next file on tape
    57:  ;
    58:  ; Output:
    59:  ;   C = 1 --> an error occurred
    60:  ;   C = 0 --> no error occurred
    61:  ;   Z = 1 --> EOT was found
    62:  ;   Z = 0 --> no EOT was found
    63:  ;   X = tape buffer type if no error occurred
    64:  ;
    65:  ; BUG:
    66:  ;   If the buffer could not be read at all, Z = 1 will be true if zVERCKK contains a 0.
    67:  ;   Thus, Z will not represent the status of the EOT.
    68:  ;
    69:  TapeReadTapeHeaderOfNextFile:
    70:  
    71:  @ReadNextHeader:
    72:          lda     zVERCKK                         ; remember verify (=1) or load (=0) flag
    73:          pha                                     ; onto stack
    74:  
    75:          jsr     TapeReadNextBuffer              ; get next buffer from tape
    76:  
    77:          pla
    78:          sta     zVERCKK                         ; restore verify (=1) or load (=0) flag
    79:  
    80:          bcs     @Rts                            ; if an error occurred, we are done
    81:  
    82:          ldy     #TAPE_BUFFER_OFFSET_TYPE        ; start reading buffer at tape buffer type byte
    83:          lda     (zTAPE1),y                      ; get tape buffer type byte into A
    84:  
    85:          cmp     #TAPE_BUFFER_TYPE_EOT           ; is it of type "end-of-tape, EOT"?
    86:          beq     @Rts                            ; yes, we are done
    87:  
    88:          cmp     #TAPE_BUFFER_TYPE_BASIC         ; is it of type "BASIC program"?
    89:          beq     @FoundFile                      ; yes -> branch, process it
    90:  
    91:          cmp     #TAPE_BUFFER_TYPE_ABSOLUTE      ; is it of type "ABSOLUTE loading program, i.e., machine language program"?
    92:          beq     @FoundFile                      ; yes -> branch, process it
    93:  
    94:          cmp     #TAPE_BUFFER_TYPE_DATA          ; is it of type "DATA file, that is, no program"?
    95:          bne     @ReadNextHeader                 ; no -> branch, get next tape buffer from tape
    96:  
    97:  @FoundFile:
    98:          tax                                     ; remember tape buffer type in X
    99:          bit     zNSGFLG                         ; kernal message output policy: do we want to output "Loading", "Saving", "Verifying", "Found", ... messages?
   100:          bpl     @ClcRts                         ; no --> skip output, return without an error
   101:  
   102:          ldy     #StrFound - LMESSAGES           ; offset of "FOUND " message
   103:          jsr     OutputMessage                   ; output it
   104:  
   105:  
   106:          ldy     #TAPE_BUFFER_OFFSET_FILENAME    ; offset of file name in buffer
   107:  
   108:  @OutputFilename:
   109:          lda     (zTAPE1),y                      ; get character from the file name
   110:          jsr     kCHROUT                         ; and output it (onto screen)
   111:          iny                                     ; proceed to next character
   112:          cpy     #TAPE_BUFFER_OFFSET_ENDFILENAME ; already at end of file name?
   113:          bne     @OutputFilename                 ; no -> branch, output next character
   114:  
   115:  .if CompileComputer >= C64_02
   116:  
   117:          ; introduce a delay so the user has the option to actually see the output
   118:          ; This is necessary on the C64, as it disables the VIC-II output on tape operations.
   119:          ; On the VIC-20, this is not the case, thus, the user can see the output anyway
   120:  
   121:          lda     zTIME + 1                       ; get the current time (middle byte)
   122:          jsr     TapeKeyOrTimeout                ; delay the output until a timeout happens, or the user presses a key
   123:          nop                                     ; fill-byte
   124:  
   125:  .elseif CompileComputer >= C64_GENERAL
   126:  
   127:          ; on the C64-01, the delay was until some key was pressed.
   128:          ; This is quite large and most probably the reason why the C64-02 KERNAL (cf. above)
   129:          ; introduced a timeout
   130:  
   131:  @WaitKeyPress:
   132:          lda     zSTKEY                          ; get the status of the keyboard column at the time the keyboard was checked the last time
   133:          cmp     #$FF                            ; no key pressed?
   134:          beq     @WaitKeyPress                   ; yes -> branch -> loop until a key is pressed
   135:  .endif
   136:  
   137:  @ClcRts:
   138:          clc
   139:          dey                                     ; put Y to point at the last byte of the filename
   140:                                                  ; (thus, a subsequent INY will point it to the next data byte)
   141:  @Rts:   rts
   142:  
   143:  
   144:  ; Create an empty tape buffer
   145:  ; Input: A = type of tape buffer (one of TAPE_BUFFER_TYPE_...)
   146:  ;
   147:  TapeCreateFileBuffer:
   148:          sta     zPTR1                           ; remember tape buffer type
   149:  
   150:          jsr     TapeGetPointer                  ; get pointer to tape buffer into (X/Y) (unused, but flags important)
   151:          bcc     @Rts                            ; C = 0 -> tape buffer points to stack page or zero page -> quit, we do not want to overwrite essential data!
   152:  
   153:          lda     zSTAL + 1                       ; remember start address high
   154:          pha
   155:          lda     zSTAL                           ; remember start address low
   156:          pha
   157:          lda     zEAL + 1                        ; remember end address high
   158:          pha
   159:          lda     zEAL                            ; remember end address low
   160:          pha
   161:  
   162:          ; Delete complete tape buffer, overwriting it with $20 (SPACE)
   163:          ldy     #lTBUFFR_SIZE - 1               ; size of buffer to erase
   164:          lda     #$20                            ; empty buffer pattern
   165:  :
   166:          sta     (zTAPE1),y
   167:          dey
   168:  bne     :-
   169:  
   170:          lda     zPTR1                           ; store tape buffer type
   171:          sta     (zTAPE1),y                      ; into position $00
   172:          iny
   173:  
   174:          lda     zSTAL                           ; store start address low
   175:          sta     (zTAPE1),y                      ; into position $01
   176:          iny
   177:  
   178:          lda     zSTAL + 1                       ; store start address high
   179:          sta     (zTAPE1),y                      ; into position $02
   180:          iny
   181:  
   182:          lda     zEAL                            ; store end address low
   183:          sta     (zTAPE1),y                      ; into position $03
   184:          iny
   185:  
   186:          lda     zEAL + 1                        ; store end address high
   187:          sta     (zTAPE1),y                      ; into position $04
   188:          iny
   189:  
   190:          sty     zPTR2                           ; remember write pointer
   191:  
   192:          ; store file name into buffer
   193:  
   194:          ldy     #0                              ; pointer into file name
   195:          sty     zPTR1
   196:  
   197:  @CopyFilename:
   198:          ldy     zPTR1                           ; check if we reached end of file name
   199:          cpy     zFNLEN
   200:          beq     @CopyFilenameQuit               ; yes -> quit loop
   201:  
   202:          lda     (zFNADR),y                      ; get next file name character
   203:          ldy     zPTR2                           ; (write pointer into tape buffer)
   204:          sta     (zTAPE1),y                      ; store file name character into tape buffer
   205:  
   206:          inc     zPTR1                           ; increment file name read pointer
   207:          inc     zPTR2                           ; increment tape write pointer
   208:          bne     @CopyFilename                   ; (unconditional)
   209:          ; -----------------------
   210:  
   211:  @CopyFilenameQuit:
   212:          jsr     TapeSetStartAndEndToBuffer      ; set zSTAL/zSTAL + 1 to begin of tape buffer, and zEAL/zEAL+1 to end of it
   213:  
   214:          lda     #TAPE_RIPRTY_69
   215:          sta     zRIPRTY                         ; Write TAPE_RIPRTY_69 * $100 pulses for the preamble
   216:  
   217:          jsr     TapeWriteCompleteFileCustomPreamble     ; write buffer onto tape, using the set preamble length
   218:  
   219:          tay                                     ; remember A in Y (TODO why? What content does A have here?)
   220:  
   221:          ; restore start adn end addresses
   222:          pla                                     ; restore end address low
   223:          sta     zEAL
   224:          pla                                     ; restore end address high
   225:          sta     zEAL + 1
   226:          pla                                     ; restore start address low
   227:          sta     zSTAL
   228:          pla                                     ; restore start address high
   229:          sta     zSTAL + 1
   230:  
   231:          tya                                     ; restore A from Y (TODO why? What content does A have here?)
   232:  @Rts:
   233:          rts
   234:  
   235:  ; Get tape buffer pointer into X/Y
   236:  ;
   237:  ; Return:
   238:  ; C = 0 if tape buffer points into stack page or zero page
   239:  ; C = 1 if not
   240:  ;
   241:  TapeGetPointer:
   242:          ldx     zTAPE1
   243:          ldy     zTAPE1 + 1
   244:          cpy     #>lBUF
   245:          rts
   246:  
   247:  ; set zSTAL/zSTAL + 1 to begin of tape buffer, and zEAL/zEAL+1 to end of it
   248:  
   249:  TapeSetStartAndEndToBuffer:
   250:          jsr     TapeGetPointer                  ; get tape buffer pointer into x/y
   251:  
   252:          txa                                     ; get address low
   253:          sta     zSTAL                           ; and store it as start address low
   254:  
   255:          clc
   256:          adc     #lTBUFFR_SIZE                   ; add size of tape buffer
   257:          sta     zEAL                            ; and store it as end address
   258:  
   259:          tya                                     ; get address high
   260:          sta     zSTAL + 1                       ; and store it as end address high
   261:          adc     #0                              ; add carry from previous addition
   262:          sta     zEAL + 1                        ; store the result as end address high
   263:          rts
   264:          ; -----------------
   265:  
   266:  
   267:  ; Find a specific file on tape
   268:  ;
   269:  ; Input:
   270:  ;   zFNLEN:          Length of the file name
   271:  ;   zFNADR/zFNADR+1: Pointer to file name (if zFNLEN != 0)
   272:  ;
   273:  ; Output:
   274:  ;   C = 1 --> an error occurred
   275:  ;   C = 0 --> no error occurred
   276:  ;   Z = 1 --> EOT was found
   277:  ;   Z = 0 --> no EOT was found
   278:  ;   X = tape buffer type if no error occurred
   279:  ;
   280:  ; BUG:
   281:  ;   If the buffer could not be read at all, Z = 1 will be true if zVERCKK contains a 0.
   282:  ;   Thus, Z will not represent the status of the EOT.
   283:  ;
   284:  TapeFindSpecificFile:
   285:  
   286:  @NextFile:
   287:          jsr     TapeReadTapeHeaderOfNextFile    ; read in the next header on tape
   288:          bcs     @Rts                            ; C == 1 --> an error occurred --> branch --> quit (This will also happen if Z=1)
   289:  
   290:          ldy     #TAPE_BUFFER_OFFSET_FILENAME    ; offset of file name in tape buffer
   291:          sty     zPTR2
   292:  
   293:          ldy     #0                              ; offset of requested file name
   294:          sty     zPTR1
   295:  
   296:  @TestNextChar:
   297:          cpy     zFNLEN                          ; check if we reached the end of the file name
   298:          beq     @ClcRts                         ; yes -> branch, return with c = 0
   299:  
   300:          lda     (zFNADR),y                      ; read in character from requested file name
   301:          ldy     zPTR2
   302:          cmp     (zTAPE1),y                      ; compare with character from file name in tape buffer
   303:          bne     @NextFile                       ; not equal -> file name differes -> branch -> check next file on tape
   304:  
   305:          inc     zPTR1                           ; increment pointers for file name characters
   306:          inc     zPTR2
   307:  
   308:          ldy     zPTR1                           ; make sure Y has the offset of the current character of the requested file name
   309:          bne     @TestNextChar                   ; uncond. branch
   310:          ; ----------------------
   311:  
   312:  @ClcRts:
   313:          clc                                     ; mark: no error (i.e., we found the requested file)
   314:  @Rts:
   315:          rts
   316:          ; -------------------
   317:  
   318:  ; increment the pointer into the tape buffer
   319:  ;
   320:  ; Return:
   321:  ; Y = the tape pointer
   322:  ;
   323:  ; Z = 1 if the buffer is full / completely read
   324:  ; Z = 0 otherwise
   325:  ;
   326:  TAPE_INCREMENT_WRITE_POINTER:
   327:          jsr     TapeGetPointer
   328:          inc     zBUFPNT                 ; increment the pointer into the tape buffer
   329:          ldy     zBUFPNT                 ; read i
   330:          cpy     #lTBUFFR_SIZE
   331:          rts
   332:  
   333:  ; Wait for the PLAY key to be pressed
   334:  ; If it is not, output "PRESS PLAY ON TAPE" and wait for it to be pressed
   335:  ;
   336:  TapePressPlayOnTape:
   337:          jsr     TapeCheckPlayPressed    ; check if play key is pressed
   338:          beq     TapeClcRts              ; yes -> branch, we're done
   339:  
   340:          ldy     #StrPlay - LMESSAGES    ; get offset of "PRESS PLAY ON TAPE" string to output the text
   341:  
   342:  TapePressPlayCommon:
   343:  
   344:  .if CompileComputer = C64_4064
   345:          jsr     OutputMessageIfAllowed  ; output the text, only if allowed
   346:  .else
   347:          jsr     OutputMessage           ; output the text
   348:  .endif
   349:  
   350:          ; Wait for PLAY key to be pressed
   351:  
   352:  @WaitForPlay:
   353:          jsr     TapeCheckForStop        ; check if stop key was pressed. If so, do not return here, but to our caller!
   354:  
   355:          jsr     TapeCheckPlayPressed    ; has PLAY been pressed on the tape?
   356:          bne     @WaitForPlay            ; no -> branch --> test again
   357:  
   358:          ldy     #StrOk - LMESSAGES      ; get offset of "OK" text to output it
   359:  
   360:  .if CompileComputer = C64_4064
   361:          jmp     OutputMessageIfAllowed  ; output the text, only if allowed
   362:          ; ------------------------------
   363:  .else
   364:          jmp     OutputMessage           ; output the text
   365:          ; ------------------------------
   366:  .endif
   367:  
   368:  ; Find out if the PLAY key
   369:  ; is pressed on the tape
   370:  ;
   371:  ; This includes the combination of RECORD + PLAY
   372:  ;
   373:  ; Output:
   374:  ; Z = 0: not pressed
   375:  ; Z = 1: pressed
   376:  ;
   377:  TapeCheckPlayPressed:
   378:          lda     #TAPE_B_SENSE
   379:          bit     TAPE_REG_SENSE
   380:          bne     TapeClcRts
   381:          bit     TAPE_REG_SENSE
   382:  
   383:  TapeClcRts:
   384:          clc
   385:          rts
   386:  
   387:  ; Wait for the PLAY key to be pressed
   388:  ; If it is not, output "PRESS RECORD & PLAY ON TAPE" and wait for it to be pressed
   389:  
   390:  TapePressRecordAndPlayOnTape:
   391:          jsr     TapeCheckPlayPressed    ; check if play key is pressed
   392:          beq     TapeClcRts              ; yes -> branch, we're done
   393:  
   394:          ldy     #StrRecPlay - LMESSAGES ; get offset of "PRESS RECORD & PLAY ON TAPE" string to output the text
   395:  
   396:          bne     TapePressPlayCommon     ; (other than the text, this routine is identical to TapePressPlayOnTape. Thus, handle it over there)
   397:          ; ---------------------------
   398:  
   399:  
   400:  TapeReadNextBuffer:
   401:          lda     #0
   402:          sta     zSTATUS                         ; clear status, no error yet
   403:  
   404:          sta     zVERCKK                         ; mark: we are loading (not verifying) from tape
   405:  
   406:          jsr     TapeSetStartAndEndToBuffer      ; set zSTAL/zSTAL + 1 to begin of tape buffer, and zEAL/zEAL+1 to end of it
   407:  
   408:  TapeReadFileContents:
   409:          jsr     TapePressPlayOnTape             ; output "PRESS PLAY ON TAPE" and wait for the PLAY key to be pressed
   410:          bcs     LF86E                           ; C = 1 --> an error occurred (STOP was pressed) --> branch --> quit (this address is used as a trampoline, as the real target is too far away)
   411:  
   412:          sei
   413:  
   414:          ; clear some essential variables
   415:  
   416:          lda     #$00
   417:          sta     zRIDATA
   418:          sta     zBITTS
   419:          sta     zCMPO
   420:          sta     zPTR1                           ; or Pass1ReadErrors
   421:          sta     zPTR2                           ; or Pass2ReadErrors
   422:          sta     zDPSW                           ; or ByteReceivedFlag
   423:  
   424:          lda     #TAPE_REG_ICR_B_CASSREAD
   425:          ldx     #(VecTapeIrqRead - TapeIrqVectors) + 8  ; IRQ vector number to be set: Reading from tape
   426:          bne     LF875                           ; everything but the IRQ vector is identical to recording, thus, use the code there
   427:          ; ------------------------------
   428:  
   429:  ; write out the tape buffer to tape
   430:  ; TODO
   431:  
   432:  TapeWriteCompleteBuffer:
   433:          jsr     TapeSetStartAndEndToBuffer              ; set zSTAL/zSTAL + 1 to begin of tape buffer, and zEAL/zEAL+1 to end of it
   434:  
   435:  TapeWriteCompleteFile:
   436:          lda     #TAPE_RIPRTY_14
   437:          sta     zRIPRTY                                 ; Write TAPE_RIPRTY_14 * TODO pulses for the preamble
   438:  TapeWriteCompleteFileCustomPreamble:
   439:          jsr     TapePressRecordAndPlayOnTape            ; output "PRESS RECORD & PLAY ON TAPE" and wait for the PLAY key to be pressed
   440:  LF86E:
   441:          bcs     TapeSave_ClearIRQtmp_and_RTS            ; C = 1 --> an error occurred (STOP was pressed) --> branch, quit
   442:  
   443:          sei
   444:  
   445:          lda     #TAPE_REG_ICR_B_WR_TIMER                ; start the write timer after setting the IRQ vector
   446:          ldx     #(VecTapeIrqWritePreamble - TapeIrqVectors) + 8 ; IRQ vector number to be set
   447:  
   448:  LF875:
   449:          ldy     #TAPE_REG_ICR_B_CLEARALL                ; mask: clear all interrupt sources
   450:          sty     TAPE_REG_ICR                            ; clear all interrupt sources
   451:  
   452:          sta     TAPE_REG_ICR                            ; and set the needed interrupt source (given in A)
   453:  
   454:  .if CompileComputer >= C64_GENERAL
   455:          ; TODO: what?
   456:          lda     CIA1 + CIA_O_CRA
   457:          ora     #CIA_CRB_B_FORCE_LOAD | CIA_CRB_B_ONESHOT | CIA_CRB_B_START
   458:          sta     CIA1 + CIA_O_CRB
   459:          and     #CIA_CRA_B_50HZ | CIA_CRA_B_FORCE_LOAD | CIA_CRA_B_START
   460:          sta     lTODSNS
   461:  .endif
   462:  
   463:          jsr     LF0A4                                   ; TODO
   464:  
   465:  .if CompileComputer >= C64_GENERAL
   466:          ; switch off display
   467:          lda     VIC + VICII_O_ControlReg1
   468:          and     # ~ VICII_B_ControlReg1_DEN
   469:          sta     VIC + VICII_O_ControlReg1
   470:  .endif
   471:  
   472:          ; save IRQ vector in order to be able to restore it after the tape operation
   473:          lda     lCINV                                   ; IRQ vector low byte
   474:          sta     lIRQTMP
   475:          lda     lCINV + 1                               ; IRQ vector high byte
   476:          sta     lIRQTMP + 1
   477:  
   478:          jsr     TapeSetIrqVector
   479:  
   480:          lda     #$02
   481:          sta     zFSBLK                                  ; set number of copies to write = 2.
   482:  
   483:          jsr     TapeInitInputOutputOfByte               ; prepare output of a byte by initialising zTSFCNT, zTBTCNT, zBITC1, zPRTY and zRINONE
   484:  
   485:  
   486:          ; switch on the tape motor
   487:          lda     TAPE_REG_MOTOR
   488:          and     #TAPE_B_MOTOR_OFF_AND
   489:  .if CompileComputer < C64_GENERAL
   490:          ora     #TAPE_B_MOTOR_OFF_OR
   491:  .endif
   492:          sta     TAPE_REG_MOTOR
   493:  
   494:          sta     zCAS1                                   ; mark: tape operation in progress.
   495:  
   496:          ; delay of TODO ms to allow tape motor for stable operation
   497:          ldx     #$FF
   498:  @LF8B5:
   499:          ldy     #$FF
   500:  @LF8B7:
   501:          dey
   502:          bne     @LF8B7
   503:          dex
   504:          bne     @LF8B5
   505:  
   506:  .if CompileComputer < C64_GENERAL
   507:          sta     TAPE_TIMER1_HI                          ; TODO why?
   508:  .endif
   509:          cli                                             ; allow interrupts: From now on, writing to the tape is controlled by the IRQ routine only!
   510:  
   511:  ; Wait for the recording to have quit. For this, we compare the IRQ vector. When it is restored to be original vector
   512:  ; stored in lIRQTMP/lIRQTMP+1, then the recording has finished.
   513:  ;
   514:  ; Also check for STOP, as the user might want to stop the recording prematurely.
   515:  ;
   516:  @WaitForFinish:
   517:          lda     lIRQTMP + 1                             ; compare stored IRQ vector high
   518:          cmp     lCINV + 1                               ; with current IRQ vector high
   519:          clc                                             ; (in case we finish the loop: mark "no error")
   520:          beq     TapeSave_ClearIRQtmp_and_RTS            ; if they are the same, the recording has finished -> quit this loop
   521:  
   522:          jsr     TapeCheckForStop                        ; check if stop key was pressed. If so, do not return here, but to our caller!
   523:  
   524:  ; (physically) check if RUN/STOP has been pressed
   525:  
   526:  .if CompileComputer >= C64_GENERAL
   527:          ; on the C64, we just check for RUN/STOP. Note that the time is not updated while storing to TAPE
   528:          jsr     iUDTIM_CheckRunStop
   529:  .else
   530:          lda     VIA2_IFR                                ; check interrupt flag register: Would T1 (the ticker) generate an event?
   531:          and     #VIA_IFR_B_T1
   532:          beq     @WaitForFinish                          ; no -> loop
   533:          lda     VIA1_T1CL                               ; clear the IFR state by reading T1
   534:          jsr     iUDTIM                                  ; update time. This function also checks for RUN/STOP
   535:  .endif
   536:          jmp     @WaitForFinish                          ; loop, wait for finishing
   537:          ; ------------------
   538:  
   539:  ; Check for stop key
   540:  ;
   541:  ; This function checks if the STOP key has been pressed.
   542:  ; If so, it does not return to its caller, but to the caller of its caller!
   543:  ;
   544:  TapeCheckForStop:
   545:          jsr     kSTOP                                   ; check for stop key (returns: Z = 1 <--> STOP key was pressed)
   546:          clc
   547:          bne     TapeSaveRts                                     ; no stop key -> branch, we're done.
   548:  
   549:          jsr     TapeStopMotor_and_InitTimer             ; stop cass. motor, restore timer, restore IRQ vector
   550:          sec                                             ; mark: an error occurred
   551:  
   552:          ; remove return address of our caller from the stack
   553:          pla
   554:          pla
   555:  
   556:  TapeSave_ClearIRQtmp_and_RTS:
   557:          ; mark: we do not have an IRQ vector to restore
   558:          lda     #$00
   559:          sta     lIRQTMP + 1             ; high byte of IRQ vector to restore (if any)
   560:  
   561:  TapeSaveRts:
   562:          rts
   563:          ; ------------------------
   564:  
   565:  
   566:  
   567:  
   568:  
   569:  TapeBitTimingAdjust:
   570:  ; TODO what does this function do?
   571:  ; Set expected tape timing. (according to some ROM description list)
   572:  
   573:          stx     zCMPO + 1
   574:  
   575:          ; Calculate: A := zCMPO * 5
   576:          lda     zCMPO
   577:          asl     a                                       ; multiply by 2
   578:          asl     a                                       ; multiply by 2
   579:          clc
   580:          adc     zCMPO                                   ; add old value of zCMPO
   581:  
   582:          ; calculate: [zCMPO+1] := [zCMPO+1] + zCMPO * 5
   583:          clc
   584:          adc     zCMPO + 1
   585:          sta     zCMPO + 1
   586:  
   587:          lda     #$00
   588:          bit     zCMPO
   589:          bmi     @LF8F7
   590:          rol     a
   591:  @LF8F7:
   592:          asl     zCMPO + 1
   593:          rol     a
   594:          asl     zCMPO + 1
   595:          rol     a
   596:          tax
   597:  
   598:  @LF8FE:
   599:          lda     TAPE_TIMER1_LO
   600:          cmp     #TAPE_TIMER1_CONST
   601:          bcc     @LF8FE
   602:          adc     zCMPO + 1
   603:          sta     TAPE_TIMER2_LO
   604:          txa
   605:          adc     TAPE_TIMER1_HI
   606:          sta     TAPE_TIMER2_HI
   607:  
   608:  .if CompileComputer >= C64_GENERAL
   609:          lda     lTODSNS
   610:          sta     CIA1 + CIA_O_CRA
   611:          sta     lTD1IRQ
   612:          lda     CIA1 + CIA_O_ICR
   613:          and     #CIA_ICR_B_FLAG
   614:          beq     @RetCli
   615:          lda     #>(@Ret-1)
   616:          pha
   617:          lda     #<(@Ret-1)
   618:          pha
   619:          jmp     FakeIRQ
   620:  .endif
   621:  
   622:  @RetCli:
   623:          cli
   624:  @Ret:
   625:          rts
   626:  
   627:  ;******************************************************************************
   628:  ;
   629:  ;; [[According to "The almost completely commented Vic 20 ROM disassembly.
   630:  ;;   V1.01 Lee Davison 2005-2012, amended by Ruud Baltissen; I relabeled the
   631:  ;;   cases from A B C D to X S M L:]]
   632:  ;;
   633:  ;;      On Commodore computers, the streams consist of four kinds of symbols
   634:  ;;      that denote different kinds of low-to-high-to-low transitions on the
   635:  ;;      read or write signals of the Commodore cassette interface.
   636:  ;;
   637:  ;;      X       A break in the communications, or a pulse with very long cycle
   638:  ;;              time.
   639:  ;;
   640:  ;;      S       A short pulse, whose cycle time typically ranges from 296 to 424
   641:  ;;              microseconds, depending on the computer model.
   642:  ;;
   643:  ;;      M       A medium-length pulse, whose cycle time typically ranges from
   644:  ;;              440 to 576 microseconds, depending on the computer model.
   645:  ;;
   646:  ;;      L       A long pulse, whose cycle time typically ranges from 600 to 744
   647:  ;;              microseconds, depending on the computer model.
   648:  
   649:  ;; [[According to the text in `Programming the PET/CBM' by Raeto West:]]
   650:  ;; A byte is stored as a byte marker (a long wave followed by a medium wave: L-M)
   651:  ;; followed by 9 bits: 8 data plus odd parity.
   652:  ;; A 0-bit is short followed by long (S-L)
   653:  ;; A 1-bit is long followed by short (L-S)
   654:  ;; [[but the diagram on page -236- agrees with the below:]]
   655:  
   656:  ;; [[According to Keith Falkner in Compute! Issue #008, Jan 1981:]]
   657:  ;; A 0-bit is short followed by medium (S-M)
   658:  ;; A 1-bit is medium followed by short (M-S)
   659:  
   660:  ;; [[According to the VICE source code, tape/tap.c:]]
   661:  ;; The tape preamble (leader) consists of at least 32 short pulses.
   662:  
   663:  ;; The tape preamble (leader) ends with the data bytes 89, 88, 87, .. 81,
   664:  ;; or for the second copy 09, 08, 07, .. 01.
   665:  ;;
   666:  ;; A tape block is ended with L-S (instead of L-M for the next byte).
   667:  
   668:  ; read tape bits, IRQ routine
   669:  
   670:  ; read T2C which has been counting down from $FFFF. subtract this from $FFFF
   671:  
   672:  TapeIrqRead:
   673:          ; IRQ4
   674:          ; read out the timer value making sure that we do not read
   675:          ; while a underflow occurs from the low byte to the high byte
   676:  
   677:          ldx     TAPE_TIMER1_HI                  ; read in timer high byte
   678:  
   679:          ; calculate $FF - timer low byte
   680:          ldy     #$FF
   681:          tya
   682:          sbc     TAPE_TIMER1_LO
   683:  
   684:          cpx     TAPE_TIMER1_HI                  ; is the high byte still the same as above?
   685:  
   686:          bne     TapeIrqRead                     ; no -> we just had an underflow -> retry
   687:  
   688:          stx     zCMPO + 1                       ; remember high byte of timer value
   689:  
   690:          tax                                     ; X := $FF - low byte of timer value as calculated above
   691:  
   692:          ; restart timer with $FFFF (longest possible timer start address)
   693:  
   694:          sty     TAPE_TIMER1_LO
   695:          sty     TAPE_TIMER1_HI
   696:  
   697:  .if CompileComputer >= C64_GENERAL
   698:  
   699:          ; restart the timer in oneshot mode
   700:  
   701:          lda     #CIA_CRB_B_FORCE_LOAD | CIA_CRB_B_ONESHOT | CIA_CRB_B_START
   702:          sta     CIA1 + CIA_O_CRB
   703:  
   704:          ; read the ICR (the read clears it) and store it in lTRDTMP
   705:  
   706:          lda     TAPE_REG_ICR
   707:          sta     lTRDTMP
   708:  
   709:  .endif
   710:  
   711:          tya                                     ; now, calculate $FF - high byte of timer value
   712:          sbc     zCMPO + 1
   713:          stx     zCMPO + 1                       ; here, zCMPO+1 / A contains ($FFFF - timer value)
   714:  
   715:          ; divide the timer value by 4
   716:  
   717:          lsr     a                               ; shift right, dividing by 2 (hi)
   718:          ror     zCMPO + 1                       ;                            (lo)
   719:          lsr     a                               ; shift right, deviding by 2 another time (hi)
   720:          ror     zCMPO + 1                       ; ==> time of pulse / 4                   (lo)
   721:  
   722:          lda     zCMPO                           ; get tape timing constant min byte
   723:          clc
   724:          adc     #$3C
   725:  
   726:  .if CompileComputer < C64_GENERAL
   727:          bit     VIA2_PA
   728:  .endif
   729:  
   730:          cmp     zCMPO + 1                       ; compare with time of pulse / 4
   731:                                                  ; compare with ($FFFF - TAPE_TIMER1) >> 2
   732:          bcs     @LF9AC                          ; branch if min + $3C >= ($FFFF - T2C) >> 2
   733:                                                  ; or in other words, if the pulse is too short.
   734:          ldx     ByteReceivedFlag                ; or zDPSW
   735:          beq     @LF969                          ; no byte received yet
   736:          jmp     LFA60                           ; byte received
   737:          ; -------------------
   738:  
   739:  @LF969:
   740:          ldx     zTSFCNT                         ; bit counter (8->0)
   741:          bmi     @LF988 ; -> @LFA10
   742:  
   743:          ; ? Determine if the pulse is short, medium or long.
   744:  
   745:          ldx     #$00                            ; data bit may be 0
   746:          adc     #$30
   747:          adc     zCMPO                           ; add tape timing constant min byte
   748:          cmp     zCMPO + 1                       ; compare with time of pulse / 4
   749:          bcs     @LF993                          ; Is the pulse smaller? -> It is a SHORT pulse
   750:          inx                                     ; data bit may be 1
   751:          adc     #$26
   752:          adc     zCMPO                           ; add tape timing constant min byte
   753:          cmp     zCMPO + 1                       ; compare with time of pulse / 4
   754:          bcs     @LF997                          ; Is the pulse still smaller? -> it is a MEDIUM pulse
   755:          adc     #$2C
   756:          adc     zCMPO                           ; add tape timing constant min byte
   757:          cmp     zCMPO + 1                       ; compare with time of pulse / 4
   758:          bcc     @LF98B                          ; pulse is too long
   759:  @LF988:
   760:          jmp     @LFA10                          ; Is the pulse smaller than the max long pulse?
   761:                                                  ; -> it is a LONG pulse
   762:          ; -----------------
   763:  
   764:  @LF98B:                                         ; pulse is too long
   765:          lda     zBITTS                          ; ? get EOI flag byte
   766:          beq     @LF9AC ; -> IrqEnd1
   767:          sta     ErrorFlagOnTapeRead             ; or zBITC1     store non-zero
   768:          bne     @LF9AC  ; always
   769:  
   770:                                                  ; After a short pulse we need a medium one,
   771:                                                  ; or the other way around. That keeps the counter on 0.
   772:  @LF993:                                         ; pulse was SHORT
   773:          inc     ReadBitSequenceErrors           ; or zRINONE
   774:          bcs     @LF999 ; always
   775:  @LF997:                                         ; pulse was MEDIUM
   776:          dec     ReadBitSequenceErrors           ; or zRINONE
   777:  @LF999:
   778:          sec                                     ; ?? adjust some timing parameter
   779:          sbc     #$13
   780:          sbc     zCMPO + 1                       ; subtract time of pulse / 4
   781:          adc     zSVXT
   782:          sta     zSVXT
   783:  
   784:          lda     zTBTCNT                         ; cycle counter (which half of the bit cycle is current)
   785:          eor     #$01
   786:          sta     zTBTCNT                         ; cycle counter
   787:          beq     @LF9D5                          ; wrong half of the bit cycle? i.e. 2nd pulse
   788:  
   789:          stx     zSCHAR                          ; distilled a bit (from ldx #$00 / inx above)
   790:                                                  ; takes the timing from the 1st pulse
   791:  
   792:  @LF9AC:                                         ; don't store a bit
   793:          lda     zBITTS                          ; ? get EOI flag byte
   794:          beq     @IrqEnd1
   795:  .if CompileComputer >= C64_GENERAL
   796:          lda     lTRDTMP                         ; saved TAPE_REG_ICR
   797:          and     #$01
   798:          bne     @LF9BC                          ; timer had no interrupt
   799:          lda     lTD1IRQ
   800:          bne     @IrqEnd1
   801:  .else
   802:          bit     VIA2_IFR
   803:          bvc     @IrqEnd1                        ; timer 1 no interrupt
   804:  .endif
   805:  
   806:  @LF9BC:                                         ; force restart for next bit
   807:          lda     #$00
   808:          sta     zTBTCNT                         ; cycle counter; there are 2 pulses per bit
   809:  .if CompileComputer >= C64_GENERAL
   810:          sta     lTD1IRQ
   811:  .endif
   812:          lda     zTSFCNT                         ; bit counter (8->0)
   813:          bpl     @LF9F7
   814:          bmi     @LF988
   815:  
   816:  @LF9C9:
   817:          ldx     #$A6
   818:          jsr     TapeBitTimingAdjust
   819:          lda     zPRTY                           ; parity
   820:          bne     @LF98B
   821:  @IrqEnd1:
   822:          jmp     NMI_End
   823:          ; ----------------------
   824:  
   825:  @LF9D5:
   826:          lda     zSVXT
   827:          beq     @LF9E0
   828:          bmi     @LF9DE
   829:          dec     zCMPO
   830:          .byte   ASM_BIT3
   831:  @LF9DE:
   832:          inc     zCMPO
   833:  @LF9E0:
   834:          lda     #$00
   835:          sta     zSVXT
   836:          cpx     zSCHAR
   837:          bne     @LF9F7
   838:          txa
   839:          bne     @LF98B
   840:          lda     ReadBitSequenceErrors           ; or zRINONE
   841:          bmi     @LF9AC
   842:          cmp     #$10
   843:          bcc     @LF9AC
   844:          sta     zSYNO
   845:          bcs     @LF9AC
   846:  @LF9F7:
   847:          txa
   848:          eor     zPRTY
   849:          sta     zPRTY
   850:          lda     zBITTS
   851:          beq     @IrqEnd1
   852:          dec     zTSFCNT                         ; bit counter (8->0)
   853:          bmi     @LF9C9
   854:  
   855:          lsr     zSCHAR                          ; shift a bit into the collected byte
   856:          ror     SerialWordBuffer                ; or zMYCH
   857:          ldx     #$DA
   858:          jsr     TapeBitTimingAdjust
   859:          jmp     NMI_End
   860:          ; ----------------------
   861:  
   862:  @LFA10:                                         ; found a LONG pulse -or- no more bits
   863:          lda     zSYNO
   864:          beq     @LFA18
   865:          lda     zBITTS
   866:          beq     @LFA1F
   867:  
   868:  @LFA18:
   869:          lda     zTSFCNT                         ; bit counter (8->0)
   870:  .if CompileComputer >= C64_GENERAL
   871:          bmi     @LFA1F
   872:          jmp     @LF997
   873:  .else
   874:          bpl     @LF997
   875:  .endif
   876:  
   877:  @LFA1F:
   878:          lsr     zCMPO + 1
   879:          lda     #$93
   880:          sec
   881:          sbc     zCMPO + 1
   882:          adc     zCMPO
   883:          asl     a
   884:          tax
   885:  
   886:          jsr     TapeBitTimingAdjust
   887:          inc     ByteReceivedFlag                ; or zDPSW
   888:          lda     zBITTS
   889:          bne     @LFA44
   890:          lda     zSYNO
   891:          beq     @IrqEnd2
   892:          sta     ErrorFlagOnTapeRead             ; or zBITC1
   893:          lda     #$00
   894:          sta     zSYNO
   895:  
   896:          lda     #TAPE_REG_ICR_B_SET_3
   897:          sta     TAPE_REG_ICR
   898:  
   899:          sta     zBITTS
   900:  
   901:  @LFA44:
   902:          lda     zSYNO
   903:          sta     zNXTBIT                         ; ?? indicate whether we got a bit
   904:          beq     @LFA53
   905:          lda     #$00
   906:          sta     zBITTS
   907:  
   908:          lda     #TAPE_REG_ICR_B_UNSET_3
   909:          sta     TAPE_REG_ICR
   910:  
   911:  @LFA53:
   912:          lda     SerialWordBuffer                ; or zMYCH
   913:          sta     ReadCharacterIn                 ; or zROPRTY
   914:          lda     ErrorFlagOnTapeRead             ; or zBITC1
   915:          ora     ReadBitSequenceErrors           ; or zRINONE
   916:          sta     ReadCharacterError              ; or zRODATA
   917:  @IrqEnd2:
   918:          jmp     NMI_End
   919:          ; --------------------
   920:  
   921:  
   922:          ; store tape chars      ; FA57 in PET 3032
   923:  
   924:  LFA60:                                          ; byte received
   925:          jsr     TapeInitInputOutputOfByte       ; prepare input of a byte by initialising zTSFCNT, zTBTCNT, zBITC1, zPRTY and zRINONE; returns with A = 0
   926:          sta     ByteReceivedFlag                ; zDPSW := 0
   927:  
   928:          ldx     #$DA
   929:          jsr     TapeBitTimingAdjust
   930:          lda     zFSBLK                          ; number of blocks remaining to read
   931:          beq     @LFA70                          ; if pass 1 was error free, don't really
   932:          sta     NrBlocksRemaining               ; bother with the second pass ; or zINBIT
   933:  @LFA70:
   934:          lda     #$0F
   935:          bit     zRIDATA                         ; 00=scan, $01-$0F=count, $40=load, $80=End of Tape marker
   936:          bpl     @LFA8D
   937:                                                  ; ---- $80 = EOT
   938:          lda     zNXTBIT                         ; ?? did we get a bit?
   939:          bne     @LFA86
   940:  
   941:          ldx     zFSBLK                          ; nr of copies remaining to read
   942:          dex
   943:          bne     @IrqEnd3
   944:          lda     #STATUS_TAPE_LONG_BLOCK
   945:          jsr     SetStatus
   946:          bne     @IrqEnd3
   947:  
   948:  @LFA86:
   949:          lda     #$00
   950:          sta     zRIDATA                         ; switch to scan
   951:  @IrqEnd3:
   952:          jmp     NMI_End
   953:          ; ------------------
   954:  
   955:  @LFA8D:
   956:          bvs     @LFAC0
   957:          bne     @LFAA9
   958:  
   959:          lda     zNXTBIT                         ; ---- 00 = scan
   960:          bne     @IrqEnd3                        ; ?? if we got a bit -> done
   961:  
   962:          lda     ReadCharacterError              ; or zRODATA
   963:          bne     @IrqEnd3                        ; error? -> done
   964:          lda     NrBlocksRemaining               ; or zINBIT
   965:          lsr     a
   966:          lda     ReadCharacterIn                 ; or zROPRTY
   967:          bmi     @LFAA3
   968:          bcc     @LFABA                          ; ?? 0 or 2 blocks remaining ; switch to EOT
   969:          clc
   970:  @LFAA3:
   971:          bcs     @LFABA                          ; switch to EOT
   972:          and     #$0F
   973:          sta     zRIDATA                         ; switch to count
   974:  
   975:  @LFAA9:                                         ; ---- 01-0F = count
   976:          dec     zRIDATA                         ; count down 1
   977:          bne     @IrqEnd3
   978:          lda     #$40                            ; when we have reached 00,
   979:          sta     zRIDATA                         ; switch to load
   980:          jsr     Copy_zSTAL_to_zSAL
   981:          lda     #$00
   982:          sta     zRIPRTY
   983:          beq     @IrqEnd3
   984:          ; -------------------------
   985:  
   986:  @LFABA:
   987:          lda     #$80                            ; switch to EOT
   988:          sta     zRIDATA
   989:          bne     @IrqEnd3
   990:  @LFAC0:                                         ; ---- $40 = load
   991:          lda     zNXTBIT
   992:          beq     @LFACE                          ; ?? if we got a bit, go on
   993:  
   994:          lda     #STATUS_TAPE_SHORT_BLOCK
   995:          jsr     SetStatus
   996:          lda     #$00
   997:          jmp     @LFB4A                          ; switch to 00 scan
   998:  
   999:  @LFACE:
  1000:          jsr     HasEndAddressBeenReached
  1001:          bcc     @LFAD6                  ; no
  1002:          jmp     @LFB48                  ; yes
  1003:          ; ------------------------
  1004:  
  1005:  @LFAD6:                                 ; end address has not been reached
  1006:          ldx     NrBlocksRemaining       ; # blocks remaining, 1 or 2 ; or zINBIT
  1007:          dex
  1008:  
  1009:          beq     @LFB08                  ; go to second pass
  1010:  
  1011:          lda     zVERCKK                 ; LOAD or VERIFY
  1012:          beq     @LFAEB
  1013:  
  1014:          ldy     #$00                    ; VERIFY
  1015:          lda     ReadCharacterIn         ; or zROPRTY
  1016:          cmp     (zSAL),y                ; check if byte matches
  1017:          beq     @LFAEB
  1018:  
  1019:          lda     #$01                    ; remember there was an error
  1020:          sta     ReadCharacterError      ; or zRODATA
  1021:  @LFAEB:                                 ; LOAD
  1022:          lda     ReadCharacterError      ; or zRODATA
  1023:          beq     @LFB3A                  ; just store this byte
  1024:  
  1025:          ldx     #$3D                    ; max # of read errors we can store
  1026:          cpx     Pass1ReadErrors         ; or zPTR1
  1027:          bcc     @LFB33                  ; too many -> LOAD or VERIFY error
  1028:          ldx     Pass1ReadErrors         ; or zPTR1
  1029:          lda     zSAL + 1                ; store high byte of error address
  1030:          sta     lSTACK + 1,x
  1031:          lda     zSAL                    ; and low byte
  1032:          sta     lSTACK,x
  1033:          inx
  1034:          inx
  1035:          stx     Pass1ReadErrors         ; or zPTR1
  1036:          jmp     @LFB3A                  ; store this byte anyway
  1037:          ; -----------------
  1038:  
  1039:  
  1040:  @LFB08:                                 ; this is done during the second read pass
  1041:          ldx     Pass2ReadErrors         ; pass 2 read errors    or zPTR2
  1042:          cpx     Pass1ReadErrors         ; pass 1 read errors    or zPTR1
  1043:          beq     @GotAllReadErrors       ; processed all
  1044:  
  1045:          lda     zSAL                    ; current address LO
  1046:          cmp     lSTACK,x                ; equal to address of next read error?
  1047:          bne     @GotAllReadErrors
  1048:          lda     zSAL + 1                ; also check current address HI
  1049:          cmp     lSTACK + 1,x
  1050:          bne     @GotAllReadErrors
  1051:          inc     Pass2ReadErrors         ; move over to next address of a read error
  1052:          inc     Pass2ReadErrors         ; or zPTR2
  1053:          lda     zVERCKK                 ; check if LOAD or VERIFY
  1054:          beq     @LFB2F
  1055:  
  1056:          lda     ReadCharacterIn         ; do a VERIFY ; or zROPRTY
  1057:          ldy     #0
  1058:          cmp     (zSAL),y                ; 2nd pass matches memory -> ok
  1059:          beq     @GotAllReadErrors
  1060:          iny
  1061:          sty     ReadCharacterError      ; read character error flag     ; or zRODATA
  1062:  @LFB2F:
  1063:          lda     ReadCharacterError      ; or zRODATA
  1064:          beq     @LFB3A
  1065:  @LFB33:                                 ; unrecoverable read error, or, VERIFY error
  1066:          lda     #STATUS_VERIFY
  1067:          jsr     SetStatus
  1068:          bne     @GotAllReadErrors
  1069:  
  1070:  @LFB3A:                                 ; just (maybe) store the byte that was read
  1071:          lda     zVERCKK                 ; 1 = VERIFY
  1072:          bne     @GotAllReadErrors
  1073:          tay
  1074:          lda     ReadCharacterIn         ; or zROPRTY
  1075:          sta     (zSAL),y                ; store the byte as read from tape into memory
  1076:  @GotAllReadErrors:
  1077:          jsr     Increment_zSAL_Address
  1078:          bne     @IrqEnd4
  1079:  @LFB48:
  1080:          lda     #$80                    ; switch to EOT
  1081:  @LFB4A:
  1082:          sta     zRIDATA                 ; switch reading mode
  1083:  
  1084:  .if CompileComputer >= C64_GENERAL
  1085:          sei
  1086:          ldx     #TAPE_REG_ICR_B_UNSET_3
  1087:          stx     TAPE_REG_ICR
  1088:  
  1089:          ldx     TAPE_REG_ICR
  1090:  .endif
  1091:  
  1092:          ldx     zFSBLK                  ; nr of blocks to read (or write)
  1093:          dex
  1094:          bmi     @LFB5C
  1095:          stx     zFSBLK                  ; only decrement if not negative
  1096:  @LFB5C:
  1097:          dec     NrBlocksRemaining       ; or zINBIT
  1098:          beq     @LFB68                  ; finish up by calculating the parity
  1099:  
  1100:          lda     Pass1ReadErrors         ; or zPTR1
  1101:          bne     @IrqEnd4                ; more errors? keep going
  1102:          sta     zFSBLK                  ; no errors? 0 blocks to read (or write)
  1103:          beq     @IrqEnd4 ; unconditional branch
  1104:          ; --------------
  1105:  
  1106:          ; Finish up the loading by checking the parity byte
  1107:  
  1108:  @LFB68:
  1109:          jsr     TapeStopMotor_and_InitTimer     ; stop cass. motor, restore timer, restore IRQ vector
  1110:  
  1111:                                          ; Calculate and check parity (just a XOR)
  1112:          jsr     Copy_zSTAL_to_zSAL
  1113:          ldy     #$00
  1114:          sty     zRIPRTY                 ; clear to 00
  1115:  
  1116:  @LFB72:
  1117:          lda     (zSAL),y
  1118:          eor     zRIPRTY                 ; xor another byte into it
  1119:          sta     zRIPRTY
  1120:          jsr     Increment_zSAL_Address
  1121:          jsr     HasEndAddressBeenReached
  1122:          bcc     @LFB72                  ; and another byte
  1123:          lda     zRIPRTY
  1124:          eor     ReadCharacterIn         ; mix in final parity byte ; or zROPRTY
  1125:          beq     @IrqEnd4
  1126:  
  1127:          lda     #STATUS_TAPE_CHKSUM_ERR ; not equal -> error
  1128:          jsr     SetStatus
  1129:  @IrqEnd4:
  1130:          jmp     NMI_End
  1131:          ; --------------------
  1132:  
  1133:  Copy_zSTAL_to_zSAL:
  1134:          lda     zSTAL + 1
  1135:          sta     zSAL + 1
  1136:          lda     zSTAL
  1137:          sta     zSAL
  1138:          rts
  1139:          ; -------------------------
  1140:  
  1141:  ; prepare input or output of a byte by initialising zTSFCNT, zTBTCNT, zBITC1, zPRTY and zRINONE
  1142:  ;
  1143:  ; Return:
  1144:  ;   A = 0
  1145:  
  1146:  TapeInitInputOutputOfByte:
  1147:          lda     #$08                                    ; set number of bits to be output to 8
  1148:          sta     zTSFCNT
  1149:  
  1150:          lda     #$00
  1151:          sta     zTBTCNT                                 ; mark: the bit to output is the real bit, not the inverted one
  1152:  
  1153:          sta     zBITC1                                  ; clear bit-counter that determines if the start or the end of a pulse have been reached
  1154:  
  1155:          sta     zPRTY                                   ; clear parity
  1156:  
  1157:          sta     zRINONE                                 ; set: the start bit ("1") has not yet been written
  1158:  
  1159:          rts
  1160:          ; --------------------
  1161:  
  1162:  TapeSetTimerAndWriteEdgeForBit:
  1163:          lda     zROPRTY                                 ; get data byte to be output
  1164:          lsr     a                                       ; get lowest bit into C
  1165:          lda     #TAPE_TIMER_CONSTANT_BIT0               ; preset $60 as timer value in case the lowest bit is 0
  1166:          bcc     TapeSetTimerLowAndWriteEdge             ; C = 0 --> branch, use $60 constant
  1167:  
  1168:  TapeSetTimerAndWriteEdgeFor1:
  1169:          lda     #TAPE_TIMER_CONSTANT_BIT1               ; set $B0 as timer value because the lowest bit is 1
  1170:  
  1171:  TapeSetTimerLowAndWriteEdge:
  1172:          ldx     #$00                                    ; high byte of timer value
  1173:  
  1174:  TapeSetTimerAndWriteEdge:
  1175:          sta     TAPE_TIMER1_LO                          ; set timer low
  1176:          stx     TAPE_TIMER1_HI                          ; and high
  1177:  
  1178:  .if CompileComputer >= C64_GENERAL
  1179:          lda     TAPE_REG_ICR                            ; clear ICR by reading it
  1180:  
  1181:          lda     #CIA_CRB_B_FORCE_LOAD | CIA_CRB_B_ONESHOT | CIA_CRB_B_START
  1182:          sta     CIA1 + CIA_O_CRB                        ; program timer B as oneshot, starting it
  1183:  .endif
  1184:  
  1185:          ; change the level of the CASS WRITE line
  1186:  
  1187:          lda     TAPE_REG_WRITE
  1188:          eor     #TAPE_B_WRITE
  1189:          sta     TAPE_REG_WRITE
  1190:  
  1191:          and     #TAPE_B_WRITE                           ; determine the new level
  1192:          rts
  1193:          ; -----------------------
  1194:  
  1195:  LFBC8:
  1196:          ; TODO
  1197:          sec
  1198:  .if CompileComputer >= C64_GENERAL
  1199:          ror     zRODATA
  1200:  .else
  1201:          ror     zSAL + 1
  1202:  .endif
  1203:          bmi     TapeIrqEnd1                             ; (uncond. branch)
  1204:          ; ------------------------
  1205:  
  1206:  TapeIrqWrite:
  1207:  ; IRQ2
  1208:          lda     zBITC1
  1209:          bne     @LFBE3
  1210:  
  1211:          lda     #<TAPE_TIMER_CONSTANT_WRITE
  1212:          ldx     #>TAPE_TIMER_CONSTANT_WRITE
  1213:          jsr     TapeSetTimerAndWriteEdge
  1214:          bne     TapeIrqEnd1                             ; if the write bit is 1, the pulse has just started -> branch -> quit IRQ, we only proceed when the pulse has ended
  1215:  
  1216:          inc     zBITC1                                  ; mark: we have already written the bit above
  1217:  
  1218:          ; TODO ???
  1219:  
  1220:  .if CompileComputer >= C64_GENERAL
  1221:          lda     zRODATA
  1222:  .else
  1223:          lda     zSAL + 1
  1224:  .endif
  1225:          bpl     TapeIrqEnd1
  1226:  
  1227:          jmp     TapeBlockCompletelyWritten              ; the complete block has been written
  1228:          ; ---------------------------
  1229:  
  1230:  @LFBE3:
  1231:          ; write a "1" bit
  1232:  
  1233:          lda     zRINONE                                 ; have we already written the bit?
  1234:          bne     @LFBF0                                  ; yes -> branch, write data bit
  1235:  
  1236:          jsr     TapeSetTimerAndWriteEdgeFor1            ; set a pulse for a "1" bit
  1237:          bne     TapeIrqEnd1                             ; if the write bit is 1, the pulse has just started -> branch -> quit IRQ, we only proceed when the pulse has ended
  1238:  
  1239:          inc     zRINONE                                 ; mark: The "1" bit has already been written
  1240:          bne     TapeIrqEnd1                             ; (uncond. branch: If we are here, zRINONE was zero, thus, it cannot be there here after the inc)
  1241:          ; ---------------------------
  1242:  
  1243:  @LFBF0:
  1244:          jsr     TapeSetTimerAndWriteEdgeForBit          ; set the timer for a "1" or "0" bit, depending upon if the bit to be output (zROPRTY.0) is 1 or 0.
  1245:          bne     TapeIrqEnd1                             ; if the write bit is 1, the pulse has just started -> branch -> quit IRQ, we only proceed when the pulse has ended
  1246:  
  1247:          ; after outputting the "0" or "1" bit, the routine
  1248:          ; also outputs the inverse of it ("1" or "0", respectively)
  1249:  
  1250:          ; Here, at this place, zTBTCNT is used to find out if the first,
  1251:          ; original bit has been sent (= $00), or if the inverted one has
  1252:          ; been sent (= $01)
  1253:  
  1254:          lda     zTBTCNT
  1255:          eor     #$01
  1256:          sta     zTBTCNT                                 ; invert zTBTCNT.0
  1257:  
  1258:          beq     TapeBitWritten                          ; if zTBTCNT == $00 here, then the second, inverted bit has been sent -> branch, the bit is completely written
  1259:  
  1260:          ; invert data bit that was just output (zROPRTY.0)
  1261:          ; Thus, the inverted bit is output the next time
  1262:  
  1263:          lda     zROPRTY
  1264:          eor     #$01
  1265:          sta     zROPRTY
  1266:  
  1267:          ; calculate the parity (with the inverted bit)
  1268:          and     #$01                                    ; extract the (inverted) data bit
  1269:          eor     zPRTY                                   ; and eor it with parity (TODO?)
  1270:          sta     zPRTY
  1271:  
  1272:  TapeIrqEnd1:
  1273:          jmp     NMI_End
  1274:          ; -------------------
  1275:  
  1276:  TapeBitWritten:
  1277:          ; the bit has been written (in non-inverted and inverted form)
  1278:  
  1279:          lsr     zROPRTY                                 ; extract next bit to be output
  1280:  
  1281:          dec     zTSFCNT                                 ; decrement number of bits to be output
  1282:  
  1283:          lda     zTSFCNT                                 ; still bits to be output?
  1284:          beq     TapeOutputParityBit                     ; no -> branch, output the parity bit
  1285:  
  1286:          bpl     TapeIrqEnd1                             ; no. of bits to be output > 0 --> still bits to be output, end IRQ here
  1287:  
  1288:  LFC16:
  1289:          ; when we reach here, all bits of the current byte have been output
  1290:          ; thus, advance to the next byte
  1291:  
  1292:          jsr     TapeInitInputOutputOfByte               ; prepare output of a byte by initialising zTSFCNT, zTBTCNT, zBITC1, zPRTY and zRINONE
  1293:  
  1294:          cli                                             ; TODO: timing is not that critical anymore (we have a start bit, thus, a delay is not fatal)
  1295:  
  1296:          ; TODO ??? Have we reached end of current byte?
  1297:  
  1298:          ; TODO follow logic of this code part
  1299:  
  1300:          lda     zCNTDN                                  ; countdown at end of preamble
  1301:          beq     @LFC30                                  ; zero -> branch, proceed to next byte and check if end address has been reached
  1302:  
  1303:          ; We're at the end of the preamble, write 89, 88, or 09, 08, ... etc
  1304:  
  1305:          ldx     #0
  1306:          stx     zSCHAR                                  ; clear check byte
  1307:  
  1308:          dec     zCNTDN
  1309:  
  1310:          ldx     zFSBLK                                  ; is this the first copy of the tape file?
  1311:          cpx     #$02
  1312:          bne     @LFC2C                                  ; no -> branch, output TODO ???
  1313:  
  1314:          ora     #$80                                    ; or in 1st copy, 89, 88, 87...
  1315:  
  1316:  @LFC2C:
  1317:          sta     zROPRTY                                 ; byte to write
  1318:          bne     TapeIrqEnd1                             ; (uncond. branch)
  1319:          ; ------------------
  1320:  
  1321:  @LFC30:
  1322:          jsr     HasEndAddressBeenReached                ; check if the last byte has been written (the end address has been reached)
  1323:          bcc     @ProcessNextByte                        ; no -> branch, process the next byte
  1324:  
  1325:          bne     LFBC8                                   ; has the extra check byte been written -> branch -> TODO
  1326:  
  1327:          inc     zSAL + 1                                ; increment start address: this way, the "bne" above will branch the next time!
  1328:  
  1329:          lda     zSCHAR                                  ; get the check byte
  1330:          sta     zROPRTY                                 ; and put it as output byte
  1331:  
  1332:          bcs     TapeIrqEnd1                             ; (uncond. branch)
  1333:          ; -------------------------
  1334:  
  1335:  @ProcessNextByte:
  1336:          ldy     #0
  1337:          lda     (zSAL),y                                ; read next byte to process
  1338:          sta     zROPRTY                                 ; and store it as new byte to output
  1339:  
  1340:          eor     zSCHAR                                  ; XOR it with the check byte
  1341:          sta     zSCHAR                                  ; and store it
  1342:  
  1343:          jsr     Increment_zSAL_Address                  ; increment pointer to next byte to write
  1344:  
  1345:          bne     TapeIrqEnd1                             ; if we do not want to write $FFFF, this is an uncond. branch
  1346:                                                          ; BUG: If we write the KERNAL onto tape, we will fall through! (TODO: Really)
  1347:          ; ---------------------------
  1348:  
  1349:  TapeOutputParityBit:
  1350:          lda     zPRTY
  1351:          eor     #$01
  1352:          sta     zROPRTY
  1353:  
  1354:  TapeIrqEnd2:
  1355:          jmp     NMI_End
  1356:          ; -------------------------
  1357:  
  1358:  TapeBlockCompletelyWritten:
  1359:          ; the block has been completely written to the tape
  1360:  
  1361:          ; found out if we still have a copy to be written
  1362:  
  1363:          dec     zFSBLK                                  ; decrement number of copies still to write
  1364:  bne     :+                                      ; have we reached 0? --> skip next instruction --> do not switch off the tape motor
  1365:  
  1366:          jsr     TapeSwitchOffMotor                      ; switch off the tape motor
  1367:  
  1368:  :       lda     #$50
  1369:          sta     zINBIT                                  ; TODO: Write "shorter" preamble
  1370:  
  1371:          ; set IRQ vector to: write preamble
  1372:  
  1373:          ldx     #(VecTapeIrqWritePreamble - TapeIrqVectors) + 8
  1374:          sei
  1375:          jsr     TapeSetIrqVector
  1376:  
  1377:          bne     TapeIrqEnd2                             ; (uncond. branch)
  1378:          ; -------------------------------------
  1379:  
  1380:  
  1381:  ; This IRQ routine is called when the system wants to write a preamble to the tape
  1382:  
  1383:  TapeIrqWritePreamble:
  1384:          lda     #TAPE_TIMER_CONSTANT_PREAMBLE
  1385:          jsr     TapeSetTimerLowAndWriteEdge
  1386:  
  1387:          bne     TapeIrqEnd2                             ; if the write bit is 1, the pulse has just started -> branch -> quit IRQ, we only proceed when the pulse has ended
  1388:  
  1389:          ; if we reach here, the tape write bit is 0.
  1390:          ; We have just written a pulse of length TAPE_TIMER_CONSTANT_PREAMBLE
  1391:  
  1392:          dec     zINBIT                                  ; decrement number of bits to write
  1393:          bne     TapeIrqEnd2                             ; not yet 0 --> branch --> quit IRQ, we're done for now (write more bits)
  1394:  
  1395:          jsr     TapeInitInputOutputOfByte               ; prepare output of a byte by initialising zTSFCNT, zTBTCNT, zBITC1, zPRTY and zRINONE
  1396:  
  1397:          dec     zRIPRTY                                 ; decrement number of "bytes" (of zINBIT bits each, that is, $100 bits each!) to write
  1398:          bpl     TapeIrqEnd2                             ; not yet negative -> branch, we're done for now (write more bytes)
  1399:  
  1400:          ldx     #(VecTapeIrqWrite - TapeIrqVectors) + 8 ; change the IRQ routine to the write routine itself
  1401:          jsr     TapeSetIrqVector
  1402:  
  1403:          cli
  1404:  
  1405:          inc     zRIPRTY                                 ; set number of bytes back to 0
  1406:  
  1407:          lda     zFSBLK                                  ; check number of copies still to write
  1408:          beq     TapeAllCopiesWritten                    ; 0 copies to write --> branch, quit writing to tape
  1409:  
  1410:          jsr     Copy_zSTAL_to_zSAL                      ; TODO copy the tape start address to the start address
  1411:  
  1412:          ldx     #$09                                    ; Leader (preamble) finishes with 89, 88, .. 81
  1413:                                                          ; for 2nd copy: 09, 08, ... 01
  1414:          stx     zCNTDN
  1415:  
  1416:  .if CompileComputer >= C64_GENERAL
  1417:          stx     zRODATA
  1418:  .endif
  1419:          bne     LFC16 ; always                          ; switch to writing bytes
  1420:          ; -----------------------
  1421:  
  1422:  .endif ;  .if CompileComputer = C64_GS
  1423:  
  1424:  ; stop cass. motor, restore timer, restore IRQ vector
  1425:  ;
  1426:  ; Remark:
  1427:  ;   Flags stay unchanged!
  1428:  ;
  1429:  TapeStopMotor_and_InitTimer:
  1430:          php                                             ; remember I status
  1431:          sei                                             ; make sure we do not get interrupted by an IRQ
  1432:  
  1433:  .if CompileComputer >= C64_GENERAL
  1434:          ; enable display
  1435:          lda     VIC + VICII_O_ControlReg1
  1436:          ora     #VICII_B_ControlReg1_DEN
  1437:          sta     VIC + VICII_O_ControlReg1
  1438:  .endif
  1439:  
  1440:          jsr     TapeSwitchOffMotor                      ; switch tape motor off
  1441:  
  1442:          lda     #TAPE_REG_ICR_B_CLEARALL                ; clear all interrupt sources
  1443:          sta     TAPE_REG_ICR
  1444:  
  1445:  .if CompileComputer < C64_GENERAL
  1446:          ; TODO document
  1447:          lda     #$F7
  1448:          sta     VIA2_PB
  1449:          lda     #VIA_ACR_B_T1_CONTROL_FREERUN
  1450:          sta     VIA2_ACR
  1451:  .endif
  1452:  
  1453:          jsr     iIOINIT_TIMER                           ; initialise timers (part of iIOINIT)
  1454:  
  1455:          ; restore interrupt vector
  1456:          ;
  1457:          lda     lIRQTMP + 1                             ; get high address of stored IRQ vector
  1458:  beq     :+                                      ; = 0 --> no IRQ vector was stored -> branch, skip restoring
  1459:          sta     lCINV + 1                               ; restore IRQ vector high
  1460:  
  1461:          lda     lIRQTMP                                 ; stored IRQ vector low
  1462:          sta     lCINV                                   ; restore IRQ vector low
  1463:  :
  1464:          plp                                             ; restore I status
  1465:          rts
  1466:          ; -----------------------
  1467:  
  1468:  TapeAllCopiesWritten:
  1469:          jsr     TapeStopMotor_and_InitTimer             ; stop cass. motor, restore timer, restore IRQ vector --> complete END the tape IRQ routines.
  1470:          beq     TapeIrqEnd2                             ; (uncond. branch, as TapeStopMotor_and_InitTimer restores the flags, and we are only called via a BEQ)
  1471:          ; -------------------------
  1472:  
  1473:  ; set IRQ vector according to X
  1474:  ; X must be calculated rather "weird": It is done as ldx #(VecNAME - TapeIrqVectors) + 8 if VecNAME is to be set.
  1475:  TapeSetIrqVector:
  1476:          lda     TapeIrqVectors - 8,x                    ; get low byte of vector
  1477:          sta     lCINV                                   ; and store it as IRQ vector low
  1478:          lda     TapeIrqVectors - 8 + 1,x                ; get high byte of vector
  1479:          sta     lCINV + 1                               ; and store it as IRQ vector high
  1480:          rts
  1481:          ; -------------------
  1482:  
  1483:  ; Switch off the tape motor
  1484:  ;
  1485:  TapeSwitchOffMotor:
  1486:          lda     TAPE_REG_MOTOR
  1487:          ora     #TAPE_B_MOTOR_ON_ALL
  1488:          sta     TAPE_REG_MOTOR
  1489:          rts
  1490:  
  1491:  ; Check if the end address has been reached
  1492:  ; in writing
  1493:  ;
  1494:  ; Return:
  1495:  ; C = 0: End address has not yet been reached
  1496:  ; C = 1: End address has been reached
  1497:  ;
  1498:  ; This routine calculated zSAL/zSAL+1 - zEAL/zEAL+1.
  1499:  ; If zSAL/zSAL+1 is smaller than zEAL/zEAL+1, we end with C=0, as there was a "borrow".
  1500:  ; Otherwise (equal or bigger), C=1 as no borrow occurred.
  1501:  ;
  1502:  ; This routine is also used in fileio.a65 for IEC transfers
  1503:  ;
  1504:  HasEndAddressBeenReached:
  1505:          sec
  1506:          lda     zSAL
  1507:          sbc     zEAL
  1508:          lda     zSAL + 1
  1509:          sbc     zEAL + 1
  1510:          rts
  1511:  
  1512:  ; Increment the zSAL/zSAL+1 address
  1513:  ; That is, this routine proceeds the pointer to the next character to be written/read.
  1514:  ;
  1515:  ; This routine is also used in fileio.a65 for IEC transfers
  1516:  ;
  1517:  Increment_zSAL_Address:
  1518:          inc     zSAL
  1519:          bne     @Rts
  1520:          inc     zSAL + 1
  1521:  @Rts:
  1522:          rts
  1523:          ; ------------------------
  1524:  
Valid XHTML 1.0 Strict
tape.a65.html; generated on Fri Sep 18 21:44:54 2015 by ca65html
uz@cc65.org