fileio.a65

     1:  ;  B-11. Function Name: GETIN
     2:  ;
     3:  ;
     4:  ;    Purpose: Get a character
     5:  ;    Call address: $FFE4 (hex) 65508 (decimal)
     6:  ;    Communication registers: A
     7:  ;    Preparatory routines: CHKIN, OPEN
     8:  ;    Error returns: See READST
     9:  ;    Stack requirements: 7+
    10:  ;    Registers affected: A (X, Y)
    11:  ;
    12:  ;    Description: If the channel is the keyboard, this subroutine removes
    13:  ;  one character from the keyboard queue and returns it as an ASCII value in
    14:  ;  the accumulator. If the queue is empty, the value returned in the
    15:  ;  accumulator will be zero. Characters are put into the queue automatically
    16:  ;  by an interrupt driven keyboard scan routine which calls the SCNKEY
    17:  ;  routine. The keyboard buffer can hold up to ten characters. After the
    18:  ;  buffer is filled, additional characters are ignored until at least one
    19:  ;  character has been removed from the queue. If the channel is RS-232, then
    20:  ;  only the A register is used and a single character is returned. See
    21:  ;  READST to check validity. If the channel is serial, cassette, or screen,
    22:  ;  call BASIN routine.
    23:  ;
    24:  ;
    25:  ;  How to Use:
    26:  ;
    27:  ;    1) Call this routine using a JSR instruction.
    28:  ;    2) Check for a zero in the accumulator (empty buffer).
    29:  ;    3) Process the data.
    30:  ;
    31:  ;
    32:  ;  EXAMPLE:
    33:  ;
    34:  ;         ;WAIT FOR A CHARACTER
    35:  ;    WAIT JSR GETIN
    36:  ;         CMP #0
    37:  ;         BEQ WAIT
    38:  ;
    39:  
    40:  KGETIN:
    41:          lda     zDFLTN          ; get device address
    42:          bne     @NoKeyboard     ; != 0 --> not the keyboard
    43:          lda     zNDX            ; are there characters in the key buffer?
    44:          beq     ClcRts1         ; no, next test
    45:          sei                     ; protect the keyboard buffer
    46:          jmp     GETIN_KEYB      ; get key from keyboard buffer
    47:  
    48:  @NoKeyboard:
    49:          cmp     #FILE_RS232     ; device address = RS232 device?
    50:          bne     LF166           ; no, next test
    51:  
    52:  KGETIN_RS232:
    53:          sty     zTEMPX          ; save Y in order to leave it unchanged
    54:          jsr     RS232_GETCHAR   ; get a character from RS232
    55:          ldy     zTEMPX          ; restore Y
    56:  
    57:  .if CompileComputer >= C64_GENERAL
    58:  ClcRts1:
    59:  .endif
    60:  
    61:          clc
    62:          rts
    63:  
    64:  ;  B-4. Function Name: CHRIN a.k.a. BASIN
    65:  ;
    66:  ;    Purpose: Get a character from the input channel
    67:  ;    Call address: $FFCF (hex) 65487 (decimal)
    68:  ;    Communication registers: A
    69:  ;    Preparatory routines: (OPEN, CHKIN)
    70:  ;    Error returns: 0 (See READST)
    71:  ;    Stack requirements: 7+
    72:  ;    Registers affected: A, X
    73:  ;
    74:  ;    Description: This routine gets a byte of data from a channel already
    75:  ;  set up as the input channel by the KERNAL routine CHKIN. If the CHKIN has
    76:  ;  NOT been used to define another input channel, then all your data is
    77:  ;  expected from the keyboard. The data byte is returned in the accumulator.
    78:  ;  The channel remains open after the call.
    79:  ;    Input from the keyboard is handled in a special way. First, the cursor
    80:  ;  is turned on, and blinks until a carriage return is typed on the
    81:  ;  keyboard. All characters on the line (up to 88 characters) are stored in
    82:  ;  the BASIC input buffer. These characters can be retrieved one at a time
    83:  ;  by calling this routine once for each character. When the carriage return
    84:  ;  is retrieved, the entire line has been processed. The next time this
    85:  ;  routine is called, the whole process begins again, i.e., by flashing the
    86:  ;  cursor.
    87:  ;
    88:  ;  How to Use:
    89:  ;
    90:  ;  FROM THE KEYBOARD
    91:  ;
    92:  ;    1) Retrieve a byte of data by calling this routine.
    93:  ;    2) Store the data byte.
    94:  ;    3) Check if it is the last data byte (is it a CR?)
    95:  ;    4) If not, go to step 1.
    96:  ;
    97:  ;  EXAMPLE:
    98:  ;
    99:  ;       LDY $#00      ;PREPARE THE Y REGISTER TO STORE THE DATA
   100:  ;   RD  JSR CHRIN
   101:  ;       STA DATA,Y    ;STORE THE YTH DATA BYTE IN THE YTH
   102:  ;                     ;LOCATION IN THE DATA AREA.
   103:  ;       INY
   104:  ;       CMP #CR       ;IS IT A CARRIAGE RETURN?
   105:  ;       BNE RD        ;NO, GET ANOTHER DATA BYTE
   106:  ;
   107:  ;
   108:  ;
   109:  ;  EXAMPLE:
   110:  ;
   111:  ;    JSR CHRIN
   112:  ;    STA DATA
   113:  ;
   114:  ;  FROM OTHER DEVICES
   115:  ;
   116:  ;    0) Use the KERNAL OPEN and CHKIN routines.
   117:  ;    1) Call this routine (using a JSR instruction).
   118:  ;    2) Store the data.
   119:  ;
   120:  ;  EXAMPLE:
   121:  ;
   122:  ;    JSR CHRIN
   123:  ;    STA DATA
   124:  ;
   125:  ;
   126:  KBASIN:
   127:          lda     zDFLTN          ; get device address
   128:  .ifdef JIFFY
   129:          bne     JDLF1A9
   130:  .else
   131:          bne     LF166           ; not keyboard --> jump
   132:  .endif
   133:  
   134:          ; TODO
   135:          lda     zPNTR           ; remember zPNTR (current column on screen) in zTEMP_zPNTR
   136:          sta     zTEMP_zPNTR     ; for later restoration
   137:  
   138:          lda     zTBLX           ; remember zTBLX (current row on screen) in zLXSP
   139:          sta     zLXSP
   140:  
   141:          jmp     BASIN_KEYB      ; input character from the screen
   142:          ; ---------------------
   143:  
   144:  LF166:  cmp     #FILE_SCREEN    ; device address == screen?
   145:          bne     KBASIN_NoScreen ; no, next test
   146:  
   147:          ; TODO
   148:  JDLF16A:
   149:          sta     zCRSW           ; zCRSW := 3 --> mark that we do not wait for key pressed until CR has been pressed
   150:  
   151:          lda     zLNMX           ; logical line length of the current line
   152:          sta     zINDX           ; is the number of characters in this line
   153:  
   154:          jmp     BASIN_KEYB      ; input character from the screen
   155:          ; ---------------------
   156:  
   157:  KBASIN_NoScreen:
   158:          bcs     KBASIN_TestIec  ; greater than screen (that is, IEC bus) --> jump
   159:          cmp     #FILE_RS232     ; is it from RS232?
   160:          beq     BASIN_RS232     ; Yes, process RS232
   161:  
   162:  .ifdef JIFFY
   163:  JDLF179:
   164:          jsr     JDLFBAA
   165:          pha
   166:          bit     zTSFCNT
   167:          bvc     LF19C
   168:          cpx     #$00
   169:          bne     LF187
   170:          lda     $C4
   171:  LF187:  cmp     #$04
   172:          bcc     LF19C
   173:          ldy     #$00
   174:          lda     (zFNADR),y
   175:          cmp     #$24
   176:          beq     LF19C
   177:          inc     zSA
   178:          jsr     JDLF38B
   179:          dec     zSA
   180:          asl     zTSFCNT
   181:  LF19C:  pla
   182:          rts
   183:  LF19E:  lda     #$10
   184:          jmp     SetStatus
   185:  
   186:  LF1A3:  .addr   PatchErrorOut
   187:          .addr   IMAIN
   188:          .addr   LA57C
   189:  
   190:  JDLF1A9:
   191:          cmp     #$04
   192:          bcc     LF166
   193:  
   194:  .else
   195:          ; if we are here, we want to get input from TAPE
   196:  
   197:          stx     zTEMPX          ; remember X
   198:          jsr     @GetNextTapeCharacterFromBuffer ; read in the next character from the tape buffer
   199:          bcs     @Ret_No_PLA                     ; if an error occurred, stop here
   200:  
   201:          ; Find out if we can read even one more character, and that character is not 0.
   202:          ; Note that we only test for it. The value will be disregarded, and the tape buffer pointer
   203:          ; will be set backwards afterwards!
   204:  
   205:          pha                                     ; remember the read character
   206:          jsr     @GetNextTapeCharacterFromBuffer ; read in the next character from the tape buffer
   207:          bcs     @Ret_With_PLA                   ; if an error occurred, stop here, making sure to PLA the remembered character
   208:  
   209:          bne     @NoNulCharacter                 ; no NUL ($00) character -> branch, skip setting the status flag
   210:  
   211:          ; mark an end-of-file status
   212:          lda     #STATUS_TAPE_EOF
   213:          jsr     SetStatus
   214:  
   215:  @NoNulCharacter:
   216:          dec     zBUFPNT                         ; put tape buffer pointer backwards, so we will read the same byte again in the next call
   217:  
   218:          ldx     zTEMPX                          ; get back (remembered) X
   219:          pla                                     ; get back remembered character
   220:          rts
   221:          ; ----------------------
   222:  
   223:  @Ret_With_PLA:
   224:          tax
   225:          pla
   226:          txa
   227:  
   228:  @Ret_No_PLA:
   229:          ldx     zTEMPX                          ; get back (remembered) X
   230:          rts
   231:          ; ----------------------
   232:  
   233:  @GetNextTapeCharacterFromBuffer:
   234:          jsr     TAPE_INCREMENT_WRITE_POINTER    ; increment the pointer into the tape buffer
   235:          bne     @ReadTapeBuffer                 ; Z = 0 --> there are still bytes to read --> branch, read bytes
   236:  
   237:          ; we must read in the next tape buffer from tape
   238:  
   239:          jsr     TapeReadNextBuffer              ; read in the next tape buffer
   240:          bcs     BASIN_RTS1                      ; C = 1 --> an error occurred -> branch, return with C = 1 indicating an error
   241:  
   242:          lda     #0                              ; set tape buffer pointer to 0 (will be incremented directly afterwards)
   243:          sta     zBUFPNT                         ; this ensures we skip the tape buffer type marker at position 0.
   244:  
   245:          beq     @GetNextTapeCharacterFromBuffer ; (uncond. branch) retry reading the tape buffer
   246:          ; ------------------
   247:  
   248:  @ReadTapeBuffer:
   249:          lda     (zTAPE1),y
   250:          clc
   251:          rts
   252:  .endif
   253:  
   254:  KBASIN_TestIec:
   255:          ; Input from IEC bus
   256:          lda     zSTATUS         ; current status
   257:          beq     BASIN_IEC       ; == 0 --> jump, get byte from IEC bus
   258:  
   259:  ReturnCR:
   260:          lda     #ASC_CR         ; an error or EOI occurred on the IEC bus, return CR
   261:  
   262:  .if CompileComputer < C64_GENERAL
   263:  ClcRts1:
   264:  .endif
   265:  ClcRts2:
   266:          clc
   267:  .if CompileComputer >= C64_GENERAL
   268:  BASIN_RTS2:
   269:  .endif
   270:  BASIN_RTS1:
   271:          rts
   272:  
   273:  BASIN_IEC:
   274:  .ifdef JIFFY
   275:          jmp     JDLFBAA
   276:  .else
   277:          jmp     iACPTR
   278:  .endif
   279:  
   280:  BASIN_RS232:
   281:          ; get data from RS232
   282:          jsr     KGETIN_RS232    ; get character from RS232, or 0 if empty
   283:          bcs     BASIN_RTS2
   284:  
   285:          cmp     #0              ; did we read a 0 (buffer empty)?
   286:  
   287:  .if CompileComputer >= C64_GENERAL
   288:          bne     ClcRts2         ; no -> return with the current character
   289:  
   290:          lda     lRSSTAT         ; check lRSSTAT (TODO)
   291:          and     #$60            ; TODO: Meaning of the bits?
   292:          bne     ReturnCR        ; bits are not null -> return with ASCII CR instead of waiting
   293:          beq     BASIN_RS232     ; wait until there is a character received from RS232
   294:          ; -------------------
   295:  .else
   296:          beq     BASIN_RS232     ; yes, wait until there is a character received from RS232
   297:          clc                     ; return: No error
   298:  
   299:  BASIN_RTS2:
   300:          rts
   301:          ; --------------------
   302:  .endif
   303:          ; --------------------
   304:  
   305:  ;  B-5. Function Name: CHROUT a.k.a. BSOUT
   306:  ;
   307:  ;    Purpose: Output a character
   308:  ;    Call address: $FFD2 (hex) 65490 (decimal)
   309:  ;    Communication registers: A
   310:  ;    Preparatory routines: (CHKOUT,OPEN)
   311:  ;    Error returns: 0 (See READST)
   312:  ;    Stack requirements: 8+
   313:  ;    Registers affected: A
   314:  ;
   315:  ;    Description: This routine outputs a character to an already opened
   316:  ;  channel. Use the KERNAL OPEN and CHKOUT routines to set up the output
   317:  ;  channel before calling this routine, If this call is omitted, data is
   318:  ;  sent to the default output device (number 3, the screen). The data byte
   319:  ;  to be output is loaded into the accumulator, and this routine is called.
   320:  ;  The data is then sent to the specified output device. The channel is left
   321:  ;  open after the call.
   322:  ;
   323:  ;  +-----------------------------------------------------------------------+
   324:  ;  | NOTE: Care must be taken when using this routine to send data to a    |
   325:  ;  | specific serial device since data will be sent to all open output     |
   326:  ;  | channels on the bus. Unless this is desired, all open output channels |
   327:  ;  | on the serial bus other than the intended destination channel must be |
   328:  ;  | closed by a call to the KERNAL CLRCHN routine.                        |
   329:  ;  +-----------------------------------------------------------------------+
   330:  ;
   331:  ;
   332:  ;
   333:  ;  How to Use:
   334:  ;
   335:  ;    0) Use the CHKOUT KERNAL routine if needed, (see description above).
   336:  ;    1) Load the data to be output into the accumulator.
   337:  ;    2) Call this routine.
   338:  ;
   339:  ;  EXAMPLE:
   340:  ;
   341:  ;    ;DUPLICATE THE BASIC INSTRUCTION CMD 4,"A";
   342:  ;    LDX #4          ;LOGICAL FILE #4
   343:  ;    JSR CHKOUT      ;OPEN CHANNEL OUT
   344:  ;    LDA #'A
   345:  ;    JSR CHROUT      ;SEND CHARACTER
   346:  ;
   347:  ;
   348:  KBSOUT:
   349:          pha                             ; remember character to be output
   350:          lda     zDFLTO                  ; get output device
   351:          cmp     #FILE_SCREEN            ; is it the screen?
   352:          bne     @NoScreen               ; no -> jump, test other devices
   353:          pla                             ; get back the character to be output
   354:          jmp     CHROUT_SCREEN           ; output to screen
   355:          ; ----------------------
   356:  
   357:  @NoScreen:
   358:          bcc     @NoIec                  ; device number is less than (or equal, but this cannot be here) than screen (3) -> jump, output is not on the IEC bus
   359:          pla                             ; get back the character to be output
   360:          jmp     iCIOUT                  ; output character to the IEC bus
   361:  
   362:  @NoIec:
   363:  .if CompileComputer >= C64_GENERAL
   364:          ; here, A can only contain 0, 1 or 2. "1" is tape, "2" is RS232.
   365:          ; TODO: Can it contain 0 at all?
   366:  
   367:          lsr     a                       ; prepare comparison with RS232 device
   368:          ; if C == 1, than output is to tape. If C == 0, then output is to console (0 - TODO) or RS232 (2).
   369:  
   370:  .else
   371:          cmp     #FILE_RS232             ; output device = RS232?
   372:          beq     KBSOUT_RS232            ; yes -> jump, output to RS232
   373:  .endif
   374:  
   375:          pla                             ; get back the character to be output
   376:  
   377:  KBSOUT_TAPE:
   378:  
   379:          sta     zPTR1                   ; remember character to be output
   380:  
   381:  .if CompileComputer < C64_GENERAL
   382:          pha                             ; remember character to be output on stack
   383:  .endif
   384:          txa                             ; remember X
   385:          pha                             ; on stack
   386:          tya                             ; remember Y
   387:          pha                             ; on stack
   388:  
   389:  .if CompileComputer >= C64_GENERAL
   390:          bcc     KBSOUT_RS232            ; we want to output onto RS232 (TODO: Or console (0)?) --> jump
   391:  .endif
   392:  
   393:          ; if we reached here, it is BSOUT on tape
   394:  
   395:  .ifdef JIFFY
   396:          jmp     JDLF3F1
   397:  
   398:  JDLF1E8:
   399:          jsr     JDLF8BF
   400:          jsr     JDLE4C6
   401:          cmp     #$30
   402:          rts
   403:          jsr     bGTBYTC
   404:          stx     zFA
   405:          jsr     JDLF75C
   406:          stx     zFSBLK
   407:          rts
   408:  
   409:  .else
   410:          jsr     TAPE_INCREMENT_WRITE_POINTER    ; increment pointer into tape buffer, and return it in Y
   411:          bne     @BufferNotYetFull               ; buffer not yet full -> jump
   412:  
   413:          ; the tape buffer is full, write out the buffer and create a new one
   414:          ;
   415:          jsr     TapeWriteCompleteBuffer         ; write out the tape buffer to tape
   416:          bcs     BSOUT_Quit                      ; C = 1 -> error -> quit
   417:  
   418:          lda     #TAPE_BUFFER_TYPE_CONTINUATION  ; mark the buffer: It is a continuation buffer
   419:          ldy     #TAPE_BUFFER_OFFSET_TYPE        ; new pointer into the tape buffer
   420:          sta     (zTAPE1),y                      ; store the mark for the buffer that it is a continuation buffer
   421:          iny                                     ; advance buffer pointer
   422:          sty     zBUFPNT                         ; and remember it
   423:  
   424:  @BufferNotYetFull:
   425:          lda     zPTR1                           ; get character to be output
   426:          sta     (zTAPE1),y                      ; put it into the tape buffer
   427:  
   428:  .endif
   429:  
   430:  BSOUT_QuitSuccess:
   431:          clc                                     ; mark: We quit with success
   432:  
   433:  BSOUT_Quit:
   434:          pla                                     ; restore Y from stack
   435:          tay
   436:          pla                                     ; restore X from stack
   437:          tax
   438:  .if CompileComputer >= C64_GENERAL
   439:          lda     zPTR1                           ; restore A (character to be output)
   440:  .else
   441:          pla                                     ; restore A (character to be output) from stack
   442:  .endif
   443:  
   444:          bcc     @Rts                            ; if routine was successfull, skip next instruction
   445:          lda     #0                              ; set error number to 0 (TODO: why 0?)
   446:  @Rts:   rts
   447:  
   448:  KBSOUT_RS232:
   449:  
   450:  .if CompileComputer < C64_GENERAL
   451:          pla                             ; restore A (character to be output) from stack
   452:          stx     zTEMPX                  ; remember X
   453:          sty     zPTR1                   ; and Y
   454:  .endif
   455:  
   456:          jsr     RS232_PUTCHAR           ; put character in A into RS232 output buffer
   457:  
   458:  .if CompileComputer >= C64_GENERAL
   459:          jmp     BSOUT_QuitSuccess
   460:          ; -----------------------
   461:  .else
   462:          ldx     zTEMPX                  ; restore X
   463:          ldy     zPTR1                   ; and Y
   464:          clc                             ; mark: success
   465:          rts
   466:          ; -----------------------
   467:  .endif
   468:          ; -----------------------
   469:  
   470:  ;  B-2. Function Name: CHKIN
   471:  ;
   472:  ;    Purpose: Open a channel for input
   473:  ;    Call address: $FFC6 (hex) 65478 (decimal)
   474:  ;    Communication registers: X
   475:  ;    Preparatory routines: (OPEN)
   476:  ;    Error returns:
   477:  ;    Stack requirements: None
   478:  ;    Registers affected: A, X
   479:  ;
   480:  ;
   481:  ;    Description: Any logical file that has already been opened by the
   482:  ;  KERNAL OPEN routine can be defined as an input channel by this routine.
   483:  ;  Naturally, the device on the channel must be an input device. Otherwise
   484:  ;  an error will occur, and the routine will abort.
   485:  ;    If you are getting data from anywhere other than the keyboard, this
   486:  ;  routine must be called before using either the CHRIN or the GETIN KERNAL
   487:  ;  routines for data input. If you want to use the input from the keyboard,
   488:  ;  and no other input channels are opened, then the calls to this routine,
   489:  ;  and to the OPEN routine are not needed.
   490:  ;    When this routine is used with a device on the serial bus, it auto-
   491:  ;  matically sends the talk address (and the secondary address if one was
   492:  ;  specified by the OPEN routine) over the bus.
   493:  ;
   494:  ;  How to Use:
   495:  ;
   496:  ;    0) OPEN the logical file (if necessary; see description above).
   497:  ;    1) Load the X register with number of the logical file to be used.
   498:  ;    2) Call this routine (using a JSR command).
   499:  ;
   500:  ;
   501:  ;  Possible errors are:
   502:  ;
   503:  ;    #3: File not open
   504:  ;    #5: Device not present
   505:  ;    #6: File not an input file
   506:  ;
   507:  ;  EXAMPLE:
   508:  ;
   509:  ;    ;PREPARE FOR INPUT FROM LOGICAL FILE 2
   510:  ;    LDX #2
   511:  ;    JSR CHKIN
   512:  ;
   513:  KCHKIN:
   514:          jsr     FindFileAndClearStatus                  ; find index for file no. in X, return in X
   515:          beq     @Found                                  ; Z=1 -> has been found, branch and process
   516:          jmp     KErrFileNotOpen                         ; return with "file not open" error
   517:  
   518:  @Found:
   519:          jsr     SetActiveFile                           ; set the active file to the parameters of the file just found
   520:          lda     zFA                                     ; get device number (of output file)
   521:          beq     @SetDefault                             ; console (0) --> set it, quit with success
   522:  
   523:          cmp     #FILE_SCREEN
   524:          beq     @SetDefault                             ; screen (3) --> set it, quit with success
   525:  
   526:          bcs     @Iec                                    ; > 3 ( = some IEC device) -> special processing for IEC device
   527:  
   528:          cmp     #FILE_RS232                             ; rs232 (2)?
   529:          bne     @Tape                                   ; no -> only tape left -> process tapes
   530:  
   531:          jmp     RS232_CHKIN                             ; special processing for RS232 CHKIN
   532:  
   533:          ; special processing for TAPE:
   534:          ; check if the tape file is opened as input
   535:  
   536:  @Tape:
   537:          ldx     zSA                                     ; check secondary address
   538:          cpx     #$60                                    ; = $60 (output file)?
   539:          beq     @SetDefault                             ; yes, set it as default device
   540:          jmp     KErrNotInputFile                        ; return with error: Not Input File
   541:  
   542:  @SetDefault:
   543:          sta     zDFLTN                                  ; set default output device
   544:          clc                                             ; mark: success
   545:          rts
   546:          ; -----------------
   547:  
   548:          ; Special processing for IEC:
   549:          ; tell device to talk to use (TALK)
   550:  
   551:  @Iec:
   552:          tax                                             ; remember device address in X
   553:          jsr     iTALK                                   ; give device in A a TALK command
   554:  
   555:          lda     zSA                                     ; get secondary address
   556:          bpl     @SecondaryAddress                       ; In range $00..$7F? -> give secondary address
   557:  
   558:          jsr     iTKSA2                                  ; no secondary address, but perform talker - listener - exchange
   559:          jmp     @CheckStatus
   560:  
   561:  @SecondaryAddress:
   562:          jsr     iTKSA                                   ; set secondary address after TALK
   563:  
   564:  @CheckStatus:
   565:          txa                                             ; get back device number
   566:  
   567:          bit     zSTATUS                                 ; check status
   568:          bpl     @SetDefault                             ; bit 7 (device not present) unset -> success -> set device as default input device
   569:  
   570:          jmp     KErrDeviceNotPresent                    ; return with error: Device Not Present
   571:          ; -----------------------------
   572:  
   573:  ;  B-3. Function Name: CHKOUT
   574:  ;
   575:  ;    Purpose: Open a channel for output
   576:  ;    Call address: $FFC9 (hex) 65481 (decimal)
   577:  ;    Communication registers: X
   578:  ;    Preparatory routines: (OPEN)
   579:  ;    Error returns: 0,3,5,7 (See READST)
   580:  ;    Stack requirements: 4+
   581:  ;    Registers affected: A, X
   582:  ;
   583:  ;    Description: Any logical file number that has been created by the
   584:  ;  KERNAL routine OPEN can be defined as an output channel. Of course, the
   585:  ;  device you intend opening a channel to must be an output device.
   586:  ;  Otherwise an error will occur, and the routine will be aborted.
   587:  ;    This routine must be called before any data is sent to any output
   588:  ;  device unless you want to use the Commodore 64 screen as your output
   589:  ;  device. If screen output is desired, and there are no other output chan-
   590:  ;  nels already defined, then calls to this routine, and to the OPEN routine
   591:  ;  are not needed.
   592:  ;    When used to open a channel to a device on the serial bus, this routine
   593:  ;  will automatically send the LISTEN address specified by the OPEN routine
   594:  ;  (and a secondary address if there was one).
   595:  ;
   596:  ;  How to Use:
   597:  ;  +-----------------------------------------------------------------------+
   598:  ;  | REMEMBER: this routine is NOT NEEDED to send data to the screen.      |
   599:  ;  +-----------------------------------------------------------------------+
   600:  ;    0) Use the KERNAL OPEN routine to specify a logical file number, a
   601:  ;       LISTEN address, and a secondary address (if needed).
   602:  ;    1) Load the X register with the logical file number used in the open
   603:  ;       statement.
   604:  ;    2) Call this routine (by using the JSR instruction).
   605:  ;
   606:  ;  EXAMPLE:
   607:  ;
   608:  ;    LDX #3        ;DEFINE LOGICAL FILE 3 AS AN OUTPUT CHANNEL
   609:  ;    JSR CHKOUT
   610:  ;
   611:  ;    Possible errors are:
   612:  ;    #3: File not open
   613:  ;    #5: Device not present
   614:  ;    #7: Not an output file
   615:  ;
   616:  ;
   617:  ;
   618:  KCHKOUT:
   619:          jsr     FindFileAndClearStatus                  ; find index for file no. in X, return in X
   620:          beq     @Found                                  ; Z=1 -> has been found, branch and process
   621:          jmp     KErrFileNotOpen                         ; return with "file not open" error
   622:  
   623:  @Found:
   624:          jsr     SetActiveFile                           ; set the active file to the parameters of the file just found
   625:          lda     zFA                                     ; get device number (of input file)
   626:          bne     @NoScreen                               ; not screen (0) -> check for other devices
   627:  
   628:  
   629:  @NotInputFile:
   630:          jmp     KErrNotOutputFile                       ; return with error: Not Output File (screen cannot be output file)
   631:  
   632:  @NoScreen:
   633:          cmp     #FILE_SCREEN                            ; is output file screen (3)?
   634:          beq     @SetDefault                             ; yes -> jump, set it as default device
   635:  
   636:          bcs     @Iec                                    ; > 3 ( = some IEC device) -> special processing for IEC device
   637:  
   638:          cmp     #FILE_RS232                             ; rs232 (2)?
   639:          bne     @Tape                                   ; no -> only tape left -> process tapes
   640:  
   641:          jmp     RS232_CHKOUT                            ; special processing for RS232 CHKIN
   642:  
   643:  @Tape:
   644:          ldx     zSA                                     ; check secondary address
   645:          cpx     #$60                                    ; = $60 (output file)?
   646:          beq     @NotInputFile                           ; yes -> return with error: Not Input File
   647:  
   648:  @SetDefault:
   649:          sta     zDFLTO                                  ; set default output device
   650:          clc                                             ; mark: success
   651:          rts
   652:          ; --------------------
   653:  
   654:  @Iec:
   655:          tax                                             ; remember device address in X
   656:          jsr     iLISTEN                                 ; give device in A a LISTEN command
   657:  
   658:          lda     zSA                                     ; get secondary address
   659:          bpl     @SecondaryAddress                       ; In range $00..$7F? -> give secondary address
   660:  
   661:          jsr     IEC_CLR_ATN                             ; only clear ATN status of IEC device
   662:          bne     @CheckStatus
   663:          ; ----------------------
   664:  
   665:  @SecondaryAddress:
   666:          jsr     iSECOND                                 ; set secondary address after LISTEN
   667:  
   668:  @CheckStatus:
   669:          txa                                             ; get back device number
   670:  
   671:          bit     zSTATUS                                 ; check status
   672:          bpl     @SetDefault                             ; bit 7 (device not present) unset -> success -> set device as default output device
   673:  
   674:          jmp     KErrDeviceNotPresent                    ; return with error: Device Not Present
   675:          ; -----------------------------
   676:  
   677:  
   678:  ;  B-9. Function Name: CLOSE
   679:  ;
   680:  ;    Purpose: Close a logical file
   681:  ;    Call address: $FFC3 (hex) 65475 (decimal)
   682:  ;    Communication registers: A
   683:  ;    Preparatory routines: None
   684:  ;    Error returns: 0,240 (See READST)
   685:  ;    Stack requirements: 2+
   686:  ;    Registers affected: A, X, Y
   687:  ;
   688:  ;    Description: This routine is used to close a logical file after all I/O
   689:  ;  operations have been completed on that file. This routine is called after
   690:  ;  the accumulator is loaded with the logical file number to be closed (the
   691:  ;  same number used when the file was opened using the OPEN routine).
   692:  ;
   693:  ;
   694:  ;
   695:  ;
   696:  ;
   697:  ;
   698:  ;  How to Use:
   699:  ;
   700:  ;    1) Load the accumulator with the number of the logical file to be
   701:  ;       closed.
   702:  ;    2) Call this routine.
   703:  ;
   704:  ;  EXAMPLE:
   705:  ;
   706:  ;    ;CLOSE 15
   707:  ;    LDA #15
   708:  ;    JSR CLOSE
   709:  ;
   710:  KCLOSE:
   711:          jsr     FindFile                                ; find index for file no. in A, return in X
   712:          beq     @DoClose                                ; z=1 -> file index exists -> branch, close file
   713:  
   714:          ; if we reach here, the file did not exist
   715:          clc                                             ; report success (closing a non-existing file is not an error!)
   716:          rts
   717:          ; ------------
   718:  
   719:  @DoClose:
   720:          jsr     SetActiveFile                           ; set the active file to the parameters of the file just found
   721:  
   722:          txa                                             ; put current index into table of open files into A
   723:          pha                                             ; and store it on the stack
   724:  
   725:          lda     zFA                                     ; get device address
   726:          beq     CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE      ; console (0) --> get back index into table and set file parameters into table of open files
   727:          cmp     #FILE_SCREEN
   728:          beq     CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE      ; screen (3) --> get back index into table and set file parameters into table of open files
   729:  
   730:          bcs     CLOSE_FILE_ON_IEC                       ; > 3 (IEC device) --> perform open on IEC
   731:  
   732:          cmp     #FILE_RS232
   733:          bne     @Tape                                   ; not RS232 (2) --> only TAPE left --> open TAPE
   734:  
   735:  
   736:          ; if we reach here, we want to open a RS232 file
   737:  
   738:          ; the following 2 instructions could be replaced with jsr CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE
   739:          pla                                             ; get back index into table of open files
   740:          jsr     CLOSE_DELETE_FROM_TABLE                 ; set file parameters in table of open files
   741:  
   742:  .if CompileComputer >= C64_GENERAL
   743:          jsr     LF483
   744:  .else
   745:          lda     #$7D
   746:          sta     VIA1_IEC
   747:          lda     #$06
   748:          sta     VIA1_PB
   749:          lda     #$EE
   750:          sta     VIA1_PCR
   751:  .endif
   752:  
   753:          ; manipulate MEMTOP in order to release the memory of (2*) 256 byte each for
   754:          ; the RS232 input and output buffers
   755:          ;
   756:          jsr     iMEMTOP_Get                             ; get current memory top into (X/Y)
   757:          lda     zRIBUF + 1                              ; current RS232 input ring buffer high address set?
   758:  beq     :+                                      ; no, skip, we do not need to free memory for it
   759:  
   760:          iny                                             ; free memory by incrementing MEMTOP high byte
   761:  
   762:  :
   763:          lda     zROBUF + 1                              ; current RS232 output ring buffer high address set?
   764:  beq     :+                                      ; no, skip, we do not need to free memory for it
   765:  
   766:          iny                                             ; free memory by incrementing MEMTOP high byte
   767:  
   768:  :
   769:          lda     #0                                      ; in order to mark that they do not exist anymore,
   770:          sta     zRIBUF + 1                              ; clear high bytes of RS232 input buffer
   771:          sta     zROBUF + 1                              ; and rS232 output buffer
   772:  
   773:          jmp     SetMemtop_And_Return_With_F0            ; set memory top and return with error code $F0, which indicates the memory top has been changed
   774:          ; --------------
   775:  
   776:  
   777:          ; TODO: Comment tape close routine
   778:  
   779:  @Tape:
   780:  .ifdef JIFFY
   781:          pla
   782:          jmp     KErrIllegalDeviceNumber
   783:  
   784:  LF2CC:  jsr     kCLRCHN
   785:  LF2CF:  lda     #$6F
   786:          jsr     FindFile
   787:          bne     CLOSE_Rts
   788:          jmp     JDLF2F3
   789:  
   790:  LF2D9:  stx     zFA
   791:  LF2DB:  tya
   792:          pha
   793:          jsr     JDLF8B2
   794:          jsr     JDLF7A2
   795:          php
   796:          jsr     LF2CC
   797:          plp
   798:          pla
   799:          tay
   800:          ldx     zFA
   801:          rts
   802:          .byte   $F2
   803:  
   804:  .else
   805:  
   806:          lda     zSA                                     ; get secondary address
   807:          and     #$0F                                    ; (only the lower 4 bit, as the value has been ORed with IEEE_OPEN)
   808:          beq     CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE      ; == 0 -> tape was opened for read --> branch, we do not need to write out the rest of the tape buffer --> get back index into table and set file parameters into table of open files
   809:  
   810:          jsr     TapeGetPointer                          ; get pointer to tape buffer into (X/Y) (unused, but flags important)
   811:          lda     #0
   812:  
   813:  .if CompileComputer >= C64_GENERAL
   814:          sec                             ; set C = 1 to make sure the output is not send to RS232 in KBSOUT_TAPE!
   815:                                          ; (C=0 would output to RS232 instead; the C64 implementation differs from the VIC20 one in this aspect!)
   816:  
   817:  .endif
   818:  
   819:          jsr     KBSOUT_TAPE             ; output to TAPE
   820:  
   821:  .if 0
   822:      ; this macro is defined in fileio_data.inc
   823:  
   824:      .macro FILEIO_PATCH_CLOSE_TAPE
   825:  
   826:          jsr     TapeWriteCompleteBuffer                 ; write out the tape buffer to tape
   827:          bcc     FileIoPatch_NoError                     ; C = 0 -> no error -> branch
   828:  
   829:          pla                                             ; get back index into table of open files
   830:          lda     #$00
   831:  
   832:      .endmacro
   833:  .endif
   834:  
   835:  .if CompileComputer >= C64_GENERAL
   836:  
   837:          FILEIO_PATCH_CLOSE_TAPE
   838:  
   839:          rts
   840:  
   841:  FileIoPatch_NoError:
   842:  
   843:  .elseif CompileComputer = VIC20_02
   844:  
   845:          jsr     TapeWriteCompleteBuffer ; write out the tape buffer to tape
   846:          bcs     CLOSE_ClcRts            ; C = 1 -> error -> quit
   847:  
   848:  .else
   849:          jmp     FileIoPatchCloseTape
   850:  
   851:  FileIoPatchCloseTape_Return:
   852:  
   853:          bcs     CLOSE_Rts               ; if error, quit with RTS
   854:                                          ; not needed on C64, as the RTS is directly after the patch there.
   855:  
   856:  .endif
   857:  
   858:          lda     zSA                                     ; secondary address
   859:          cmp     #$62                                    ; $62 (that is, bit 1 set: "Write end-of-tape (EOT) marker"
   860:          bne     CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE      ; not $62 --> get back index into table and set file parameters into table of open files
   861:  
   862:          ; Special processing for secondary address $62: Write an end-of-tape marker on the tape
   863:  
   864:          lda     #TAPE_BUFFER_TYPE_EOT                   ; tape buffer type: END-OF-TAPE (EOT)
   865:          jsr     TapeCreateFileBuffer                    ; create the file buffer and write it on tape
   866:  
   867:          jmp     CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE      ; get back index into table and set file parameters into table of open files
   868:  
   869:  .endif
   870:  
   871:  CLOSE_FILE_ON_IEC:
   872:          jsr     IecClose                                        ; if zSA.7 is not set, close the file on the IEC bus.
   873:  
   874:  CLOSE_GET_BACK_INDEX_AND_DELETE_FROM_TABLE:
   875:          pla                                             ; get back index into table of open files
   876:  
   877:  CLOSE_DELETE_FROM_TABLE:
   878:          tax                                             ; X := index of entry to close
   879:  
   880:  JDLF2F3:
   881:          dec     zLDTND                                  ; decrement number of entries in table of open files
   882:  
   883:          cpx     zLDTND                                  ; have we found 0 open files?
   884:          beq     CLOSE_ClcRts                                    ; yes, nothing to do
   885:  
   886:          ; replace entry to be closed with last entry in table
   887:          ldy     zLDTND                                  ; get index of last entry
   888:  
   889:          lda     lLAT,y                                  ; replace logical file number (LA)
   890:          sta     lLAT,x
   891:          lda     lFAT,y                                  ; replace device address (FA)
   892:          sta     lFAT,x
   893:          lda     lSAT,y                                  ; replace secondary address (SA)
   894:          sta     lSAT,x
   895:  
   896:  CLOSE_ClcRts:
   897:          clc                                             ; mark: success
   898:  CLOSE_Rts:
   899:          rts
   900:          ; ------------
   901:  
   902:  
   903:  ; Clear status and perform FindFile afterwards
   904:  ;
   905:  ; Input:  X := file no
   906:  ; Output: X := index into the tables (>= 0), or $FF if not found
   907:  ;         Z = 1, N = 0 if index found,
   908:  ;         Z = 0, N = 1 otherwise
   909:  ; Uses:   X, A
   910:  ;
   911:  ; This function is often followed by a call to SetActiveFile
   912:  ; to get the data of lLAT, lFAT and lSAT into zLA, zFA and zSA.
   913:  ;
   914:  ; Note that register inputs differ from FindFile!
   915:  ;
   916:  FindFileAndClearStatus:
   917:          lda     #0
   918:          sta     zSTATUS
   919:          txa
   920:  
   921:  ; Find the index into the lLAT, lFAT and lSAT tables
   922:  ; for a specific file number.
   923:  ;
   924:  ; That is, if a file is opened with (BASIC)
   925:  ;   open 1,2,3
   926:  ; then find the index into these tables for the given "1".
   927:  ;
   928:  ; Input:  A := file no
   929:  ; Output: X := index into the tables (>= 0), or $FF if not found
   930:  ;         Z = 1, N = 0 if index found,
   931:  ;         Z = 0, N = 1 otherwise
   932:  ; Uses:   X
   933:  ;
   934:  ; This function is often followed by a call to SetActiveFile
   935:  ; to get the data of lLAT, lFAT and lSAT into zLA, zFA and zSA.
   936:  ;
   937:  FindFile:
   938:          ldx     zLDTND                          ; get number of entries in the table
   939:  @Next:  dex
   940:          bmi     FindFileRts                     ; all searched? Then quit
   941:          cmp     lLAT,x                          ; is this the right file no?
   942:          bne     @Next                           ; no -> branch, try the next index
   943:          rts
   944:          ; --------------------
   945:  
   946:  
   947:  ; Set the file parameters (zLA, zFA and zSA) for a given file
   948:  ;
   949:  ; Input:  X := index into the tables lLAT, lFAT and LSAT, as returned by
   950:  ;              FindFile or FindFileAndClearStatus
   951:  ; Output:
   952:  ;         Z = 1, N = 0 if index found,
   953:  ;         Z = 0, N = 1 otherwise
   954:  ; Uses:   A
   955:  ;
   956:  SetActiveFile:
   957:          lda     lLAT,x                  ; get file no from table
   958:          sta     zLA                     ; into current value
   959:  
   960:          lda     lFAT,x                  ; get device number from table
   961:          sta     zFA                     ; into current value
   962:  
   963:          lda     lSAT,x                  ; get secondary address from table
   964:          sta     zSA                     ; into current value
   965:  
   966:  FindFileRts:
   967:          rts
   968:          ; ---------------------
   969:  
   970:  ;  B-8. Function Name: CLALL
   971:  ;
   972:  ;    Purpose: Close all files
   973:  ;    Call address: $FFE7 (hex) 65511 (decimal)
   974:  ;    Communication registers: None
   975:  ;    Preparatory routines: None
   976:  ;    Error returns: None
   977:  ;    Stack requirements: 11
   978:  ;    Registers affected: A, X
   979:  ;
   980:  ;    Description: This routine closes all open files. When this routine is
   981:  ;  called, the pointers into the open file table are reset, closing all
   982:  ;  files. Also, the CLRCHN routine is automatically called to reset the I/O
   983:  ;  channels.
   984:  ;
   985:  ;  How to Use:
   986:  ;
   987:  ;    1) Call this routine.
   988:  ;
   989:  ;  EXAMPLE:
   990:  ;
   991:  ;    JSR CLALL   ;CLOSE ALL FILES AND SELECT DEFAULT I/O CHANNELS
   992:  ;    JMP RUN     ;BEGIN EXECUTION
   993:  ;
   994:  ;
   995:  KCLALL:
   996:          lda     #$00
   997:          sta     zLDTND                  ; set number of open files to 0
   998:  
   999:          ; "fall through" to CLRCHN
  1000:  
  1001:  ;  B-10. Function Name: CLRCHN a.k.a. CLRCH
  1002:  ;
  1003:  ;    Purpose: Clear I/O channels
  1004:  ;    Call address: $FFCC (hex) 65484 (decimal)
  1005:  ;    Communication registers: None
  1006:  ;    Preparatory routines: None
  1007:  ;    Error returns:
  1008:  ;    Stack requirements: 9
  1009:  ;    Registers affected: A, X
  1010:  ;
  1011:  ;    Description: This routine is called to clear all open channels and re-
  1012:  ;  store the I/O channels to their original default values. It is usually
  1013:  ;  called after opening other I/O channels (like a tape or disk drive) and
  1014:  ;  using them for input/output operations. The default input device is 0
  1015:  ;  (keyboard). The default output device is 3 (the Commodore 64 screen).
  1016:  ;    If one of the channels to be closed is to the serial port, an UNTALK
  1017:  ;  signal is sent first to clear the input channel or an UNLISTEN is sent to
  1018:  ;  clear the output channel. By not calling this routine (and leaving lis-
  1019:  ;  tener(s) active on the serial bus) several devices can receive the same
  1020:  ;  data from the Commodore 64 at the same time. One way to take advantage
  1021:  ;  of this would be to command the printer to TALK and the disk to LISTEN.
  1022:  ;  This would allow direct printing of a disk file.
  1023:  ;    This routine is automatically called when the KERNAL CLALL routine is
  1024:  ;  executed.
  1025:  ;
  1026:  ;  How to Use:
  1027:  ;    1) Call this routine using the JSR instruction.
  1028:  ;
  1029:  ;  EXAMPLE:
  1030:  ;    JSR CLRCHN
  1031:  ;
  1032:  KCLRCH:
  1033:          ldx     #FILE_SCREEN            ; device address for screen (3)
  1034:  
  1035:          cpx     zDFLTO                  ; compare current default output device to 3?
  1036:  bcs     :+                      ; <= 3, skip next step
  1037:  
  1038:          jsr     iUNLSN                  ; send UNLISTEN on IEC bus
  1039:  
  1040:  :       cpx     zDFLTN                  ; compare current default input device to 3?
  1041:  bcs     :+                      ; <= 3, skip next step
  1042:  
  1043:          jsr     iUNTLK                  ; send UNTALK on IEC bus
  1044:  
  1045:  :       stx     zDFLTO                  ; set default output device to screen
  1046:  
  1047:          lda     #FILE_KEYBOARD
  1048:          sta     zDFLTN                  ; set default input device to keyboard
  1049:          rts
  1050:          ; ---------------
  1051:  
  1052:  ;  B-18. Function Name: OPEN
  1053:  ;
  1054:  ;
  1055:  ;    Purpose: Open a logical file
  1056:  ;    Call address: $FFC0 (hex) 65472 (decimal)
  1057:  ;    Communication registers: None
  1058:  ;    Preparatory routines: SETLFS, SETNAM
  1059:  ;    Error returns: 1,2,4,5,6,240, READST
  1060:  ;    Stack requirements: None
  1061:  ;    Registers affected: A, X, Y
  1062:  ;
  1063:  ;    Description: This routine is used to OPEN a logical file. Once the
  1064:  ;  logical file is set up, it can be used for input/output operations. Most
  1065:  ;  of the I/O KERNAL routines call on this routine to create the logical
  1066:  ;  files to operate on. No arguments need to be set up to use this routine,
  1067:  ;  but both the SETLFS and SETNAM KERNAL routines must be called before
  1068:  ;  using this routine.
  1069:  ;
  1070:  ;
  1071:  ;  How to Use:
  1072:  ;
  1073:  ;    0) Use the SETLFS routine.
  1074:  ;    1) Use the SETNAM routine.
  1075:  ;    2) Call this routine.
  1076:  ;
  1077:  ;  EXAMPLE:
  1078:  ;
  1079:  ;    This is an implementation of the BASIC statement: OPEN 15,8,15,"I/O"
  1080:  ;
  1081:  ;
  1082:  ;          LDA #NAME2-NAME    ;LENGTH OF FILE NAME FOR SETLFS
  1083:  ;          LDY #>NAME         ;ADDRESS OF FILE NAME
  1084:  ;          LDX #<NAME
  1085:  ;          JSR SETNAM
  1086:  ;          LDA #15
  1087:  ;          LDX #8
  1088:  ;          LDY #15
  1089:  ;          JSR SETLFS
  1090:  ;          JSR OPEN
  1091:  ;    NAME  .BYT 'I/O'
  1092:  ;    NAME2
  1093:  ;
  1094:  
  1095:  KOPEN:
  1096:          ldx     zLA                                     ; logical address of file to open
  1097:  bne     :+                                      ; not 0 -> ok, can be opened
  1098:          jmp     KErrNotInputFile                        ; return with error: "Not Input File"
  1099:          ; ---------------------
  1100:  
  1101:  :       jsr     FindFileAndClearStatus                  ; find index for file no. in X
  1102:  bne     :+                                      ; Z=0 -> file does not exist yet -> branch -> process this open
  1103:          jmp     KErrFileOpen                            ; return with "file open" error
  1104:  
  1105:  :       ldx     zLDTND                                  ; get number of open files
  1106:          cpx     #lLAT_Size                              ; limit not yet reached?
  1107:  bcc     :+                                      ; no -> continue
  1108:          jmp     KErrTooManyOpenFiles                    ; return with error: "Too many files error"
  1109:  
  1110:  :       inc     zLDTND                                  ; increment number of open files
  1111:  
  1112:          lda     zLA                                     ; store logical file number
  1113:          sta     lLAT,x                                  ; in table
  1114:  
  1115:          lda     zSA                                     ; modify secondary address
  1116:          ora     #IEEE_OPEN                              ; to mean "for OPEN" on the IEC Bus
  1117:          sta     zSA                                     ; store it back
  1118:          sta     lSAT,x                                  ; and in the table
  1119:  
  1120:          lda     zFA                                     ; store device (primary) address
  1121:          sta     lFAT,x                                  ; in table
  1122:  
  1123:          beq     KOPEN_ClcRts                            ; open of the keyboard -> branch -> quit function with success, nothing else to do
  1124:  
  1125:          cmp     #FILE_SCREEN
  1126:          beq     KOPEN_ClcRts                            ; open of the screen -> branch -> quit function with success, nothing else to do
  1127:  
  1128:          bcc     @NoIec                                  ; open of something else but IEC (tape, rs232) -> test for it
  1129:  
  1130:          jsr     KOPEN_IEC                               ; open the IEC file
  1131:          bcc     KOPEN_ClcRts                            ; if we succeeded in opening the IEC file, succeed this call, too
  1132:  
  1133:          ; TODO: what if KOPEN_IEC (calling iUNLSN as last command) ended with C=1? Is this a BUG?
  1134:  
  1135:  @NoIec:
  1136:  
  1137:  .ifdef JIFFY
  1138:          cmp     #$01
  1139:          beq     JDLF3F3
  1140:          jmp     OPEN_RS232
  1141:  
  1142:  JDLF38B:
  1143:          jsr     kUNTLK
  1144:          lda     zFA
  1145:          jsr     kTALK
  1146:          lda     zSA
  1147:          jmp     kTKSA
  1148:  
  1149:  JDLF398:
  1150:          ; @@@todo some table?
  1151:  
  1152:          eor     $572D
  1153:          brk
  1154:          asl     $1C
  1155:          lda     $0261
  1156:          sta     zCHARAC
  1157:          lda     #$12
  1158:          sta     $06
  1159:          ldx     #$00
  1160:          stx     zROBUF
  1161:          jsr     $D586
  1162:          ldy     $0267
  1163:          lda     ($30),y
  1164:          eor     #$40
  1165:          sta     ($30),y
  1166:          jmp     $D58A
  1167:          eor     $452D
  1168:          brk
  1169:          asl     zOPMASK
  1170:          and     $6A57
  1171:          brk
  1172:          ora     (zOPMASK,x)
  1173:          and     $6957
  1174:          brk
  1175:          ora     ($50,x)
  1176:          ror     a:zR6510
  1177:          .byte   $53
  1178:          .byte   $3A
  1179:  
  1180:  .else
  1181:          cmp     #FILE_RS232                             ; is it an RS232 device?
  1182:  
  1183:  .if CompileComputer = C64_SX64
  1184:          bne     @ErrIllegalDeviceNumber                 ; no -> tape -> the SX64 does not have a tape -> report an error
  1185:  .else
  1186:          bne     @Tape                                   ; no -> tape -> process opening a tape file
  1187:  .endif
  1188:          jmp     OPEN_RS232                              ; open the RS232 device
  1189:          ; ------------------
  1190:  
  1191:          ; TODO: Document
  1192:  
  1193:  @Tape:
  1194:          jsr     TapeGetPointer                  ; get pointer to tape buffer into (X/Y) (unused, but flags important)
  1195:          bcs     @TapeAllowed                    ; C = 1 -> tape buffer does NOT point to stack page or zero page -> proceed, we do not overwrite essential data!
  1196:  
  1197:  @ErrIllegalDeviceNumber:
  1198:          jmp     KErrIllegalDeviceNumber
  1199:  
  1200:  @TapeAllowed:
  1201:          lda     zSA                             ; check secondary address of the file
  1202:          and     #$0F                            ; (only the lower 4 bit, as the value has been ORed with IEEE_OPEN)
  1203:          bne     KLOAD_OpenTapeForWrite          ; anything but 0 means: WRITE. Thus, branch if we have a write operation
  1204:  
  1205:          ; Open the tape for a read operation
  1206:  
  1207:          jsr     TapePressPlayOnTape             ; output "PRESS PLAY ON TAPE" and wait for PLAY to be pressd
  1208:          bcs     KOPEN_Rts                       ; C=1 -> an error occurred (i.e., STOP was pressed) -> branch, abort
  1209:  
  1210:          jsr     OutputSearchingFor              ; output the text "SEARCHING" or "SEARCHING FOR <filename>"
  1211:  
  1212:          lda     zFNLEN                          ; test file name length
  1213:          beq     KLOAD_NoFilenameGiven           ; == 0 -> no file name given -> branch, skip finding the right file
  1214:  
  1215:          ; try to find the specified file
  1216:  
  1217:          jsr     TapeFindSpecificFile            ; try to find the specified file
  1218:          bcc     KLOAD_PrepareTapeBuffer         ; C = 0 -> no error occurred -> branch, proceed with operation
  1219:  
  1220:          beq     KOPEN_Rts                       ; Z = 1 --> EOT was found, abort (TODO can this branch happen actually?)
  1221:  
  1222:  KLOAD_ErrFileNotFound:
  1223:          jmp     KErrFileNotFound                ; return with FILE NOT FOUND error
  1224:          ; ------------------------
  1225:  
  1226:  
  1227:  KLOAD_NoFilenameGiven:
  1228:          ; there was no file name given, just search for any file
  1229:  
  1230:          jsr     TapeReadTapeHeaderOfNextFile    ; try to find ANY file header
  1231:          beq     KOPEN_Rts                       ; TODO what?
  1232:          bcc     KLOAD_PrepareTapeBuffer         ; C = 0 --> no error occurred -> branch, proceed with operation
  1233:  
  1234:          bcs     KLOAD_ErrFileNotFound           ; (uncond. branch) return with FILE NOT FOUND error
  1235:          ; ---------------------------
  1236:  
  1237:  KLOAD_OpenTapeForWrite:
  1238:          jsr     TapePressRecordAndPlayOnTape    ; output "PRESS RECORD + PLAY ON TAPE" and wait for PLAY to be pressd
  1239:          bcs     KOPEN_Rts                       ; C=1 -> an error occurred (i.e., STOP was pressed) -> branch, abort
  1240:  
  1241:          lda     #TAPE_BUFFER_TYPE_DATA          ; tape buffer type: a data file
  1242:          jsr     TapeCreateFileBuffer            ; create the file buffer (including start and end address and file name) and write it on tape
  1243:  
  1244:  KLOAD_PrepareTapeBuffer:
  1245:          ; depending upon if the tape file is opened for read or write, the tape
  1246:          ; buffer has to be prepared differently.
  1247:          ;
  1248:          ; For reading, the tape buffer pointer has to point to the last address (lTBUFFR_SIZE - 1)
  1249:          ; in order to generate an overflow on the first call to read a character.
  1250:          ; The buffer itself does not need to be changed.
  1251:          ;
  1252:          ; For writing, however, the pointer must be set to the beginning of the buffer,
  1253:          ; and the beginning must be initialized to "2" (TODO: List of values with defines).
  1254:          ;
  1255:          lda     #lTBUFFR_SIZE - 1               ; assumed value for the tape buffer pointer: Assume reading of the tape
  1256:  
  1257:          ldy     zSA                             ; check secondary address
  1258:          cpy     #$60                            ; is it 0 ( | IEEE_OPEN)?
  1259:          beq     @NoWrite                        ; yes -> branch, we want to read the tape
  1260:  
  1261:          ; process tape buffer for writing:
  1262:  
  1263:          ldy     #0                              ; index into tape buffer = 0 (start at the beginning)
  1264:          lda     #TAPE_BUFFER_TYPE_CONTINUATION
  1265:          sta     (zTAPE1),y                      ; mark the block as continuation buffer
  1266:  
  1267:          tya                                     ; set tape buffer pointer to 0
  1268:  
  1269:  .endif
  1270:  
  1271:  @NoWrite:
  1272:          sta     zBUFPNT                         ; and store it.
  1273:  
  1274:  .if CompileComputer >= C64_GENERAL
  1275:  KOPEN_ClcRts2:
  1276:  .endif
  1277:  
  1278:  KOPEN_ClcRts:
  1279:          clc
  1280:  
  1281:  KOPEN_Rts:
  1282:          rts
  1283:          ; -------------------------------
  1284:  
  1285:  
  1286:  
  1287:  ; Remark:
  1288:  ;   In case the device does not exist, thus function will not return to the caller,
  1289:  ;   but to the caller of the caller (in case there was no manipulation of the stack in between!)
  1290:  
  1291:  KOPEN_IEC:      ; Open a file on the IEC bus
  1292:          lda     zSA                     ; get secondary address
  1293:          bmi     KOPEN_ClcRts2           ; bit 7 set -> nothing to do, we're done
  1294:  
  1295:          ldy     zFNLEN                  ; get length of the file name
  1296:          beq     KOPEN_ClcRts2           ; is it 0 -> nothing to do, we're done
  1297:  
  1298:  .if CompileComputer >= C64_GENERAL
  1299:          lda     #0                      ; clear the status byte, that is, start with a clean status
  1300:          sta     zSTATUS
  1301:  .endif
  1302:  
  1303:          lda     zFA
  1304:          jsr     iLISTEN                 ; send LISTEN to the device
  1305:  
  1306:          lda     zSA
  1307:          ora     #IEEE_SECONDARY
  1308:          jsr     iSECOND                 ; send secondary address after LISTEN
  1309:  
  1310:          lda     zSTATUS                 ; check device status
  1311:          bpl     KOPEN_Iec_DeviceExists  ; bit 7 (device not present) unset -> device exists -> proceed
  1312:  
  1313:  JDLF3F1:
  1314:          ; if we reach here, zSTATUS.7 was set. Thus, we got a timeout -> the device does not exist
  1315:          ;
  1316:          pla                             ; remove return address of caller from the stack (!)
  1317:          pla
  1318:  
  1319:  JDLF3F3:
  1320:          jmp     KErrDeviceNotPresent    ; return to the caller of the caller with "Device not Present" error
  1321:          ; --------------------------
  1322:  
  1323:  KOPEN_Iec_DeviceExists:
  1324:          lda     zFNLEN                  ; get length of the file name
  1325:          beq     @NoName                 ; if 0, we're done. (UNNECCESSARY, as this has already been tested above!)
  1326:  
  1327:          ; now, the file name is output to the IEC bus, one byte after the other
  1328:  
  1329:          ldy     #0
  1330:  :       lda     (zFNADR),y              ; get next character of the file name
  1331:          jsr     iCIOUT                  ; output it to the IEC bus
  1332:          iny                             ; proceed to next character
  1333:          cpy     zFNLEN                  ; have we reached the end yet?
  1334:  bne     :-                      ; now, process this character, too.
  1335:  
  1336:  @NoName:
  1337:  .if CompileComputer >= C64_GENERAL
  1338:          ; for C64, this functionality has been implemented by jumping to another place with the same implementation as here:
  1339:  
  1340:          jmp     DoUnlistenClcRts
  1341:  .else
  1342:          ; the VIC-20 does it itself here, which results in duplicate code:
  1343:  
  1344:          jsr     iUNLSN
  1345:  
  1346:  .if CompileComputer < C64_GENERAL
  1347:  KOPEN_ClcRts2:
  1348:  .endif
  1349:          clc
  1350:          rts
  1351:  .endif
  1352:  
  1353:  OPEN_RS232:
  1354:  
  1355:          ; TODO: Initialize RS232
  1356:  
  1357:  .if CompileComputer >= C64_GENERAL
  1358:          jsr     LF483
  1359:  .else
  1360:          lda     #$06
  1361:          sta     VIA1_DDRB
  1362:          sta     VIA1_PB
  1363:          lda     #$EE
  1364:          sta     VIA1_PCR
  1365:          ldy     #0
  1366:  .endif
  1367:          sty     lRSSTAT
  1368:  @LF40F: cpy     zFNLEN
  1369:          beq     @LF41D
  1370:          lda     (zFNADR),y
  1371:          sta     lM51CTR,y
  1372:          iny
  1373:          cpy     #$04
  1374:          bne     @LF40F
  1375:  @LF41D:
  1376:          jsr     LEF4A
  1377:          stx     lBITNUM
  1378:          lda     lM51CTR
  1379:          and     #$0F
  1380:  
  1381:  .if CompileComputer >= C64_02
  1382:          beq     @LF446
  1383:          asl     a
  1384:          tax
  1385:          lda     lTVSFLG
  1386:          bne     @LF43A
  1387:          ldy     LFEC2 - 1,x
  1388:          lda     LFEC2 - 2,x
  1389:          jmp     @LF440
  1390:  @LF43A:
  1391:          ldy     LE4EC-1,x
  1392:          lda     LE4EC-2,x
  1393:  @LF440:
  1394:          sty     lM51AJB + 1
  1395:          sta     lM51AJB
  1396:  @LF446:
  1397:          lda     lM51AJB
  1398:          asl     a
  1399:          jsr     LFF2E
  1400:          lda     lM51CDR
  1401:          lsr     a
  1402:          bcc     @LF45C
  1403:          lda     CIA2 + CIA_O_PB
  1404:          asl     a
  1405:          bcs     @LF45C
  1406:          jsr     LF00D
  1407:  .else
  1408:          bne     @LF435
  1409:    .if CompileComputer >= C64_GENERAL
  1410:          lda     lM51AJB
  1411:          asl     a
  1412:          tay
  1413:          lda     lM51AJB + 1
  1414:          jmp     @LF43F
  1415:    .endif
  1416:  @LF435: asl     a
  1417:          tax
  1418:          lda     LFEC2 - 2,x
  1419:          asl     a
  1420:          tay
  1421:          lda     LFEC2 - 1,x
  1422:  @LF43F: rol     a
  1423:          pha
  1424:          tya
  1425:          adc     #<200
  1426:          sta     lBAUDOF
  1427:          pla
  1428:          adc     #>200
  1429:          sta     lBAUDOF + 1
  1430:          lda     lM51CDR
  1431:          lsr     a
  1432:          bcc     @LF45C
  1433:    .if CompileComputer >= C64_GENERAL
  1434:          lda     CIA2 + CIA_O_PB
  1435:    .else
  1436:          lda     VIA2_PB
  1437:    .endif
  1438:          asl     a
  1439:          bcs     @LF45C
  1440:    .if CompileComputer >= C64_GENERAL
  1441:          jmp     LF00D
  1442:    .else
  1443:          jmp     LF016
  1444:    .endif
  1445:  .endif
  1446:  
  1447:  @LF45C: lda     lRIDBE
  1448:          sta     lRIDBS
  1449:          lda     lRODBE
  1450:          sta     lRODBS
  1451:          jsr     iMEMTOP_Get
  1452:          lda     zRIBUF + 1
  1453:          bne     @LF474
  1454:          dey
  1455:          sty     zRIBUF + 1
  1456:          stx     zRIBUF
  1457:  @LF474:
  1458:          lda     zROBUF + 1
  1459:          bne     SetMemtop_And_Return_With_F0
  1460:          dey
  1461:          sty     zROBUF + 1
  1462:          stx     zROBUF
  1463:  
  1464:  SetMemtop_And_Return_With_F0:
  1465:          sec
  1466:          lda     #$F0
  1467:          jmp     iMEMTOP_Set
  1468:  
  1469:  .if CompileComputer >= C64_GENERAL
  1470:  LF483:  lda     #$7F
  1471:          sta     CIA2 + CIA_O_ICR
  1472:          lda     #$06
  1473:          sta     CIA2 + CIA_O_DDRB
  1474:          sta     CIA2 + CIA_O_PB
  1475:          lda     #$04
  1476:          ora     IEC_REG
  1477:          sta     IEC_REG
  1478:          ldy     #$00
  1479:          sty     lENABL
  1480:          rts
  1481:  .endif
  1482:  
  1483:  
  1484:  ;  B-15. Function Name: LOAD
  1485:  ;
  1486:  ;    Purpose: Load RAM from device
  1487:  ;    Call address: $FFD5 (hex) 65493 (decimal)
  1488:  ;    Communication registers: A, X, Y
  1489:  ;    Preparatory routines: SETLFS, SETNAM
  1490:  ;    Error returns: 0,4,5,8,9, READST
  1491:  ;    Stack requirements: None
  1492:  ;    Registers affected: A, X, Y
  1493:  ;
  1494:  ;    Description: This routine LOADs data bytes from any input device di-
  1495:  ;  rectly into the memory of the Commodore 64. It can also be used for a
  1496:  ;  verify operation, comparing data from a device with the data already in
  1497:  ;  memory, while leaving the data stored in RAM unchanged.
  1498:  ;    The accumulator (.A) must be set to 0 for a LOAD operation, or 1 for a
  1499:  ;  verify, If the input device is OPENed with a secondary address (SA) of 0
  1500:  ;  the header information from the device is ignored. In this case, the X
  1501:  ;  and Y registers must contain the starting address for the load. If the
  1502:  ;  device is addressed with a secondary address of 1, then the data is
  1503:  ;  loaded into memory starting at the location specified by the header. This
  1504:  ;  routine returns the address of the highest RAM location loaded.
  1505:  ;    Before this routine can be called, the KERNAL SETLFS, and SETNAM
  1506:  ;  routines must be called.
  1507:  ;
  1508:  ;
  1509:  ;  +-----------------------------------------------------------------------+
  1510:  ;  | NOTE: You can NOT LOAD from the keyboard (0), RS-232 (2), or the      |
  1511:  ;  | screen (3).                                                           |
  1512:  ;  +-----------------------------------------------------------------------+
  1513:  ;
  1514:  ;
  1515:  ;  How to Use:
  1516:  ;
  1517:  ;    0) Call the SETLFS, and SETNAM routines. If a relocated load is de-
  1518:  ;       sired, use the SETLFS routine to send a secondary address of 0.
  1519:  ;    1) Set the A register to 0 for load, 1 for verify.
  1520:  ;    2) If a relocated load is desired, the X and Y registers must be set
  1521:  ;       to the start address for the load.
  1522:  ;    3) Call the routine using the JSR instruction.
  1523:  ;
  1524:  ;
  1525:  ;
  1526:  ;
  1527:  ;
  1528:  ;
  1529:  ;  EXAMPLE:
  1530:  ;
  1531:  ;          ;LOAD   A FILE FROM TAPE
  1532:  ;
  1533:  ;           LDA #DEVICE1        ;SET DEVICE NUMBER
  1534:  ;           LDX #FILENO         ;SET LOGICAL FILE NUMBER
  1535:  ;           LDY CMD1            ;SET SECONDARY ADDRESS
  1536:  ;           JSR SETLFS
  1537:  ;           LDA #NAME1-NAME     ;LOAD A WITH NUMBER OF
  1538:  ;                               ;CHARACTERS IN FILE NAME
  1539:  ;           LDX #<NAME          ;LOAD X AND Y WITH ADDRESS OF
  1540:  ;           LDY #>NAME          ;FILE NAME
  1541:  ;           JSR SETNAM
  1542:  ;           LDA #0              ;SET FLAG FOR A LOAD
  1543:  ;           LDX #$FF            ;ALTERNATE START
  1544:  ;           LDY #$FF
  1545:  ;           JSR LOAD
  1546:  ;           STX VARTAB          ;END OF LOAD
  1547:  ;           STY VARTA B+1
  1548:  ;           JMP START
  1549:  ;   NAME    .BYT 'FILE NAME'
  1550:  ;   NAME1                       ;
  1551:  ;
  1552:  iLOAD:  stx     zMEMUSS_2                       ; remember alternate start at (zMEMUSS_2/zMEMUSS_2+1)
  1553:          sty     zMEMUSS_2 + 1
  1554:          jmp     (lILOAD)                        ; normally points to KLOAD
  1555:  
  1556:  KLOAD:
  1557:          sta     zVERCKK                         ; remember if LOAD (= 0) or VERIFY (= 1)
  1558:  
  1559:          lda     #0                              ; clear status
  1560:          sta     zSTATUS
  1561:  
  1562:          lda     zFA                             ; get device address
  1563:          bne     @NotKeyboard                    ; not keyboard (0) -> test for other devices
  1564:  
  1565:  @ErrIllegalDeviceNumber:
  1566:          jmp     KErrIllegalDeviceNumber         ; exit with "Illegal Device Number" error
  1567:          ; -----------------------------
  1568:  
  1569:  @NotKeyboard:
  1570:          cmp     #FILE_SCREEN
  1571:          beq     @ErrIllegalDeviceNumber         ; load from screen --> exit with "Illegal Device Number" error
  1572:  
  1573:          ; if C = 0, only tape and RS232 have been left.
  1574:  
  1575:  .ifdef JIFFY
  1576:          ; JiffyDOS does not support a tape, and load from RS232 is not possible.
  1577:          ; thus, if C=0, we are not able to load. Thus:
  1578:  
  1579:          bcc     @ErrIllegalDeviceNumber         ; RS232 or TAPE --> exit with "Illegal Device Number" error
  1580:  .elseif CompileComputer = C64_SX64
  1581:          ; the SX64 does not have a tape, and load from RS232 is not possible.
  1582:          ; thus, if C=0, we are not able to load. Thus:
  1583:  
  1584:          bcc     @ErrIllegalDeviceNumber         ; RS232 or TAPE --> exit with "Illegal Device Number" error
  1585:  .else
  1586:          bcc     KLOAD_TapeOrRS232               ; RS232 or TAPE --> try further tests
  1587:  .endif
  1588:  
  1589:  
  1590:          ; if we reach here, we want to load a file from IEC
  1591:  
  1592:  .if CompileComputer = VIC20_02
  1593:          lda     #IEEE_OPEN                      ; set secondary address: LOAD
  1594:          sta     zSA
  1595:  .endif
  1596:  
  1597:          ldy     zFNLEN                          ; get length of file name
  1598:          bne     KLOAD_FileNameGiven                     ; not 0 -> branch, name was given -> proceed
  1599:  .ifdef JIFFY
  1600:          jmp     $F659
  1601:  .else
  1602:          jmp     KErrFileNameMissing             ; error: We need a file name for IEC load --> return with "File Name Missing" error
  1603:  .endif
  1604:          ; -------------------------
  1605:  
  1606:  KLOAD_FileNameGiven:
  1607:  
  1608:  .if CompileComputer = VIC20_02
  1609:  
  1610:          ; on the VIC20-02 ROM, a "ldx zSA" is missing:
  1611:          ; TODO: Is this "ldx" needed at all?
  1612:  
  1613:          jsr     OutputSearchingFor              ; output the "Searching [for <FILENAME>]" message
  1614:  
  1615:  .elseif CompileComputer >= C64_GENERAL
  1616:  
  1617:          ; the C64 ROMs contain this ldx:
  1618:  
  1619:          ldx     zSA                             ; TODO: why?
  1620:          jsr     OutputSearchingFor              ; output the "Searching [for <FILENAME>]" message
  1621:  .else
  1622:          ; for the VIC20 ROMs (other than -02), the same sequence as in the C64 case is done in a patch that is called here:
  1623:  
  1624:          jsr     LE4BC
  1625:  .endif
  1626:  
  1627:  .if CompileComputer <> VIC20_02
  1628:          lda     #IEEE_LOAD                      ; set secondary address: LOAD
  1629:          sta     zSA
  1630:  .endif
  1631:  
  1632:          jsr     KOPEN_IEC                       ; open the file on the IEC bus. In case of a timeout, this function will not return here, but return to our caller!
  1633:  
  1634:          ; send a TALK command
  1635:          lda     zFA
  1636:          jsr     iTALK                           ; send TALK
  1637:          lda     zSA
  1638:          jsr     iTKSA                           ; and secondary address after TALK
  1639:  
  1640:          jsr     iACPTR                          ; read 1st byte from IEC
  1641:          sta     zEAL                            ; and store it as low byte of start address
  1642:  
  1643:          lda     zSTATUS                         ; check status bit 6 (EOI)
  1644:          lsr     a
  1645:          lsr     a
  1646:          bcs     KLOAD_ErrFileNotFound2          ; EOI -> return with "File Not Found" error
  1647:  
  1648:  .ifdef JIFFY
  1649:          jsr     JDLF179
  1650:  .else
  1651:          jsr     iACPTR                          ; read 2nd byte from IEC
  1652:  .endif
  1653:          sta     zEAL + 1                        ; and store it as high byte of start address
  1654:  
  1655:  .if 0
  1656:      ; this macro is defined in fileio_data.inc
  1657:  
  1658:      .macro LOAD_OVERWRITE_START_ADDRESS
  1659:          txa
  1660:  bne     :+
  1661:          lda     zMEMUSS
  1662:          sta     zEAL
  1663:          lda     zMEMUSS + 1
  1664:          sta     zEAL + 1
  1665:  :
  1666:  
  1667:      .endmacro
  1668:  .endif
  1669:  
  1670:  .if CompileComputer >= C64_GENERAL
  1671:          LOAD_OVERWRITE_START_ADDRESS
  1672:  .endif
  1673:  
  1674:  
  1675:  .ifdef JIFFY
  1676:  @LF4F0:
  1677:          jmp     JDLFAC4
  1678:  
  1679:  LF4F3:
  1680:          jsr     kSTOP
  1681:          bne     @LF4FB
  1682:          jmp     IecCloseBecauseStopKey
  1683:  @LF4FB:
  1684:          jsr     JDLFBAA
  1685:          lda     zSTATUS
  1686:          and     #$FD
  1687:          cmp     zSTATUS
  1688:          sta     zSTATUS
  1689:          bne     LF4F3
  1690:          ldy     #$00
  1691:          ldx     zTSFCNT
  1692:          lda     zTBTCNT
  1693:          cpy     zVERCKK
  1694:          beq     @LF51A
  1695:          cmp     (zEAL),y
  1696:          beq     @LF51C
  1697:          jsr     LF19E
  1698:          .byte   $2C
  1699:  @LF51A:
  1700:          sta     (zEAL),y
  1701:  @LF51C:
  1702:          stx     zTSFCNT
  1703:  
  1704:  .else
  1705:  
  1706:    .if CompileComputer >= C64_GENERAL .or CompileComputer = VIC20_02
  1707:          jsr     OutputLoadingOrVerify           ; output LOADING or VERIFYING messages
  1708:    .else
  1709:          ; the following patch contains:
  1710:          ; LOAD_OVERWRITE_START_ADDRESS
  1711:          ; jsr OutputLoadingOrVerify
  1712:  
  1713:          ; Thus, the implementation is identical to the C64
  1714:  
  1715:          jsr     LE4C1
  1716:    .endif
  1717:  
  1718:  LF4F3:
  1719:          lda     #~STATUS_IEC_TIMEOUT_READ       ; clear read timeout
  1720:          and     zSTATUS                         ; TODO: why?
  1721:          sta     zSTATUS
  1722:  
  1723:          jsr     kSTOP                           ; check if the stop key has been pressed
  1724:          bne     @ReadByte                       ; no stop key, proceed
  1725:  
  1726:          jmp     IecCloseBecauseStopKey          ; close file and return with error: Stopped because user pressed <STOP> key
  1727:  
  1728:  @ReadByte:
  1729:          jsr     iACPTR                          ; get data byte from IEC bus
  1730:          tax                                     ; remember it in X
  1731:  
  1732:          lda     zSTATUS                         ; check status bit 6 (EOI)
  1733:          lsr     a
  1734:          lsr     a
  1735:          bcs     LF4F3                           ; EOI -> there is no data to process -> branch
  1736:  
  1737:          txa                                     ; get back the data byte from the IEC bus
  1738:  
  1739:          ldy     zVERCKK                         ; test verify flag
  1740:          beq     @Load                           ; =0 --> LOAD --> Store data byte in memory
  1741:  
  1742:          ldy     #0                              ; make sure to compare the first byte
  1743:          cmp     (zEAL),y                        ; compare data byte
  1744:          beq     @IncrementAddress               ; it's the same -> everything ok -> branch
  1745:  
  1746:          lda     #STATUS_VERIFY                  ; set "verify error" bit
  1747:          jsr     SetStatus
  1748:  
  1749:          .byte   ASM_BIT3                        ; mask next instruction so it is not executed
  1750:  
  1751:  @Load:
  1752:          ; Y is already 0 here, as this is only executed when beq @Load after ldy zVERCKK has been taken...
  1753:          sta     (zEAL),y                        ; store data byte in memory
  1754:  
  1755:  .endif
  1756:  
  1757:  @IncrementAddress:
  1758:          inc     zEAL                            ; increment low address
  1759:  bne     :+                              ; if not zero, skip incrementing high address
  1760:          inc     zEAL + 1                        ; increment high address
  1761:  
  1762:  :       bit     zSTATUS                         ; test status bit 6 (EOI)
  1763:          bvc     LF4F3                           ; unset -> proceed with next byte
  1764:  
  1765:  KLOAD_UntalkClose:
  1766:          jsr     iUNTLK                          ; send UNTALK to device
  1767:          jsr     IecClose                        ; close the file
  1768:          bcc     LoadEndSuccess                  ; C=0 -> no error occurred -> quit with success
  1769:  
  1770:  KLOAD_ErrFileNotFound2:
  1771:          jmp     KErrFileNotFound                ; return with "File Not Found" error
  1772:          ; -------------------------
  1773:  
  1774:  KLOAD_TapeOrRS232:
  1775:          ; if we reach here, either a LOAD from TAPE (A=1) or from RS232 (=2) has been
  1776:          ; asked for
  1777:  
  1778:  .ifdef JIFFY
  1779:  
  1780:          lda     zFNLEN
  1781:          beq     LF546
  1782:          lda     (zFNADR),y
  1783:          cmp     #$24
  1784:          beq     LF56C
  1785:          jmp     JDLFC9A
  1786:          tya
  1787:  LF541:  pha
  1788:          jsr     JDLF8BF
  1789:          pla
  1790:  LF546:  sta     zBUFPNT
  1791:  LF548:  jsr     JDLF911
  1792:          bne     LF568
  1793:          lda     zBUFPNT
  1794:          php
  1795:          beq     LF557
  1796:          jsr     JDLE4C6
  1797:          beq     LF567
  1798:  LF557:  jsr     JDLF79A
  1799:          jsr     OutputFilename
  1800:          bit     zSTKEY
  1801:          bpl     LF567
  1802:          plp
  1803:          bne     LF548
  1804:          bvc     LF548
  1805:          .byte   $24
  1806:  LF567:  plp
  1807:  LF568:  rts
  1808:          ldx     #$6C
  1809:          .byte   $2C
  1810:  LF56C:  ldx     #$60
  1811:          jsr     JDLF8C1
  1812:          lda     #$39
  1813:          sta     lIERROR
  1814:          ldy     #$FC
  1815:          jsr     JDLFCA6
  1816:  LF57B:  ldy     #$00
  1817:  LF57D:  jsr     JDLFCA6
  1818:          bvs     LF5A3
  1819:          cpy     #$02
  1820:          beq     LF5A3
  1821:          cpy     #$06
  1822:          bcc     LF57D
  1823:          ldx     zFNADR
  1824:          stx     $5F
  1825:          ldx     $BC
  1826:          stx     $60
  1827:          ldy     #$01
  1828:          sta     ($5F),y
  1829:          jsr     JDLA6C3
  1830:          jsr     JDLF79A
  1831:          jsr     JDLA6D4
  1832:          bit     zSTKEY
  1833:          bmi     LF57B
  1834:  LF5A3:  lda     #$63
  1835:          sta     lIERROR
  1836:          rts
  1837:  
  1838:  .else
  1839:  
  1840:  .if CompileComputer >= C64_GENERAL
  1841:          lsr     a                               ; check if bit 0 of the device number is 1
  1842:          bcs     @Tape                           ; yes -> it is the tape -> branch, process LOAD from tape
  1843:          jmp     KErrIllegalDeviceNumber         ; load from RS232 not possible, quit with "Illegal Device Number" error
  1844:  .else
  1845:          cmp     #FILE_RS232                     ; test if the device number is the number of RS232
  1846:          bne     @Tape                           ; no -> it is the tape -> branch, process LOAD from tape
  1847:          jmp     Rs232ErrIllegalDeviceNumber     ; load from RS232 not possible, quit with "Illegal Device Number" error
  1848:  .endif
  1849:  
  1850:          ; TODO document load from TAPE
  1851:  @Tape:
  1852:          jsr     TapeGetPointer                          ; get pointer to tape buffer into (X/Y) (unused, but flags important)
  1853:  bcs     :+                                      ; C = 1 -> tape buffer does NOT point to stack page or zero page -> proceed, we do not overwrite essential data!
  1854:          jmp     KErrIllegalDeviceNumber
  1855:  
  1856:  :
  1857:          jsr     TapePressPlayOnTape             ; output "PRESS PLAY ON TAPE" and wait for the PLAY key to be pressed
  1858:          bcs     LoadRts                         ; C = 1 --> an error occurred (STOP key) --> branch, quit
  1859:  
  1860:          jsr     OutputSearchingFor              ; output "SEARCHING FOR ..."
  1861:  
  1862:  @LF549: lda     zFNLEN                          ; length of file name
  1863:  beq     :+                              ; 0 --> no file name specified -> skip
  1864:          jsr     TapeFindSpecificFile            ; try to find the specified file
  1865:          bcc     @FoundFile                      ; C = 0 -> no error occurred -> branch, proceed with operation
  1866:          beq     LoadRts                         ; Z = 1 --> an EOT was found
  1867:          bcs     KLOAD_ErrFileNotFound2          ; return with FILE NOT FOUND error
  1868:          ; ------------------------
  1869:  
  1870:  :
  1871:          jsr     TapeReadTapeHeaderOfNextFile    ; try to find ANY file header
  1872:          beq     LoadRts                         ; Z = 1 --> an EOT was found (BUG or TODO?: This is only valid if C=0, thus, this test would have to be the first one!)
  1873:          bcs     KLOAD_ErrFileNotFound2          ; C = 1 --> an error occurred -> branch, quit with FILE NOT FOUND error
  1874:  
  1875:  @FoundFile:
  1876:          lda     zSTATUS
  1877:          and     #STATUS_VERIFY                  ; TODO: STATUS_VERIFY or STATUS_TAPE_UNRECOVERABLE_READ_ERROR? The latter seems not to be generated (or I missed it until now)
  1878:          sec
  1879:          bne     LoadRts                         ; if the status indicated the error -> branch, we're done
  1880:  
  1881:          cpx     #TAPE_BUFFER_TYPE_BASIC         ; is this file a BASIC program?
  1882:          beq     @BASIC_Program                  ; yes -> branch, process the BASIC program
  1883:  
  1884:          cpx     #TAPE_BUFFER_TYPE_ABSOLUTE      ; is this file an absolute program?
  1885:          bne     @LF549                          ; no -> branch, search for the next file (there is a data file with the same name, skip it)
  1886:  
  1887:  @Absolute_Program:
  1888:          ldy     #TAPE_BUFFER_OFFSET_SAL_LOW     ; offset of start address low in tape buffer
  1889:          lda     (zTAPE1),y                      ; read the start address low
  1890:          sta     zMEMUSS_2                       ; and store it
  1891:          iny                                     ; advance offset to start address high
  1892:          lda     (zTAPE1),y                      ; read the start address high
  1893:          sta     zMEMUSS_2 + 1                   ; and store it
  1894:          bcs     @LoadFile                       ; (uncond. branch: TAPE_BUFFER_TYPE_DATA or TAPE_BUFFER_TYPE_EOT can be ruled out here, thus, the CPX resulted in a C=1)
  1895:          ; -------------------
  1896:  
  1897:  @BASIC_Program:
  1898:          lda     zSA                             ; get secondary address used to open the file
  1899:          bne     @Absolute_Program               ; not 0 --> we want to read the file absolute, ignore that it is written as BASIC program and load it absolute instead
  1900:  
  1901:          ; If the above branch did not branch and we reach this place from above (not via bcs @LoadFile), then we have a BASIC program.
  1902:          ; The start address to load it to is already at zMEMUSS_2/zMEMUSS_2 + 1.
  1903:  
  1904:  @LoadFile:
  1905:          ; Calculate the end address and write it into zEAL/zEAL+1
  1906:  
  1907:          ; sec                                   ; C=1 is always true here, thus, no need for an SEC
  1908:  
  1909:          ldy     #TAPE_BUFFER_OFFSET_EAL_LOW     ; (offset of end address low in tape buffer)
  1910:          lda     (zTAPE1),y                      ; get end address low
  1911:          ldy     #TAPE_BUFFER_OFFSET_SAL_LOW     ; (offset of start address low in tape buffer)
  1912:          sbc     (zTAPE1),y                      ; subtract end address low - start address low
  1913:          tax                                     ; remember the result in X
  1914:  
  1915:          ldy     #TAPE_BUFFER_OFFSET_EAL_HIGH    ; (offset of end address high in tape buffer)
  1916:          lda     (zTAPE1),y                      ; get end address high
  1917:          ldy     #TAPE_BUFFER_OFFSET_SAL_HIGH    ; (offset of start address high in tape buffer)
  1918:          sbc     (zTAPE1),y                      ; subtract end address high - start address high
  1919:          tay                                     ; remember the result in Y
  1920:  
  1921:          ; now, add the length (in X/Y) t zMEMUSS_2/zMEMUSS_2+1, and store the result in zEAL/zEAL+1
  1922:          clc
  1923:          txa                                     ; get length low
  1924:          adc     zMEMUSS_2                       ; add it to the start address low
  1925:          sta     zEAL                            ; and store it as end address low
  1926:  
  1927:          tya                                     ; get length high
  1928:          adc     zMEMUSS_2 + 1                   ; add it to the start address high
  1929:          sta     zEAL + 1                        ; and store it as end address high
  1930:  
  1931:          ; copy the start address from zMEMUSS_2/zMEMUSS_2+1 to zSTAL/zSTAL+1
  1932:  
  1933:          lda     zMEMUSS_2
  1934:          sta     zSTAL
  1935:          lda     zMEMUSS_2 + 1
  1936:          sta     zSTAL + 1
  1937:  
  1938:          jsr     OutputLoadingOrVerify           ; output LOADING or VERIFYING messages
  1939:  
  1940:          jsr     TapeReadFileContents            ; read in the next buffer from the tape
  1941:          .byte   ASM_BIT2                        ; make
  1942:  
  1943:  .endif
  1944:  
  1945:  LoadEndSuccess:
  1946:          clc                                     ; mark: success
  1947:          ldx     zEAL                            ; return end address (+1) in X/Y
  1948:          ldy     zEAL + 1
  1949:  LoadRts:
  1950:          rts
  1951:          ; --------------
  1952:  
  1953:  OutputSearchingFor:
  1954:          lda     zNSGFLG                         ; do we output messages "searching for", or is it prohibited?
  1955:          bpl     OutputSearchingFor_Rts          ; no -> branch -> quit, we don't want to output anything
  1956:  
  1957:          ldy     #StrSearching - LMESSAGES       ; offset of "Searching" message
  1958:          jsr     OutputMessage                   ; output it
  1959:  
  1960:          lda     zFNLEN                          ; test length of file name
  1961:          beq     OutputSearchingFor_Rts          ; length = 0 -> no filename -> branch -> quit, nothing else to do
  1962:  
  1963:          ldy     #StrFor - LMESSAGES             ; offset of " for " message
  1964:          jsr     OutputMessage                   ; output it
  1965:  
  1966:  OutputFilename:
  1967:          ldy     zFNLEN                          ; test length of file name
  1968:          beq     OutputSearchingFor_Rts          ; length = 0 -> no filename -> branch -> quit, nothing else to do
  1969:  
  1970:          ldy     #0                              ; start with first character
  1971:  :       lda     (zFNADR),y                      ; get character of file name
  1972:          jsr     kCHROUT                         ; and output it
  1973:          iny                                     ; advance to next character
  1974:          cpy     zFNLEN                          ; did we reach the end of the file name?
  1975:  bne     :-                              ; no, process next character
  1976:  
  1977:  OutputSearchingFor_Rts:
  1978:          rts
  1979:          ; --------------------
  1980:  
  1981:  OutputLoadingOrVerify:
  1982:          ldy     #StrLoading - LMESSAGES         ; offset of "LOADING" message
  1983:          lda     zVERCKK                         ; check verify flag
  1984:          beq     @Output                         ; 0 -> LOAD -> output LOADING"
  1985:          ldy     #StrVerifying - LMESSAGES       ; otherwise, it is a VERIFY: Get offset of "VERIFYING" message
  1986:  @Output:
  1987:          jmp     OutputMessageIfAllowed          ; output the message, if not prohibited
  1988:  
  1989:  
  1990:  
  1991:  ;  B-24. Function Name: SAVE
  1992:  ;
  1993:  ;    Purpose: Save memory to a device
  1994:  ;    Call address: $FFD8 (hex) 65496 (decimal)
  1995:  ;    Communication registers: A, X, Y
  1996:  ;    Preparatory routines: SETLFS, SETNAM
  1997:  ;    Error returns: 5,8,9, READST
  1998:  ;    Stack requirements: None
  1999:  ;    Registers affected: A, X, Y
  2000:  ;
  2001:  ;
  2002:  ;
  2003:  ;    Description: This routine saves a section of memory. Memory is saved
  2004:  ;  from an indirect address on page 0 specified by the accumulator to the
  2005:  ;  address stored in the X and Y registers. It is then sent to a logical
  2006:  ;  file on an input/output device. The SETLFS and SETNAM routines must be
  2007:  ;  used before calling this routine. However, a file name is not required to
  2008:  ;  SAVE to device 1 (the Datassette(TM) recorder). Any attempt to save to
  2009:  ;  other devices without using a file name results in an error.
  2010:  ;
  2011:  ;  +-----------------------------------------------------------------------+
  2012:  ;  | NOTE: Device 0 (the keyboard), device 2 (RS-232), and device 3 (the   |
  2013:  ;  | screen) cannot be SAVEd to. If the attempt is made, an error occurs,  |
  2014:  ;  | and the SAVE is stopped.                                              |
  2015:  ;  +-----------------------------------------------------------------------+
  2016:  ;
  2017:  ;  How to Use:
  2018:  ;
  2019:  ;    0) Use the SETLFS routine and the SETNAM routine (unless a SAVE with no
  2020:  ;       file name is desired on "a save to the tape recorder"),
  2021:  ;    1) Load two consecutive locations on page 0 with a pointer to the start
  2022:  ;       of your save (in standard 6502 low byte first, high byte next
  2023:  ;       format).
  2024:  ;    2) Load the accumulator with the single byte page zero offset to the
  2025:  ;       pointer.
  2026:  ;    3) Load the X and Y registers with the low byte and high byte re-
  2027:  ;       spectively of the location of the end of the save.
  2028:  ;    4) Call this routine.
  2029:  ;
  2030:  ;  EXAMPLE:
  2031:  ;
  2032:  ;    LDA #1              ;DEVICE = 1:CASSETTE
  2033:  ;    JSR SETLFS
  2034:  ;    LDA #0              ;NO FILE NAME
  2035:  ;    JSR SETNAM
  2036:  ;    LDA PROG            ;LOAD START ADDRESS OF SAVE
  2037:  ;    STA TXTTAB          ;(LOW BYTE)
  2038:  ;    LDA PROG+1
  2039:  ;    STA TXTTA B+1       ;(HIGH BYTE)
  2040:  ;    LDX VARTAB          ;LOAD X WITH LOW BYTE OF END OF SAVE
  2041:  ;    LDY VARTAB+1        ;LOAD Y WITH HIGH BYTE
  2042:  ;    LDA #<TXTTAB        ;LOAD ACCUMULATOR WITH PAGE 0 OFFSET
  2043:  ;    JSR SAVE
  2044:  ;
  2045:  ;
  2046:  iSAVE:
  2047:          stx     zEAL                            ; store end address of save at (zEAL/zEAL+1)
  2048:          sty     zEAL + 1
  2049:  
  2050:          ; store start address at (zSTAL/zSTAL + 1)
  2051:  
  2052:          tax                                     ; get index of zero page location that contains the start address into X
  2053:          lda     0,x                             ; get start address low
  2054:          sta     zSTAL
  2055:          lda     0 + 1,x                         ; get start address high
  2056:          sta     zSTAL + 1
  2057:          jmp     (lISAVE)                        ; points to KSAVE normally
  2058:  
  2059:  KSAVE:
  2060:          lda     zFA                             ; get device number
  2061:          bne     SaveNotKeyboard                 ; not keyboard (0) -> branch, test for other devices
  2062:  
  2063:  SaveErrIllegalDeviceNumber:
  2064:          jmp     KErrIllegalDeviceNumber         ; return with "Illegal Device Number" error
  2065:  
  2066:  SaveNotKeyboard:
  2067:          cmp     #FILE_SCREEN                    ; device number screen (3)?
  2068:          beq     SaveErrIllegalDeviceNumber      ; yes -> return with "Illegal Device Number" error
  2069:  
  2070:          ; if we reach here, only tape and RS232 are left with C=0
  2071:          ; (and IEC with C=1)
  2072:  .ifdef JIFFY
  2073:          bcc     SaveErrIllegalDeviceNumber      ; on the SX64, tape and RS232 are both illegal -> return with error
  2074:  .elseif CompileComputer = C64_SX64
  2075:          bcc     SaveErrIllegalDeviceNumber      ; on the SX64, tape and RS232 are both illegal -> return with error
  2076:  .else
  2077:          bcc     SaveTapeOrRs232                 ; check if tape or RS232
  2078:  .endif
  2079:  
  2080:          ; if we reach here, we want to save on IEC bus
  2081:  
  2082:          lda     #IEEE_SAVE                      ; set the secondary address
  2083:          sta     zSA                             ; to the address of a save
  2084:  
  2085:          ldy     zFNLEN                          ; is there a file name?
  2086:          bne     LF605                           ; yes -> everything ok, proceed
  2087:  
  2088:  SAVE_KErrFileNameMissing:
  2089:          jmp     KErrFileNameMissing             ; return with "File Name Missing" error
  2090:  
  2091:  LF605:
  2092:          jsr     KOPEN_IEC                       ; open the file on the IEC bus
  2093:          jsr     OutputSaving                    ; output the "SAVING" message
  2094:  
  2095:          lda     zFA                             ; send LISTEN to device
  2096:          jsr     iLISTEN
  2097:          lda     zSA                             ; with correspondig SA
  2098:          jsr     iSECOND
  2099:  
  2100:          ldy     #0                              ; index for writing data (for later, when receiving data)
  2101:  
  2102:          jsr     Copy_zSTAL_to_zSAL              ; save start address into pointer which will be used for saving
  2103:  
  2104:          ; output the start address as two first bytes
  2105:          lda     zSAL                            ; low byte
  2106:          jsr     iCIOUT
  2107:          lda     zSAL + 1                        ; high byte
  2108:          jsr     iCIOUT
  2109:  
  2110:  SAVE_Loop:
  2111:          jsr     HasEndAddressBeenReached        ; check if working address (zSAL/zSAL+1) has reached end address (zEAL/zEAL+1)
  2112:          bcs     SAVE_End                        ; reached -> branch
  2113:  
  2114:          lda     (zSAL),y                        ; byte to be saved
  2115:          jsr     iCIOUT                          ; output to IEC
  2116:  
  2117:          jsr     kSTOP                           ; check if <stop> has been pressed
  2118:          bne     SAVE_NoStop                     ; no -> branch, process next byte (if any)
  2119:  
  2120:  IecCloseBecauseStopKey:
  2121:          jsr     IecClose                        ; close the file on the IEC bus
  2122:  
  2123:          lda     #0                              ; error: Routine terminated by the <STOP> key
  2124:          sec                                     ; mark: error return
  2125:          rts
  2126:          ; --------------
  2127:  
  2128:  SAVE_NoStop:
  2129:          jsr     Increment_zSAL_Address          ; increment working address
  2130:          bne     SAVE_Loop                       ; if no "wrap around" to address 0, save the next byte
  2131:  
  2132:  
  2133:  SAVE_End:
  2134:          jsr     iUNLSN                          ; send UNLISTEN to device
  2135:  
  2136:  IecClose:
  2137:          ; Close an IEC file by sending a LISTEN with the secondary address specifying "CLOSE"
  2138:          bit     zSA                             ; secondary address
  2139:          bmi     SAVE_ClcRts                     ; bit 7 set -> branch, we are done
  2140:  
  2141:          lda     zFA                             ; send listen to device
  2142:          jsr     iLISTEN
  2143:  
  2144:          lda     zSA                             ; send secondary address "CLOSE"
  2145:          and     #$EF
  2146:          ora     #IEEE_CLOSE
  2147:          jsr     iSECOND
  2148:  
  2149:  DoUnlistenClcRts:
  2150:          jsr     iUNLSN                          ; send UNLISTEN
  2151:  
  2152:  SAVE_ClcRts:
  2153:          clc
  2154:          rts
  2155:          ; --------------
  2156:  
  2157:  SaveTapeOrRs232:
  2158:  
  2159:  .ifdef JIFFY
  2160:  
  2161:  LF659:  lda     zNDX
  2162:          beq     SAVE_KErrFileNameMissing
  2163:          lda     #$02
  2164:          sta     zSA
  2165:          ldx     #$74
  2166:          ldy     #$F6
  2167:          jsr     kSETNAM
  2168:          jmp     KLOAD_FileNameGiven
  2169:  LF66B:  ldx     #$33
  2170:          ldy     #$04
  2171:          jmp     JDLF932
  2172:  
  2173:          .byte   $40, $24, $3a, $2a, $0d, $00, $2f, $00
  2174:          .byte   $5e, $00, $25, $00, $40, $44, $00, $40
  2175:          .byte   $54, $00, $5f, $00, $40, $20, $20, $22
  2176:          .byte   $53, $3a, $00
  2177:  
  2178:  .else
  2179:  
  2180:          ; if we reach here, either a SAVE from TAPE (A=1) or from RS232 (=2) has been
  2181:          ; asked for
  2182:  
  2183:  .if CompileComputer >= C64_GENERAL
  2184:          lsr     a                               ; check if bit 0 of the device number is 1
  2185:          bcs     @Tape                           ; yes -> it is the tape -> branch, process SAVE from tape
  2186:          jmp     KErrIllegalDeviceNumber         ; save to RS232 not possible, quit with "Illegal Device Number" error
  2187:  .else
  2188:          cmp     #FILE_RS232                     ; test if the device number is the number of RS232
  2189:          bne     @Tape                           ; no -> it is the tape -> branch, process SAVE to tape
  2190:          jmp     Rs232ErrIllegalDeviceNumber     ; save to RS232 not possible, quit with "Illegal Device Number" error
  2191:  .endif
  2192:  
  2193:  @Tape:
  2194:          jsr     TapeGetPointer                  ; is the tape buffer set (high byte >= 2)?
  2195:          bcc     SaveErrIllegalDeviceNumber      ; no -> abort operation, as we would overwrite ZP or stack
  2196:  
  2197:          jsr     TapePressRecordAndPlayOnTape    ; output "PRESS RECORD + PLAY ON TAPE" and wait for PLAY to be pressd
  2198:          bcs     SAVE_Rts                        ; C=1 -> an error occurred (i.e., STOP was pressed) -> branch, abort
  2199:  
  2200:          jsr     OutputSaving                    ; Output the text "SAVING <filename>"
  2201:  
  2202:          ; determine the tape buffer type to generate for the first block (filename)
  2203:  
  2204:          ldx     #TAPE_BUFFER_TYPE_ABSOLUTE      ; assume: file is a absolute loading program (not BASIC)
  2205:  
  2206:          lda     zSA                             ; check secondary address
  2207:          and     #$01                            ; bit 0 set?
  2208:  bne     :+                              ; yes -> branch, skip
  2209:  
  2210:          ldx     #TAPE_BUFFER_TYPE_BASIC         ; no -> file is a BASIC program
  2211:  
  2212:  :       txa
  2213:          jsr     TapeCreateFileBuffer            ; create the file buffer (including start and end address and file name) and write it on tape
  2214:          bcs     SAVE_Rts                        ; C = 1 -> an error occurred -> branch, quit save with an error
  2215:  
  2216:          jsr     TapeWriteCompleteFile           ; write out the file to tape
  2217:          bcs     SAVE_Rts                        ; C = 1 -> an error occurred -> branch, quit save with an error
  2218:  
  2219:          ; check if bit 1 of the secondary address is set. If so, we have to write an end-of-tape (EOT) marker on the tape
  2220:          lda     zSA                             ; get secondary address
  2221:          and     #$02                            ; is bit 1 set?
  2222:          beq     @ClcRts                         ; no -> branch, we're done
  2223:  
  2224:          lda     #TAPE_BUFFER_TYPE_EOT           ; tape buffer type: END-OF-TAPE (EOT)
  2225:          jsr     TapeCreateFileBuffer            ; create the file buffer and write it on tape
  2226:  
  2227:          .byte   ASM_BIT2                        ; make sure not to loose the status of C by hiding the next instruction
  2228:  
  2229:  .endif
  2230:  
  2231:  @ClcRts:
  2232:          clc
  2233:  SAVE_Rts:
  2234:          rts
  2235:          ; ---------------------
  2236:  
  2237:  OutputSaving:
  2238:          lda     zNSGFLG                         ; do we output messages "searching for", or is it prohibited?
  2239:          bpl     SAVE_Rts                        ; no -> branch -> quit, we don't want to output anything
  2240:  
  2241:          ldy     #StrSaving - LMESSAGES          ; offset of "Saving" message
  2242:          jsr     OutputMessage                   ; output it
  2243:  
  2244:          jmp     OutputFilename                  ; output the file name of file to be saved
  2245:          ; ---------------------
  2246:  
  2247:  
  2248:  ;  B-36. Function Name: UDTIM
  2249:  ;
  2250:  ;    Purpose: Update the system clock
  2251:  ;    Call address: $FFEA (hex) 65514 (decimal)
  2252:  ;    Communication registers: None
  2253:  ;    Preparatory routines: None
  2254:  ;    Error returns: None
  2255:  ;    Stack requirements: 2
  2256:  ;    Registers affected: A, X
  2257:  ;
  2258:  ;    Description: This routine updates the system clock. Normally this
  2259:  ;  routine is called by the normal KERNAL interrupt routine every 1/60th of
  2260:  ;  a second. If the user program processes its own interrupts this routine
  2261:  ;  must be called to update the time. In addition, the <STOP> key routine
  2262:  ;  must be called, if the <STOP> key is to remain functional.
  2263:  ;
  2264:  ;  How to Use:
  2265:  ;    1) Call this routine.
  2266:  ;
  2267:  ;  EXAMPLE:
  2268:  ;
  2269:  ;    JSR UDTIM
  2270:  ;
  2271:  ;
  2272:  iUDTIM:
  2273:          ldx     #0                              ; will be used in the case of a wrap-around of the timer for setting the timer back to zero.
  2274:  
  2275:          inc     zTIME + 2                       ; increment lowest byte
  2276:          bne     @CheckOverflow                  ; no overflow -> done incrementing
  2277:  
  2278:          inc     zTIME + 1                       ; increment middle byte
  2279:          bne     @CheckOverflow                  ; no overflow -> done incrementing
  2280:  
  2281:          inc     zTIME                           ; increment highest byte
  2282:  
  2283:  @CheckOverflow:
  2284:  
  2285:          ; now, check if the timer overflowed, that is, 24h have been reached
  2286:          ; for this, subtract the constant for 24h (24*60*60*60)+1 from the
  2287:          ; current value
  2288:  
  2289:          sec
  2290:          lda     zTIME + 2
  2291:          sbc     #<((24*60*60*60)+1)
  2292:          lda     zTIME + 1
  2293:          sbc     #>((24*60*60*60)+1)
  2294:          lda     zTIME
  2295:          sbc     #^((24*60*60*60)+1)
  2296:  
  2297:          ; if carry is not set, the timer value is smaller -> branch, no need to perform wrap-around
  2298:          bcc     iUDTIM_CheckRunStop
  2299:  
  2300:          ; we had a wrap-around, set timer value to 0
  2301:          stx     zTIME
  2302:          stx     zTIME + 1
  2303:          stx     zTIME + 2
  2304:  
  2305:  iUDTIM_CheckRunStop:
  2306:          ; check if the Run/STOP key has been pressed
  2307:  
  2308:          ; TODO document
  2309:  
  2310:          lda     KEYB_COL_FOR_STOP
  2311:          cmp     KEYB_COL_FOR_STOP
  2312:          bne     iUDTIM_CheckRunStop
  2313:  
  2314:  .if CompileComputer >= C64_GENERAL
  2315:          tax
  2316:          bmi     @StoreStop
  2317:          ldx     #KEYB_ROW_STOP
  2318:          stx     KEYB_ROW
  2319:  @Debounce:
  2320:          ldx     KEYB_COL_FOR_STOP
  2321:          cpx     KEYB_COL_FOR_STOP
  2322:          bne     @Debounce
  2323:          sta     KEYB_ROW
  2324:          inx
  2325:          bne     @Rts
  2326:  .endif
  2327:  
  2328:  @StoreStop:
  2329:          sta     zSTKEY
  2330:  @Rts:
  2331:          rts
  2332:  
  2333:  
  2334:  ;  B-21. Function Name: RDTIM
  2335:  ;
  2336:  ;    Purpose: Read system clock
  2337:  ;    Call address: $FFDE (hex) 65502 (decimal)
  2338:  ;    Communication registers: A, X, Y
  2339:  ;    Preparatory routines: None
  2340:  ;    Error returns: None
  2341:  ;    Stack requirements: 2
  2342:  ;    Registers affected: A, X, Y
  2343:  ;
  2344:  ;    Description: This routine is used to read the system clock. The clock's
  2345:  ;  resolution is a 60th of a second. Three bytes are returned by the
  2346:  ;  routine. The accumulator contains the most significant byte, the X index
  2347:  ;  register contains the next most significant byte, and the Y index
  2348:  ;  register contains the least significant byte.
  2349:  ;
  2350:  ;  EXAMPLE:
  2351:  ;
  2352:  ;    JSR RDTIM
  2353:  ;    STY TIME
  2354:  ;    STX TIME+1
  2355:  ;    STA TIME+2
  2356:  ;    ...
  2357:  ;    TIME *=*+3
  2358:  ;
  2359:  ;
  2360:  iRDTIM:
  2361:          sei                             ; make sure we get not interrupted (atomicity)
  2362:  
  2363:          ; read the values of the time
  2364:          lda     zTIME + 2               ; high byte
  2365:          ldx     zTIME + 1               ; middle byte
  2366:          ldy     zTIME                   ; low byte
  2367:  
  2368:  ;  B-31. Function Name: SETTIM
  2369:  ;
  2370:  ;    Purpose: Set the system clock
  2371:  ;    Call address: $FFDB (hex) 65499 (decimal)
  2372:  ;    Communication registers: A, X, Y
  2373:  ;    Preparatory routines: None
  2374:  ;    Error returns: None
  2375:  ;    Stack requirements: 2
  2376:  ;    Registers affected: None
  2377:  ;
  2378:  ;
  2379:  ;
  2380:  ;    Description: A system clock is maintained by an interrupt routine that
  2381:  ;  updates the clock every 1/60th of a second (one "jiffy"). The clock is
  2382:  ;  three bytes long, which gives it the capability to count up to 5,184,000
  2383:  ;  jiffies (24 hours). At that point the clock resets to zero. Before
  2384:  ;  calling this routine to set the clock, the accumulator must contain the
  2385:  ;  most significant byte, the X index register the next most significant
  2386:  ;  byte, and the Y index register the least significant byte of the initial
  2387:  ;  time setting (in jiffies).
  2388:  ;
  2389:  ;  How to Use:
  2390:  ;    1) Load the accumulator with the MSB of the 3-byte number to set the
  2391:  ;       clock.
  2392:  ;    2) Load the X register with the next byte.
  2393:  ;    3) Load the Y register with the LSB.
  2394:  ;    4) Call this routine.
  2395:  ;
  2396:  ;  EXAMPLE:
  2397:  ;   ;SET THE CLOCK TO 10 MINUTES = 3600 JIFFIES
  2398:  ;   LDA #0               ;MOST SIGNIFICANT
  2399:  ;   LDX #>3600
  2400:  ;   LDY #<3600           ;LEAST SIGNIFICANT
  2401:  ;   JSR SETTIM
  2402:  ;
  2403:  iSETTIM:
  2404:          sei                             ; make sure we get not interrupted (atomicity)
  2405:  
  2406:          ; set the values of the time
  2407:          sta     zTIME + 2               ; high byte
  2408:          stx     zTIME + 1               ; middle byte
  2409:          sty     zTIME                   ; low byte
  2410:  
  2411:          cli                             ; the critical section is over
  2412:  
  2413:          rts
  2414:          ; ----------------
  2415:  
  2416:  ;  B-33. Function Name: STOP
  2417:  ;
  2418:  ;    Purpose: Check if <STOP> key is pressed
  2419:  ;    Call address: $FFE1 (hex) 65505 (decimal)
  2420:  ;    Communication registers: A
  2421:  ;    Preparatory routines: None
  2422:  ;    Error returns: None
  2423:  ;    Stack requirements: None
  2424:  ;    Registers affected: A, X
  2425:  ;
  2426:  ;    Description: If the <STOP> key on the keyboard was pressed during a
  2427:  ;  UDTIM call, this call returns the Z flag set. In addition, the channels
  2428:  ;  will be reset to default values. All other flags remain unchanged. If the
  2429:  ;  <STOP> key is not pressed then the accumulator will contain a byte
  2430:  ;  representing the lost row of the keyboard scan. The user can also check
  2431:  ;  for certain other keys this way.
  2432:  ;
  2433:  ;  How to Use:
  2434:  ;    0) UDTIM should be called before this routine.
  2435:  ;    1) Call this routine.
  2436:  ;    2) Test for the zero flag.
  2437:  ;
  2438:  ;
  2439:  ;  EXAMPLE:
  2440:  ;
  2441:  ;    JSR UDTIM   ;SCAN FOR STOP
  2442:  ;    JSR STOP
  2443:  ;    BNE *+5     ;KEY NOT DOWN
  2444:  ;    JMP READY   ;=... STOP
  2445:  ;
  2446:  KSTOP:
  2447:          lda     zSTKEY
  2448:          cmp     #KEYB_CHECK_STOP                ; was <STOP> key pressed?
  2449:          bne     @Rts                            ; no -> branch, quit
  2450:  
  2451:          php                                     ; make sure to preserve the status of Z (=1)
  2452:  
  2453:          jsr     kCLRCHN                         ; clear default input and output devices
  2454:                                                  ; (returns with A=0)
  2455:          sta     zNDX                            ; mark: No characters in the keyboard buffer
  2456:  
  2457:          plp                                     ; get back the status of Z (=1)
  2458:  
  2459:  @Rts:   rts
  2460:          ; -----------------
  2461:  
  2462:  ;  ERROR CODES
  2463:  ;
  2464:  ;    The following is a list of error messages which can occur when using
  2465:  ;  the KERNAL routines. If an error occurs during a KERNAL routine , the
  2466:  ;  carry bit of the accumulator is set, and the number of the error message
  2467:  ;  is returned in the accumulator.
  2468:  ;  +-----------------------------------------------------------------------+
  2469:  ;  | NOTE: Some KERNAL I/O routines do not use these codes for error       |
  2470:  ;  | messages. Instead, errors are identified using the KERNAL READST      |
  2471:  ;  | routine.                                                              |
  2472:  ;  +-----------------------------------------------------------------------+
  2473:  ;  +-------+---------------------------------------------------------------+
  2474:  ;  | NUMBER|                          MEANING                              |
  2475:  ;  +-------+---------------------------------------------------------------+
  2476:  ;  |   0   |  Routine terminated by the <STOP> key                         |
  2477:  ;  |   1   |  Too many open files                                          |
  2478:  ;  |   2   |  File already open                                            |
  2479:  ;  |   3   |  File not open                                                |
  2480:  ;  |   4   |  File not found                                               |
  2481:  ;  |   5   |  Device not present                                           |
  2482:  ;  |   6   |  File is not an input file                                    |
  2483:  ;  |   7   |  File is not an output file                                   |
  2484:  ;  |   8   |  File name is missing                                         |
  2485:  ;  |   9   |  Illegal device number                                        |
  2486:  ;  |  240  |  Top-of-memory change RS-232 buffer allocation/deallocation   |
  2487:  ;  +-------+---------------------------------------------------------------+
  2488:  KErrTooManyOpenFiles:
  2489:          lda     #$01
  2490:          .byte   ASM_BIT3
  2491:  
  2492:  KErrFileOpen:
  2493:          lda     #$02
  2494:          .byte   ASM_BIT3
  2495:  
  2496:  KErrFileNotOpen:
  2497:          lda     #$03
  2498:          .byte   ASM_BIT3
  2499:  
  2500:  KErrFileNotFound:
  2501:          lda     #$04
  2502:          .byte   ASM_BIT3
  2503:  
  2504:  KErrDeviceNotPresent:
  2505:          lda     #$05
  2506:          .byte   ASM_BIT3
  2507:  
  2508:  KErrNotInputFile:
  2509:          lda     #$06
  2510:          .byte   ASM_BIT3
  2511:  
  2512:  KErrNotOutputFile:
  2513:          lda     #$07
  2514:          .byte   ASM_BIT3
  2515:  
  2516:  KErrFileNameMissing:
  2517:          lda     #$08
  2518:          .byte   ASM_BIT3
  2519:  
  2520:  KErrIllegalDeviceNumber:
  2521:          lda     #$09
  2522:  
  2523:          pha                             ; remember error number
  2524:          jsr     kCLRCHN                 ; restore output and input to console
  2525:          ldy     #StrIoError - LMESSAGES ; prepare to output "I/O ERROR #" by getting its index
  2526:  
  2527:          bit     zNSGFLG                 ; check the flag which has the message output policy
  2528:          bvc     @NoOutput               ; test bit 6: Output error message. If not set -> branch, do not output text
  2529:  
  2530:          jsr     OutputMessage           ; output the message with index in Y
  2531:  
  2532:          pla                             ; get back error number
  2533:          pha                             ; and remember it again
  2534:  
  2535:          ora     #'0'                    ; convert it to ASCII ('0' - '9')
  2536:          jsr     kCHROUT                 ; and output it
  2537:  
  2538:  @NoOutput:
  2539:          pla                             ; get back the error number
  2540:          sec                             ; mark: An error occurred
  2541:          rts
  2542:          ; --------------
Valid XHTML 1.0 Strict
fileio.a65.html; generated on Fri Sep 18 21:44:54 2015 by ca65html
uz@cc65.org