THE ATARI® GAZETTE
Cassette Boot-Tape Generation
From DOS 2.0S Binary-Load File
Raymond W. Polone
SYZGY Microware of Texas
The binary-load file is a very easy-to-use method of accessing 6502 machine code instructions for ATARI users. The Model CXL4003 ASSEMBLER EDITOR User's Manual provides a method, using BASIC, to enter object code from a cassette tape. This article introduces a BASIC program technique that permits a user to generate a short-inter-record gap bootable cassette tape from a binary-load file. Such a tape then provides the option of using a language cartridge (i.e. BASIC or ASSEMBLER) or booting in the program with no cartridge — and no DOS!
Program 1 is the ASSEMBLER/EDITOR text listing of an appropriate handler-record that must be written on the bootable tape. The initial byte of zero at statement 40 is a flag byte used by the OS in the cassette boot operation. I have assembled the second byte as a value one, but the BASIC program must provide the correct value. This byte is a count of 128 byte records in the boot process (i.e. a value of 1 indicates the boot is 128 bytes). A value of zero indicates the boot is 128*256 or 32,768 bytes. The BASIC program in Program 2 generates the correct SIZE value in statement 26. The object code of Program 1 is contained in DATA statement 56 of the BASIC program. (The BASIC program reads the DATA into BUF$ at statement 2.) The correct 128 count is then saved in BUF$ at statement 27.
The BASIC program must also provide the correct cassette boot load address in the value at statement 50 of Program 1. This BOOT value is calculated in statement 9 after the program has detected the two flag bytes of 255 ($FF) in the binary-load file. BUF$ is updated at statement 21 (the length of the boot-handler is subtracted from the binary-load file RAM location — this means that the boot-handler is appended in RAM at the beginning of the binary-load data).
The COLDSTART address or binary-load file-run address is provided to the value at statement 60 of the assembler routine by the BASIC program at statement 25 if the binary-load file is not load-and-go (no RUN-address appended to the binary-load file in locations $2E0 and $2E1). The user is prompted to enter the decimal value in statement 22. $A000 entered as decimal 40960 will result in the BASIC or ASSEMBLER cartridge gaining control (they must be resident, of course) following the cassette boot. The binary-load INIT address in $2E2 and $2E3 are not supported in this BASIC program, however, if the RUN-address has been appended to the file — that address will be used in statement 45 (RAY is a switch that indicates RUN-address has been found). The binary-load file must be a series of increasing RAM addresses! If there is a gap of addresses, the cassette boot will include the required block of zero RAM (ATARI hearts). (It may be desirable to DIMension BUF$ as the last variable in statement 1 to reduce the possibility that RAM used in the cassette put-character XIO operation will write extraneous data.) The only exception is the RUN-address which will not be included in the RAM block. (This cassette boot is not multistage.)
The remainder of Program 1 is the routine that will get control as a result of the cassette boot (POWER-ON with START depressed). BOOT? at RAM location 9 is set to 2 to indicate the cassette vector at RAM locations 2 and 3 is to be used on a SYSTEM-RESET. COLDSTART at RAM location $244 (decimal 580) is set to zero so that a SYSTEM-RESET will not result in a re-boot. The OS cassette-boot firmware stored the RUN-address at RAM vector locations $C (12) and $D (13). The reader may elect to modify the cassette SYSTEM-RESET vector to point to some WARM-START location or use the DOS vector at $A (10) and $B (11) by setting RAM location 9 to value 1. Additional features can be implemented by continuing the boot process to DOS by appropriate jumps to an OS location such as DOBOOT at $F2ED (62189).
DATA statement 57 in the BASIC program is the USR 6502 machine language routine that is used in statement 55. The ATARI IOCB use is adapted from the INTERFACE MODULE Operator's Manual C015953 and modified for cassette so that the short-inter-record gap tape can be written. BASIC USR function is explained in detail in BASIC REFERENCE MANUAL C015307 along with some information on XIO PUT CHARACTERS. The C016555 User's Manual is of tremendous assistance in understanding the ATARI operation, also.
An adaptation of the Exclusive-Or function in Example 1 of Appendix 9 in the ASSEMBLER EDITOR User's Manual is used to illustrate the BASIC program of Program 2 in this article. The following program will generate a binary load-and-go file that will perform the logic function.
10 OPEN#1, 8, 0, "D : LOADNGO.BIN" 20 FOR I = 1 TO 30 : READ X : PUT#1, X : NEXT I 30 CLOSE#1 40 END 50 DATA 255, 255, 0, 6, 17, 6 60 DATA 104, 104, 133, 205, 104, 133, 204, 104, 69 70 DATA 205, 133, 213, 104, 69, 204, 133, 212, 96 80 DATA 224, 2, 225, 2, 0, 160
The DATA statement 50 is binary-load information that indicates the program will load at RAM location $600 (1536). Statements 60 and 70 are the BASIC USR function machine language program. Statement 80 is the RUN-address information for the load-and-go $A000 RUN-address for BASIC or ASSEMBLER cartridge. It is only necessary to RUN this program to generate an input file for the BASIC program in Program 2.
If the resulting cassette-boot tape is used with the BASIC cartridge, the familiar READY will appear on the screen after a successful cassette-boot "START, POWER-ON, RETURN" sequence. A BASIC direct POLONE = USR(1536, 65535, 0) :? PPOLONE and RETURN will provide the response "65535". If the cassette-boot sequence is used with the ASSEMBLER cartridge, it will be possible to disassemble the machine language code that was booted-in around $600 using the DEBUGGER. This illustration is only a very elementary use of the cassette-boot tape that can be generated from a binary-load file.
Program 1. Cassette boot from binary load file assembler/editor text listing.
0000 20 . PAGE 0000 30 *= $2100 ;BASIC PROVIDES CASSETTE BOOT ADDRESS 2100 0001 40 HERE .WORD $100 ;ZERO FLAG - BASIC PROVIDES 128 BLOCK 2102 0021 50 .WORD HERE ;CASSETTE BOOT LOAD ADDRESS 2104 0021 60 .WORD HERE ;BASIC PROVIDES COLDSTART ADDRESS 2106 A93C 70 LDA #$3C ;STOP CASSETTE 2108 8D02D3 80 STA PACTL 210B A902 90 LDA #$2 ;INDICATE CASSETTE BOOT 210D 8509 0100 STA $9 ;ENABLE CASSETTE SYSTEM RESET @ BOOT? 210F A900 0110 LDA #0 2111 8D4402 0120 STA COLDST ;DISABLE COLDSTART 2114 A50C 0130 LDA $C ;OS SAVED COLDSTART DOSINI 2116 8502 0140 STA $2 ;PREPARE SYSTEM RESET 2118 A50D 0150 LDA $D 211A 8503 0160 STA $3 211C 6C0C00 0170 JMP ($C) D302 0180 PACTL = $D302 0244 0190 COLDST = $244
Program 2.
1 DIM RSTART$(7), YN$(3), NAME$(15) : CORE = INT (0.89*FRE(0)) : DIM BUF$(CORE), A$(114) : RAY = 0 2 FOR I = 1 TO 31 : READ X : BUF$(I) = CHR$(X) : NEXT I 3 GRAPHICS 0 : A$ = "DOS 2. 0S BINARY-LOAD-DISK TO BOOT-TAPE by R. Polone (SYZYGY MICROWARE OF TEXAS) REV1.0 1981" : GOSUB 35 4 IOCB = 16*2 : FOR I = 1 To 7 : READ X : RSTART$ (I) = CHR$ (X) : NEXT I 5 TRAP 5 : A$ = "PLEASE ENTER BINARY LOAD FILE NAME i.e. D1 : LOADNGO. BIN (RETURN)" : GOSUB 35 : INPUT NAME$ : TRAP 40000 6 TRAP 7 : OPEN #2, 4, 0, NAME$ : GOTO 8 7 ? : ? "ERROR"; PEEK (195); "FILE" CHR$(34); NAME$; CHR$(34) : CLOSE #2 : END 8 TRAP 40000 : GET #2, X : GET #2, Y : IF X<>255 OR Y<>255 THEN A$="FILE NOT BINARY SAVE FORMAT" : GOSUB 35 : CLOSE #2 : END 9 GET #2, X : GET #2, Y : FIRST = X + 256*Y : BOOT = FIRST -LEN (BUF$) : GET #2, X : GET #2, Y : LAST = X + 256*Y : IF FIRST = 736 AND LAST = 737 THEN GOTO 43 10 FOR I = FIRST TO LAST : GET #2, X : BUF$(LEN(BUF$) +1) = CHR$(X) : NEXT I 11 TRAP 18 : GET #2, X : GET #2, Y : FIRSTA = X + 256*Y : IF FIRSTA = 65535 THEN GOTO 11 12 IF FIRSTA < = LAST AND FIRSTA <> 736 THEN GOTO 17 13 IF FIRSTA = 736 THEN GET #2, X : GET #2, Y : LASTA = X + 256*Y : IF LASTA = 737 THEN GOTO 44 14 IF FIRSTA = 736 THEN GOTO 1715 FIRST = FIRSTA : X = FIRST - LAST - 1 : Y = LEN(BUF$) : IF X <> 0 THEN BUF$(Y + X, Y + X) = CHR$(0) 16 GET #2, X : GET #2, Y : LAST = X + 256 * Y : X = LAST - FIRST : IF X > = 0 AND LEN(BUF$) + X < = CORE THEN GOTO 10 17 TRAP 40000 : A$ = "LOGICAL RECORD ERROR!" : GOSUB 35 : CLOSE #2 : END 18 IF PEEK < (195) = 136 THEN TRAP 40000 : GOTO 21 19 IF PEEK(195) = 5 THEN A$ = "RAM TOO SMALL IN THIS SYSTEM! CANNOT GENERATE BOOT CASSETTE!" : GOSUB 35 : END 20 ? CHR$(155) ; " ERROR " ; PEEK(195) : END 21 CLOSE #2 : X = INT(BOOT/256) : Y = BOOT - 256 * X : BUF$(3, 3) = CHR$(Y) : BUF$(4, 4) = CHR$(X) : IF RAY = 255 THEN GOTO 26 22 TRAP 22 : A$ = "BINARY FILE IS NOT LOAD-AND-GO (NO RUN-ADDRESS APPENDED TO FILE)! PLEASE ENTER DECIMAL-ADDRESS" : GOSUB 35 23 A$ = "$A000 HEX IS 40960 DECIMAL." : GOSUB 35 : INPUT AD 24 TRAP 40000 : IF AD < 0 OR AD > 65534 THEN GOTO 22 25 X = INT (AD/256) : Y = AD - 256 * X : BUF$(5, 5) = CHR$(Y) : BUF$(6, 6) = CHR$(X) 26 SIZE = INT(LEN(BUF$)/128) : IF SIZE * 128 <> LEN(BUF$) THEN SIZE = SIZE + 1 27 BUF$(2, 2) = CHR$(SIZE) 28 A$ = "WRITE PREPARE BOOT TAPE! BEEPS REQUIRE RETURN" : GOSUB 35 29 AUX2 = 128 : SIO = 11 : RW = 8 : LENTH = INT(LEN(BUF$)/128) * 128 + 128 : BUF = ADR(BUF$) : GOSUB 46 : CLOSE #2 30 TRAP 30 : A$ = " ANOTHER COPY OF BOOT TAPE? Y N RETURN" : GOSUB 35 : INPUT YN$ : IF YN$(1, 1) <> "Y" AND YN$(1, 1) <> "N" THEN GOTO 30 31 TRAP 40000 : IF YN$(1, 1) = "Y" THEN GOTO 28 32 TRAP 32 : A$ = "ANOTHER BINARY DISK FILE? Y N RETURN" : GOSUB 35 : INPUT YN$ : IF YN$(1, 1) <> "Y" AND YN$(1, 1) <> "N" THEN GOTO 32 33 TRAP 40000 : IF YN$(1, 1) = "Y" THEN RUN 34 END 35 X = PEEK(83) - PEEK(82) + 1 : I = X + 1 : Y = 0 : IF LEN(A$) < 1 THEN RETURN 36 IF LEN(A$) = X THEN ? A$ : A$ = "" : POKE 84, PEEK < (84) - 1 : RETURN 37 IF LEN(A$) < X THEN ? A$ : A$ = "" : RETURN 38 YN$ = A$ (I, I) : IF YN$ = " " OR YN$ = CHR$(155) THEN I = I - 1 : ? A$(1, I) : A$ = A$(I + 2, LEN(A$)) : Y = 255 39 IF Y = 255 AND I = X THEN POKE 84, PEEK < (84) - 1 : GOTO 35 40 IF Y = 255 THEN GOTO 35 41 I = I + 1 : IF I = 0 THEN ? A$ (1, X) : A$ = A$ (X + 1, LEN (A$)) : GOTO 35 42 GOTO 37 43 GOSUB 45 : GOTO 9 44 GOSUB 45 : GOTO 11 45 GET #2, X : BUF$(5, 5) = CHR$(X) : GET #2, X : BUF$(6, 6) = CHR$(X) : RAY = 255 : RETURN 46 IF RW = 8 THEN TRAP 47 : LPRINT 47 TRAP 40000 : OPEN #2, RW, AUX2, "C:" 48 POKE 832 + IOCB + 2, SIO 49 POKE 832 + IOCB + 4, BUF - (INT(BUF/256) * 256) 50 POKE 832 + IOCB + 5, INT (BUF/256) 51 POKE 832 + IOCB + 8, LENTH - (INT(LENTH/256) * 256) 52 POKE 832 + IOCB + 9, INT (LENTH/256) 53 POKE 832 + IOCB + 10, RW 54 POKE 832 + IOCB + 11, AUX2 55 RSTART = ADR (RSTART$) : POLONE = USR(RSTART, IOCB) : RETURN 56 DATA 0, 1, 0, 33, 0, 33, 169, 60, 141, 2, 211, 169, 2, 133, 9, 169, 0, 141, 68, 2, 165, 12, 133, 2, 165, 13, 133, 3, 108, 12, 0 57 DATA 104, 104, 104, 170, 76, 86, 228 58 REM 59 REM - FIGURE 2. BASIC PROGRAM TO GENERATE CASSETTE BOOT 60 REM - FROM DOS 2. 0S BINARY LOAD FILE