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: ; --------------