Classic Computer Magazine Archive ANTIC VOL. 1, NO. 6 / FEBRUARY 1983

CBOOTMGR

by Harald Striepe

Most ATARI owners start out with a cassette-based system. CASSETTE BOOT MANAGER will help you make backups of your single-stage boot tapes, and permit you to convert them to boot disks later on. The program also includes two general, relocatable subroutines important for handling Input / Output thus overcoming the most significant shortfalls of Atari's 8K BASIC.

Sectors on single-density disk drives contain the same number of bytes (128) as the "records" of the cassette system. Each cassette record is separated from the next by a recording gap, which also contains a marking tone. This tone makes it possible for the ATARI to adjust its receiving procedure to match the varying speed of the cassette recorder.

There are two kinds of gaps, long and short (LIRG and SIRG).- You might have encountered these when you used CSAVE (SIRG) or SAVE "C:" (LIRG). The two methods store information identically, the only difference is the gap length between each record LIRG is long enough to permit recorder motor to stop completely, while SIRG needs the motor to keep running.

Obviously, lead tones and gaps are not used with the disk drive. The drive receives an instruction to read or write a particular sector, and it executes that instruction on its own.

There are, however, a number of different kinds of information storage in the ATARI system. BASIC LISTed files, Assembler source files, and the textfiles of a number of word processors simply contain the ATASCII characters of that file all in a row. BASIC SAVEd files contain the tokenized statements (which look meaningless without the action of the BASIC cartridge), and related tables. DATA files contain numbers in either listed or binary form, depending on how they were stored. Binary files contain the binary information stored directly, with a header indicating the nature of the file and the starting and ending address where the file is to be located.

On cassette, this information is sequentially sent out over the serial bus, but the disk drive is a randomaccess device that allows named files (e.g., "D1:MYPROG.BAS"). So, the filemanager (FMS) part of the Disk Operating System (DOS), has to organize all the sectors in some way. One aspect of DOS is that each sector contains a three-byte link pointing to the next sector in the file, giving the file number in the directory, and counting the number of bytes actually in use in that sector. This means that in the standard disk file each sector has only 125 bytes available.

Since we normally use CIO, ATARI's most intelligent 1/0 system, we are not usually aware of what is happening. BASIC in all its I /O functions utilizes CIO. Besides opening and closing files, it controls the STATUS function, "record" related I / O (a record is terminated by a carriage return: $9B) in INPUT and PRINT, and single-bvte organized I/O in PUT and GET.

One function not supported by BASIC is the BLOCK 1/ O. Just as in PUT or GET, BLOCK 1/0 does not care at all about the content of the transfer, it just wants the buffer address for the file and its length. BASIC does use it for SAVE and LOAD functions, and the DUP.SYS part of DOS supports it in numerous ways, like BINARY LOAD or SAVE. But otherwise you cannot use it for your program from 8K Atari's BASIC (Note: OSS BASIC A+ users are provided with that capability).

BOOT records are structured differently, although they still adhere to 128-byte sector and record system. The actual layout of a boot file is very similar between cassette and disk. This is where our advantage is gained. A boot file is nothing more than all the binary information in a row (usually a machine-language program), preceded by a header. The header contains information about the number of records in the file, the starting address where the program is to be moved, and the initialization address where the Operating System is to jump during [RESET] and after boot continuation. It will then load the indicated number of sectors or records into memory, and jump to continuation right after the header.

If it is a single-stage boot, the continuation will only set memory limits and DOS vectors, stop the cassette motor to overcome a bug in OS, set the CARRY flag if everything is okay, and return. Multiplestage boots do not simply return, but continue the loading process. For example, DOS uses only three boot sectors for 384 bytes of info, and then the boot continuation loads the DOS.SYS file. Our program cannot deal with multi-stage boots, since they are impossible to predict, but single-stage boots are all similar.

So, to copy a cassette all we have to do is pull the cassette boot into a buffer, and then write it out to cassette again. To copy to disk, write the buffer one sector after another as a simple boot record, starting at sector 1. Note: the disk will not be a DOS disk, just as most game disks are not. Since singlestage cassette- and disk-boot look the same, it ought to work. Right, but there is one difficulty.

GET or PUT are much too slow to deal with the SIRG used by the cassette boot file. INPUT or PRINT would work, but the first time they encounter a $9B (carriage return) they return unpredictably. Thus, we must implement a machine-language call. The routine provided in lines 2000 to 2100 does just that. It can be POKEd into a string, since it is relocatable. You simply OPEN and CLOSE in a normal way, and use the subroutine instead of PUT or GET. Note that in the OPEN call for the cassette, we have the auxiliary information 128 (Line 3210), this sets up CIO for SIRG. In the USR call, we have to supply the CHANNEL (e.g.. OPEN #1 -), and we have to state whether we want te PUT or GET with number 7 or 11. We also supply the buffer address, and length, since this is required for BLOCK I/O.

The routine will return a 0, if you did not supply the right number of parameters. Under CIO, a 1 is the code for successful completion. If you loaded a file, and your buffer was larger than the length of the file required, the USR call will return with a 136. This is fine, since that tells you to look at locations 203 and 204 for the actual length (number of bytes) of the file you just loaded (your friendly subroutine placed it there.) From BASIC, a LENGTH = PEEK(203) + 256 ~ PEEK(204) will return the answer. Oh, yes, if your call ERROR=USR..., then naturally, ERROR will be equal to the codes I just described.

This takes care of the cassette, but the boot disk does not use CIO, and its 128-byte sector format precludes use of the FMS, anyway. We have to call the disk handler, with lines 1000 to 1140. Disk I/O always happens in 128-byte blocks, so adjust for that. Since CIO is not used, no files have to be opened or closed, but error codes (except for end-of-file, of course) are the same. The routine is general enough so you can read as well as write.

Since BASIC itself takes up a rather large space, the program is divided into two parts to give you a maximum size buffer. To make it self-loading, save both parts with "SAVE "C:", then a simple RUN "C:" from BASIC will set up your system. It will load both parts, if you add this line-325 POKE 764,12 (as if you were hitting [RETURN] upon the second RUN "C:"). The double BEEP from the cassette-open command will call you when the system is ready.

CBOOTMGR not only does the job of commercial programs costing $20 or more, but it also gives you some handy subroutines for fast and unusual 1/0 from BASIC. Good luck, and if you have questions or comments, write to me c/o ANTIC.

[CODE]