Classic Computer Magazine Archive COMPUTE! ISSUE 17 / OCTOBER 1981 / PAGE 84

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