|
Post by jmpff3d on Nov 21, 2018 13:12:30 GMT
rev 1a 96-11-01 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * THIS DOCUMENT IS COPYRIGHT (C) 1988, 1996 BY HERNE DATA SYSTEMS LTD. THE MATERIAL CONTAINED HEREIN MAY BE FREELY USED FOR PERSONAL INFORMATION ONLY. IF YOU REPRODUCE IT, THIS COPYRIGHT NOTICE MUST NOT BE REMOVED. THIS MATERIAL MAY NOT BE EXPLOITED FOR COMMERCIAL PURPOSES. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Herne Data Systems Ltd., PO Box 250, Tiverton, ON N0G 2T0 CANADA. Voice/fax 519-366-2732, e-mail herne@herne.com internet: http://www.herne.com
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*** An Introduction to the 1571 and 1581 Disk Drives ***
Commodore disk drives are amongst the most complex currently available for home computers. Each has its own microprocessor, interface circuitry and extensive ROM based operating system. This approach developed from the early Commodore PET (Personal Electronic Transactor) series computers which contained no internal interface or support for disk drives. To maintain compatibility with these earlier units, most Commodore disk drives have been built this way ever since. The 1571 disk drive, produced primarily for the C-128 and C-128 D Personal Computers, is perhaps the most complex yet: It has a sophisticated and versatile operating system which allows it to read both Commodore GCR format disks and industry standard IBM system 34 (MFM) type disks, each in either single or double sided formats. The 1571 is also one of the fastest disk drives produced by Commodore, even though it uses the C-64 style serial port rather than the IEEE 488 parallel bus used by most other Commodore fast drives. Despite its wealth of features, little has been written about the 1571, especially about the more advanced features such as burst mode and accessing MFM disks. Most programming guides deal mainly with a specific model of computer and only devote a minimal amount of space to peripherals such as disk drives, even though the peripherals may be at least as sophisticated as the computer itself. With the increasing complexity and versatility of Commodore disk drives, These drives certainly deserve equal space. 1.1 About This Series This series is intended to be a guide for programming and using the 1571 drive at all levels from novice to advanced programmer. Much of the material is also applicable to other Commodore disk drives, such as the 1541 and the new 1581. The material is especially relevant for the built in disk drive of the 128-D computer and the 1570 (a European single sided version of the 1571). The series on is organized into a number of different major parts. Although each part is more or less self contained, reference may be made to other parts for clarity or further information. The emphasis of this series is on teaching the fundamentals of how the various players in the programming game (i.e. the computer, disks, disk drives, disk files, etc.) interact with each other. In this capacity, it should be considered to be a basic reference tool or foundation upon which you can explore your own ideas and applications. Most of the examples included in this series refer specifically to BASIC 7.0, the native language of the C-128. However, since the 1571 can be used with other Commodore computers, examples are also provided in BASIC 2.0 and 3.5 as well as in assembly language. The examples presented in the text are, for the most part, greatly simplified to demonstrate a specific point. You are, of course, free to embelish them with all the bells and whistles that you see fit. Some readers may get a feeling of deja vu from certain parts of the text. This is quite understandable because parts of the text have been extracted and adapted from some of the numerous articles that I have written about programming the 1571 in Commodore Magazine, TPUG Magazine, The Transactor, Commodore Computing International and Twin Cities 128. I would like to thank the editors of these fine publications for encouraging my writing and programming efforts. Good luck, and remember, computing should be fun!! 1.2 The 1571: An Overview The Commodore 1571 disk drive is a highly versatile mass storage device which can be used with the C-128, C-128-D, C-64, 64-C, SX- 64, C-16, Plus/4 and VIC-20 computers. It is a double sided drive which can store up to 340 kilobytes of data per disk. The 1571 is read and write compatible with other Commodore drives using DOS format "A" such as the 1541 and 2031. However, since these drives are only single sided, they will not be able to access the second side of a 1571 disk.
In special circumstances, such as C-128 CP/M mode, the 1571 also allows you to use other disk formats with capacities of up to 400 kilobytes per disk. In fact, the 1571 can read most disk types, including MS-DOS, with a bit of simple programming.
1.2.1 Specifications Tables 1-1 to 1-3 summarize the hardware and technical specifications of the 1571 disk drive.
Table 1-1: 1571 Hardware Specifications ...................................................... Height 76 mm ( 3 inches) Width 216 mm ( 8.5 inches) Length 346 mm (13.5 inches) Weight 3.5 kg (7.7 lb)
Microprocessor 6502A Interface 65C22A ROM 23256 (32 k bytes) RAM 4016 (8 k bytes) MFM controller WD1770 Chip speed 1 MHz (1541 mode) 2 MHz (1571 mode) Communication: Slow Serial Bus 350 bytes/second (read and write) Fast Serial Bus 4000 bytes/second (read) 400 bytes/second (write - limited by verify after write) Device number 8 to 11 (hardware selected) 8 to 30 (software selected, temporary) ............................................................. Table 1.2: GCR Mode Specifications ............................................................. Total capacity per side 252,019 bytes (unformatted) 174,848 bytes (formatted) Bytes per sector 256 Sectors per track track # sectors ----------------------- 1 - 17 21 18 - 24 19 25 - 30 18 31 - 35 17 Tracks per side 35 (1 to 35 on side 0, 36 to 70 side 1) Sectors per side 683 total 664 available to user 19 reserved for system use Directory entries 144 per disk Max SEQ, PRG or USR 168,656 (single sided) file size (bytes) 337,312 (double sided) Max REL file size 167,132 bytes Records per REL file 65535 Record size 2 to 254 bytes Recording format 2A (1540, 1541, 1551, 2031 read/write compatible) ......................................................... Table 1-3: MFM Mode Specifications ......................................................... Total unformatted capacity 250,000 bytes per side Maximum Formatted capacity Sector size Bytes per side ----------------------------- 128 133,120 256 184,320 512 204,800 1024 204,800
Maximum Sectors per track Sector size Sectors ----------------------- 128 26 256 18 512 10 1024 5
Tracks per side 40 maximum
Note that MFM disk formats are not supported directly by Commodore DOS (i.e. LOAD, SAVE, etc) but are supported by C-128 CP/M mode .............................................................
When used with the C-128 or 128-D computer, the fast serial bus of the 1571 drive permits data transfer at speeds of over ten times faster than the 1541 type slow serial bus. The slow serial bus is also supported by the 1571 to allow it to be used with other Commodore computers. (The fast serial bus depends on special hardware in both the disk drive and the computer and will not work with older Commodore computers. Slow bus compatibility was originally included in the 1571 to make it wotk properly with the C-128 in C-64 emulation mode. To achieve true C-64 hardware compatibility, the C-128's fast serial port hardware is locked out during C-64 mode operation.) All DOS commands sent to the 1571 and status messages returned to the computer are transmitted in slow bus mode.
1.2.2 Operation
The 1571 disk drive uses standard 5-1/4 inch double sided, double density disks. Virtually any brand will work satisfactorily, although name brands are generally of better quality than discount generic brands and will give longer life, especially in frequent use applications.
A double sided disk drive has two read/write heads, one on the top and one on the bottom. This allows the drive to access both sides of the disk without having to physically turn it over. Some users of single sided disk drives, such as the 1541, have developed a habit of flipping the disk over (similar to a record or tape) to use the second side. This should not be done with the 1571 when it is used with a C-128. The 1571 will format and use both sides of the disk automatically without having to do this. If you attempt to create a "flippie", you will destroy any existing data on both sides of the disk. Even in 1541 single sided mode, using flippie disks may not be a good idea, especially for irreplaceable data, because it greatly shortens the life of the disk. Similar to an audio record, a disk turns in one direction only when reading/writing data on a given side. Because the disk is encased in a protective jacket, it is constantly rubbing on the jacket lining, but always in the same direction. When you flip the disk over, it will rotate in the opposite direction. Similar to the "knap" in fabrics and carpets, this will stress the recording media in the opposite direction. A constantly changing rotation direction caused by flipping the disk will cause greater wear and tear on the disk, thus shortening its useful life. The actual shortening of the life will vary greatly depending on how often the disk is flipped, the quality of the disk, the cleanliness of the head, as well as many other less tangible factors. To many people, the advantage of doubling the amount of data stored on each single sided disk outweighs the potential drawbacks. In other words, if you are willing to take the risk, the choice is yours. But remember, you don't get something for nothing.
In order to use flippie disks, you will need a disk notcher. This is a small device, similar to a paper punch, which will cut out an extra write protect notch on the right side of the disk. Without this notch, you cannot format or write data to the back side of a flippie.
1.3 Care and Feeding of your 1571 The 1571 is a fairly rugged device. However, that does not mean that it enjoys being dropped, drowned, kicked, etc. Like other electronic equipment, a little common sense is required, especially when moving from one location to another. The following general hints apply to all disk drives.
(a) Keep the disk drive well ventilated. Allowing a few inches of space around the vents is generally adequate for dissipating heat from normal use. Prolonged use in hot environments may require artificial cooling, such as fans when adequate ventilation space is not available.
(b) The drive is intended to be used in "normal" office or household environments. It probably will not last too long next to a heating duct or in your backyard in the Yukon in December. Similarly, it probably would not appreciate being left on the deck of Noah's ark or in a Sahara sandstorm.
(c) The read/write heads should be cleaned about once per year in normal use. More frequent cleanings may be desirable with heavier usage, but may cause additional head wear if done too frequently. Virtually any wet (generally isopropyl alcohol or fluoro carbon based) commercial head cleaner will be acceptable. These head cleaners work by inserting a special cleaning pad (containing the cleaner) resembling a floppy disk into the drive and causing the mechanism to spin for a few moments. Follow the indivdual cleaner manufacturers' instructions and recommendations for best results.
(d) The head tracking rails should be lubricated as required with a molybdenum based instrument lubricant. Do not use oil for this purpose because it will contaminate your heads and disks. Lubrication should be done by a qualified service shop.
(e) Never touch the exposed magnetic surfaces of the disk. Avoid spilling liquids and crumbs/dust on them. Keep the disk in its protective jacket when not in use. This minimizes the possibility of the above events and limits their effects if they do occur.
(f) Do not fold, spindle or mutilate the disks. Although I have revocered data from disks which were folded in two, I have also lost data on disks which were only slightly bent.
(g) Keep the disk and disk drive away from magnetic fields. This includes magnetic fields created by electric motors, TV's and monitors, computer power supplies, speakers, headphones, etc. It may also be wise to keep them away from electric trolley buses, subway trains and airport x-ray machines.
(h) Always turn on the disk drive power before inserting a disk and remove the disk before turning off the power. Never remove the disk while the green "drive in use" light is on (unless the drive is locked up or a similar emergency has occured).
BASIC Disk Commands The 1571 disk drive is primarily intended to be used with the C-128 128-D computers. (It does, however, work very well with other Commodore computers which support the serial port, such as the C-64, SX-64, Plus/4, C-16, VIC-20 or the C-128 in C-64 mode, although these other computers do not support the 1571's fast serial port mode.) The C-128's resident version of BASIC, termed BASIC 7.0, contains a rich vocabulary of disk commands. These commands greatly simplify the process of communication between the computer and the disk drive. Where noted, some of the commands are also applicable to other versions of Commodore BASIC, such as 2.0 used on the C-64 and VIC-20, and 3.5 used on the Plus/4 and C-16. In general, the commands beginning with "D" (such as DLOAD) and "B" (such as BACKUP) are not available in BASIC 2.0. However, some of these commands are available in BASIC 3.5. This document contains a description of each of the BASIC disk access commands (grouped according to major function) along with examples and tips for their usage. The command keywords are spelled out in full. Abbreviated two or three character versions are also available for most of the commands.
Capital letters included in a command string (such as U, D, B, P, etc) are part of the command string that must be entered.
As a reminder, lower case text enclosed in curly braces "{}" is used to describe the string or numeric parameters which should be entered for each command. Spaces have been added between the various parameters to improve the readability. These spaces are not required by BASIC. In immediate mode, all commands should be completed by pressing the <RETURN> or <ENTER> key. It should be noted that most of the commands will accept either constants (such as the text string "FILE2" or the numeral "8") or variables (such as the text string name F$ or numerical variable N) for specifying the command parameters. In general, variable names for the BASIC 7.0 commands should be enclosed in parentheses, such as (F$) or (N), when used in such a manner. Except where noted, all filenames can contain the wildcard and pattern matching characters "?" and "*". Command parameters enclosed in square brackets "[ ]" are optional and can be left out if the default value is suitable for the application. It normally does not matter in which order optional parameters are specified. For example, "D0,U8" is the same as "U8,D0". In all cases for the BASIC 3.5/7.0 disk commands, the default value for the disk drive unit, or device, number (as given by the U parameter) is 8 and the drive number (as given by the D parameter) is 0. The drive number is intended to be used with dual disk units (i.e two disk drives in a single case where one drive would be addressed as drive "0:" and the other as drive "1:"). Since the 1571 is only a single drive, the drive number always has a value of 0. The ",U{device#}" format can be replaced by "ON{device#}", if you so desire. The two forms are inter-changeable.
For BASIC 2.0 compatible commands, the default device number is always 1 (the cassette port).
Retrieving Programs =================== Perhaps the most common use of your disk drive will be to store and retrieve "programs". BASIC has several commands intended for the retrieval of previously stored programs on a disk. They might be programs which you have bought or ones that you have written yourself. To the disk drive, they are the same.
In simple terms, the LOAD commands read a disk file and POKE the bytes into memory. The first two bytes of the file contain the address in the computer's memory from where the file was initially saved. These two bytes are either ignored or used to tell the computer where to put the program when it is loaded, depending on the options specified with the LOAD command. Each of the LOAD commands has a different effect on where the the program will be placed in memory.
Note that all of the LOAD type commands will only work with PRG (program) type disk files. If an attempt is made to LOAD another type of file (such as SEQ), a "FILE NOT FOUND" or "TYPE MISMATCH" error will be generated. However, on the C-128, a SEQ or other type of non-program file can be read into memory as a BASIC program using a technique outlined in 1571-4.TXT (DOS & the KERNAL). For all of the LOAD type commands, the "{filename}" may contain wildcard and/or pattern matching characters. In such a case, the first filename in the disk directory which matches the pattern will be LOADed. With DLOAD, BLOAD and BOOT, a "{drive#}:" character string is automatically added to the beginning of the filename as it is sent to the disk drive and displayed on thescreen during the "SEARCHING FOR {filename}" message. For the 1571, this should always appear as "0:".
LOAD LOAD is perhaps the most familiar command for retreiving programs. It is available in all forms of Commodore BASIC. For disk users, the syntax for the command is: LOAD "[{drive#}:]{filename}",{device#}[,{relocate flag}] Since the default device number is 1 (i.e. the cassette port) for most Commodore computers (except the SX-64) a device number of, for example, 8 must be included for disk use. A {relocate flag} value of 0 means that the program file will be LOADed into the normal start of BASIC address as specified by the pointers set up in the computer's RAM. This is the default condition. In the C-128, the start of BASIC pointer is in BANK 0 at address 45-46 ($2d-$2e) in standard low byte-high byte format. This pointer can be manipulated with POKE's before a LOAD, but care should be taken to restore it to the default value after the LOAD. A {relocate flag} value of 1 indicates that the program will be LOADed into its own normal start address, as specified by the first two bytes contained in the program file. This is normally used for LOADing machine language subroutines and programs that must be in a specific RAM location for operation. It can also be used to LOAD non-program data, such as high resolution graphics screens, into specific areas of RAM.
Some examples are: LOAD "0:BASIC PROGRAM",8 will search for and, if found, LOAD the file "BASIC PROGRAM" from disk drive device #8, drive 0. LOAD "0:ML.PROGRAM",8,1 will search for and, if found, LOAD the file "ML.PROGRAM" into a specific address of RAM as determined by the first two bytes of the file. LOAD "0:TEST*",8 will LOAD the first program in the directory whose first four characters are "TEST". Two special forms of LOAD should be noted: LOAD "*",8 [,1] will LOAD the first program listed in the disk directory (optionally with the {relocate flag} set), while: LOAD "$",8 will LOAD the disk directory as a program file. To display the directory, you must then type LIST. This method is one of the easiest ways to display a disk directory in BASIC 2.0 (which lacks a true directory command) without using a DOS wedge. This method is also necessary for obtaining a printed copy of the directory in any form of BASIC. The following procedure can be used: LOAD "$",8 OPEN 4,4 CMD4:LIST PRINT#4:CLOSE4 Unfortunately, BASIC 3.5 and 7.0's DIRECTORY and CATALOG commands (discussed later) will not allow the re-direction of output from the screen to a printer. Hence, the procedure above is applicable to all current versions of Commodore BASIC. DLOAD DLOAD is a modified form of the LOAD command which is available with BASIC 3.5 and 7.0. It LOADs from disk only. The syntax is:
DLOAD "{filename}"[,U{device#}][,D{drive#}] DLOAD is the equivalent of the LOAD command but the default device is 8 (i.e. a disk drive) rather than 1. Like LOAD, DLOAD will put the file into the RAM address specified by the start of BASIC pointer. An example would be: DLOAD "GAME",U9 to DLOAD the program "GAME" from a device 9 disk drive. The statement: DLOAD (A$) will DLOAD the file specified by A$ from the default disk drive (device 8, drive 0). It should be noted that due to a bug in older versions of the C-128's ROM, DLOAD cannot be used to load a "locked" program file. (A locked file is one which cannot be scratched. It is identified by the symbol "<" which appears after the file type in the disk directory listing.) In this case, a locked file must be loaded with the LOAD command. The newer ROM versions which support locked file DLOAD's can be identified by BASIC's signon message which is dated 1986 instead of 1985. BLOAD The BASIC 7.0 command BLOAD can be thought of as a contraction of the words "Binary LOAD". It is used to LOAD a PRG type file into a specific area of memory. BLOAD can be used for LOADing a machine language subroutine that will be used by another program, or for retreiving non-program data from disk, such as a high-resolution graphics screen, data values (BANK 1), function key and sprite definitions, a custom character set, etc. BLOAD can be used either in immediate mode or from within a program. The syntax of the command is: BLOAD "{filename}"[,U{device#}][,D{drive#}][,B{bank#}] [,P{start address}] where {bank#} represents the RAM bank into which the file is to be LOADed (default BANK 15), and {start address} represents the starting memory location in RAM where the file is to be placed (default is the normal LOAD address for the file). In immediate mode, BLOAD without the {start address} option is equivalent to the LOAD command using a {relocate flag} value of 1. In program mode, BLOAD will continue with program execution with the statement immediately following the BLOAD, while LOAD will start executing the program from the beginning again, possibly creating an endless loop.
The following example will BLOAD a disk file into the area of RAM normally occupied by the C-128 high resolution graphics screen (starting at the color map): BLOAD "HI-RES PICTURE",B0,P7192 Of course, you must first set up the appropriate graphics mode with the BASIC 7.0 GRAPHIC command before BLOADing the screen. Similar to DLOAD, BLOAD will not work with locked files on older C-128 ROM versions. In this case, you should use either a KERNAL LOAD (discussed in 1571-4.TXT) or a LOAD with a {relocate flag} value of 1. MONITOR-L The BASIC 3.5 and 7.0 MONITOR commands both contain the equivalent of BLOAD which can be used when in MONITOR mode. The syntax is: L "{filename}",{device#} [,{start address}] The optional {start address} parameter is applicable to BASIC 7.0 only. It has the same meaning as the parameter in the BLOAD command. However, it should be noted that in MONITOR mode, {device#} and {start address} are always specified in hexadecimal notation. BASIC 7.0 MONITOR addresses can be 5 hex digits long. If the address is greater than 4 digits long, the left most digit is taken to be the BANK number into which to load the file. For example, L "ML.PROGRAM",08,11000 will load the specified program from device 8 into the area of RAM beginning at $1000 (decimal 4096) of BANK 1 of the C-128. Many machine language monitors, such as VICMON for the VIC-20 and C-64, have LOAD options similar to the BASIC 3.5 version. BOOT BOOT is used to automatically LOAD and execute a machine language program file on the C-128. It can also be used for the automatic execution of a BASIC program. Two forms of the command are possible: BOOT [U{device#}][,D{drive#}]
by itself will check for a valid BOOT sector (track 1, sector 0) on the specified or default drive, then LOAD its contents into the cassette buffer at $0b00 (dec 2816) and execute its contents, if found. This is similar to what happens during a C-128 hard reset (i.e. by pressing the reset button or turning the power off the on). It can be used to start up an autobooting disk, such as the CP/M Plus system disk or many commercial programs. The {device#} and {drive#} parameters are not normally specified because most multi part commercial autoboot software will only work from device 8, drive 0 (the default settings). They can be included for your own programs, however, if you include the specific device number when loading secondary files from the BOOT routine. On the C-128, the BOOT routine should check location $ba (decimal 186) or $11c (decimal 284) to determine which device is being BOOTed and use this device number for multi part loads. The second form of the command: BOOT "{filename}"[,U{device#}][,D{drive#}][,P{load address}] [,B{bank#}] will LOAD and execute the machine language program specified by "{file name}" from the disk drive specified by {device#} and {drive#} into the area of RAM specified by {load address} in BANK {bank#}. The default LOAD address is the normal LOAD address for the file while the default BANK is 15. This latter form of the BOOT command is equivalent to using a BLOAD followed by a SYS to the starting address of the program. An example is: BOOT "ML.PROGRAM",U8,P10000 to BLOAD the specified machine language program into RAM at decimal 10000 and begin its execution at this address. BOOT is available in BASIC 7.0 only. RUN In BASIC 7.0, the RUN command can be used to LOAD and RUN a BASIC program from disk, similar to BOOT for machine language files. The syntax is: RUN "{filename}"[,U{device#}][,D{drive#}] This is equivalent to DLOADing the specified BASIC program, then typing RUN. For example: RUN "STARBLASTER" will DLOAD then execute the BASIC program "STARBLASTER" from device 8, drive 0. Storing Programs ================ Analogous to the program retrieving commands, BASIC also has a set of program storing commands. These commands allow you to store your programs in a disk file for future retrieval. Special forms of the commands can be used for storing areas of the computer's RAM to a disk file for saving high resolution graphics screens, function key and sprite definitions, etc. In simple terms, the SAVE commands take a "snapshot" or memory image of an area of the computer's memory and write it to a disk file, byte by byte. The first two bytes of the file represent the starting address of the memory from where the snapshot was taken. This may be used later by the LOAD commands to indicate where to return the snapshot. It is important to remember that you are actually storing an area of the computer's addressable memory space rather than a "program". The area of memory can contain anything: graphics and text screens, BASIC programs, machine language, RAM or ROM, chip register values, etc. Note that in all of the program storing commands described below, "{filename}" cannot contain any pattern matching or wildcard characters. All SAVEd files are created in PRG (program) file format only.
SAVE The most common command for storing programs is SAVE. This is the exact opposite of the LOAD command. For disk use, the syntax is: SAVE "[{drive#}:]{filename}",{device#} A SAVE without a specified {device#} will default to the cassette port. For example: SAVE "0:RACECAR",8 will SAVE the current program in memory in the file called "RACECAR" on a device 8 disk drive. Although {drive#}: is optional, it is always prudent to specify it. This might seem odd, but the 1571 drive, just like the 1541 before it, is based on a dual drive DOS. Most of the dual drive functions have been patched out but unless the drive number of 0 is explicitly specified each time, the DOS can become confused and tries to access a non-existant drive 1, sometimes leading to drastic consequences. This is the basic reason behind the infamous "save with replace bug" described below. A special form of the SAVE command allows you to over-write an existing file and replace it with a new one of the same name. The command works within the disk drive and acts by first SAVEing the new file then scratching the old one. The syntax is: SAVE "@0:{filename",{device} An example would be: SAVE "@0:NEW VERSION",8 This example will replace the existing file "NEW VERSION" with the new program. The "@0:" at the beginning of the file name specification indicates a "save with replace" on drive 0. This command has long been a concern of many Commodore disk drive users who have complained that it leads to hopelessly corrupted files. (I have experienced it a few times working with a VIC-20 and a 1541 drive). Because of this, many users prefer to use a two step sequence for save with replace: SAVE the new file under a different name, then scratch the old file. However, if used correctly SAVE "@0:" does work properly. The key is to always include the {drive#} parameter in ALL disk operations (not just LOADs and SAVEs, but OPENs as well). DSAVE DSAVE is the opposite of DLOAD. The syntax is: DSAVE "{filename}"[,U{device#}][,D{drive#}] DSAVE will default to device 8, drive 0 if none of the optional parameters are specified. For example: DSAVE "RACECAR" is identical to the example used above for the SAVE command. DSAVE is available in BASIC 3.5 and 7.0. A special form of DSAVE can be used for the "save with replace" option. The syntax is: DSAVE "@:{filename"[,U{device#}][,D{drive#}] Note that in the case of DSAVE, "{device#}:" is never specified at the start of the file name string. This is because DSAVE automatically adds the drive specifier when it sends the filename to the drive. If you do specify it explicitly, then a double "0:0:" will be added to the filename and the file name as it appears in the disk directory will have the "0:" added to the beginning of it, such as "0:FILE" instead of just "FILE". Both SAVE and DSAVE actually SAVE the area of memory between the start of BASIC pointer and the end of BASIC text pointer. Both of these pointers can be manipulated before a SAVE or DSAVE. However, the pointers should be set back to their original values after the operation. Typically, the end of BASIC text pointer can be increased to include a machine language subroutine at the end of a program in the SAVE. On the C-128, the end of BASIC text pointer is in BANK 0 at hex $1210-1211 (decimal 4624-4625).
BSAVE BSAVE is the opposite of BLOAD, it is used to SAVE a specified area of RAM to a disk file. It can be used for SAVEing machine language programs created by BASIC POKE statements or the MONITOR command, or other areas of memory such as a high resolution graphics screen, function key definitions, or sprite data. The syntax for the command is: BSAVE "{filename}"[,U{device#}][,D{drive#}][,B{bank#}],P{start address} TO P{end address} where {bank#} is the optional bank from which to BSAVE (default BANK 15), and {start address} and {end address} are the mandatory start and end points for the memory being BSAVEd. BSAVE is only available in BASIC 7.0.
The following example will BSAVE the high resolution screen (320 by 200 pixel) color map and bit map to a disk file: BSAVE "HI-RES",B0,P7168 TO P16383 MONITOR-S The BASIC 3.5 and 7.0 MONITOR commands contain an equivalent of BSAVE for use in MONITOR mode. The general syntax is: S "{filename}", {device#}, {start address}, {end address+1}
{device#}, {start address} and {end address} are all expressed in hexadecimal form. In BASIC 7.0, {start address} and {end address} can be 5 digit hex numbers, although both must be in the same BANK. In addition, {end address} must be greater than {start address}. Unlike BSAVE, the second parameter is specified as {end address+1} to ensure that the last byte is SAVEd. (BSAVE automatically increases the {end address} pointer to include the last byte.)
For example, S "PROGRAM",08,1000,2000 will SAVE the area of memory from $1000 (decimal 4096) to $1fff (decimal 8191) to the specified filename on device 8. In BASIC 7.0, the above area of memory will be in BANK 0. RAM, ROM or chip registers can be SAVEd to disk using this technique. Similar commands for SAVEing memory are included with most machine language monitors for the C-64 and VIC-20 also. Comparing Program Files ======================= It is often desirable to check whether a program currently in memory is the same as one stored in a disk file. This can be helpful if, for example, you have several copies of a program on a disk and wish to see if they are identical or are slightly different versions. It can also be useful if you are not sure if a program that you have been working on has been correctly SAVEd to disk. These commands, which can be used in either program or immediate mode, perform a byte by byte comparison of the disk file specified by "{filename}" with a program in memory. If a single byte is found to different, a VERIFY ERROR message will be generated. Pattern matching and wild cards can be used in the "{filename}" string. VERIFY The VERIFY command is available in all forms of Commodore BASIC for comparing program files on disk to one currently stored in memory. The syntax for the command is: VERIFY "[{drive#}:]{filename}",{device#}[,{relocate flag}] With VERIFY, the {relocate flag} should have a value of 1 for comparing machine language programs with start addresses of other than the normal start of BASIC area. For disk files, {device#} must be specified.
An example is: VERIFY "DATABASE",8,1 This will compare the machine language disk program file "DATABASE" with the program currently in memory. DVERIFY Along with VERIFY, BASIC 7.0 also has a second command available with the following syntax: DVERIFY "{filename}"[,U{device#}][,D{drive#}] A special form: DVERIFY "*" can be used to test if the last program SAVEd is the same as the one currently in memory. To compare two disk program files, the following simple procedure (or equivalent in BASIC 2.0) can be used: DLOAD "{filename 1}" DVERIFY "{filename 2}" where {filename 1} and {filename 2} are the names of the two program files that you wish to compare. MONITOR-V The BASIC 7.0 MONITOR command contains the equivalent of the VERIFY command for use while in MONITOR mode. The syntax is: V "{filename}",{device#}[,{start address}] The optional {start address} parameter allows the comparison of a disk file with any area of memory starting at a location other than the normal load address of the disk file. This is especially useful for verifying relocatable machine language programs which may not normally reside in BANK 0. For example, V "APPLES",08,12000 will compare the disk file "APPLES" to the area of memory in BANK 1 beginning at $2000 (decimal 8192). Establishing the Link =====================
The second major use of a disk drive is for the storage and retrieval of data. This is a three step process, the first of which is establishing the communication link between the computer and the disk drive. In BASIC this is done with the OPEN, DOPEN or APPEND commands. One of these three commands must be used before any data can be sent to or read from a disk file. The maximum number of files which can be OPEN on the C-128 at any given time is ten. This is based on limitations of the computer. Of these, the maximum number of disk files that can be OPEN on a given disk drive is a total of 3 (SEQ or PRG or USR) files, or 1 REL file and 1 (SEQ or PRG or USR). In addition, the disk command channel can be OPEN simultaneously with any of the above combinations. This is based on limitations of the buffer space in the disk drive. OPEN OPEN is the most common command used to set up a file for subsequent input and/or output. For disk files, the syntax and usage is as follows: OPEN {file#},{device#},{channel#},"[{drive#}:]{filename}[,{type}] [,{mode}]" The {file#} is any number from 1 to 255 which must be unique for each file. (That is, no two OPEN files can have the same {file#}). This number is used to refer to the file during subsequent input and output operations. {file#} values of 128 or greater will add a linefeed (ASCII CHR$(10)) after each carriage return. Since this is generally not desirable for disk files, {file#} values of 127 or less are normally used. The {channel#} is equivalent to the secondary address used for opening files on other peripheral devices such as printers.
It can generally be any number in the range of 0 to 15 with a few reserved codes which are used internally by the 1571 DOS for special functions. These are: 0 (to LOAD a PRG file or read the disk directory as a file), 1 (to SAVE a PRG file), and 15 (to OPEN the disk command channel). Since the {channel#} is used internally by DOS to allocate disk buffers, no two open disk files on the same device should have the same {channel#}. A common practice is to use the same value for {file#} and {channel#}. The {channel#} is used mainly for specifying BLOCK commands to the disk drive, as outlined in 1571-3.TXT (The 1571 Disk Operating System).
The {type} specifies the kind of file that will be accessed and can be: P for a PRG file, S for SEQ, U for USR, or R for REL. If no {type} is specified for a read operation, OPEN will act on any type of file which matches the name (or the first match if wild cards are used). For a write file, a SEQ type is assumed if {type} is not specified.
With the 1571 disk drive, the {mode} can be any one of: R for READ, W for WRITE, A for APPEND or M for MODIFY. The default is R. The A option allows you to add data to the end of an existing file, while M allows the recovery of data from a "splat" ("*") file. A special form of the OPEN command can be used for opening REL files for relative mode operations: OPEN {file#},{device#},{channel#},"{filename},L,"+CHR$({record length}) where {record length} is the length of the relative file record in bytes. This form of the command need only be used when first setting up the relative file. More details on the various types of files and how to use them can be found in 1571-5.TXT (Effective Use of Files). OPEN is available in all versions of Commodore BASIC. An example is: OPEN 1,8,9,"0:DATAFILE,S,W" will OPEN the new SEQ file "DATAFILE" for writing on device 8, drive 0, as logical file 1, with a disk channel number of 9. OPEN will accept variables and/or constants for any of the parameters. For example, OPEN X,Y,Z,"0:"+F$+",P,R" will OPEN the PRG file specified by the variable F$ for reading on the device specified by the value of variable Y, as logical file specified by the value of variable X on the disk channel specified by the value of varible Z. Several special forms of the OPEN command can also be used: OPEN 7,8,15,"{command string}" will OPEN the disk command channel on device 8 as logical file 7 and then send the command specified by "{command string}" to the disk drive. OPEN 1,8,0,"$0[:{pattern}]" will OPEN the disk directory as a file which can be read in subsequent GET#1's and INPUT#1's, just like any other file on the disk, optionally searching for a given filename or wildcard pattern. OPEN 2,8,4,"#[{n}]" will OPEN disk buffer {n} in the 1571's internal RAM for use by the BLOCK commands as logical file 2, disk channel 4. If {n} is not specified, DOS will assign the next available buffer. If a specific buffer is requested, but is not available, a DOS error code 70 "NO CHANNEL" will be issued.
An example for a relative file would be:
OPEN 3,8,3,"RELFILE,L,"+CHR$(25) This example indicates a record length of 25 characters. DOPEN The BASIC 3.5 and 7.0 version of the OPEN command is: DOPEN#{file#},"{filename},[{type}]"[,U{device#}][,D{drive#}][,W] The [,W] flag is used to indicate a write. If no file type is specified for a read operation DOPEN will open any type of file, while for a write operation, a SEQ file is assumed if none is specified. It should be noted that DOPEN cannot be used to a open a link to the disk command channel or a direct access disk buffer channel because it does not allow the explicit specification of a channel number. A channel number is chosen automatically by the C-128 operating system. An example would be: DOPEN#1,"DATAFILE,S",W This will DOPEN the SEQ file named "DATAFILE" as logical file 1, on the default disk drive, for writing. The second form of the DOPEN command is used specifically to create relative files:
DOPEN#{file#},"{filename}",L{record size}[,U{device#}][,D{drive#]
where {record size} specifies the length of the relative file record in bytes ranging from a minimum of 2 to a maximum of 254.
For example: DOPEN#2,"RELFILE",L74 will DOPEN the relative file "RELFILE" with a record length of 74 bytes as logical file 2. For a more complete description of the use of relative files, see 1571-5.TXT (Effective Use of Files). APPEND APPEND is a modified form of the DOPEN command which allows you to add data to the end of an existing SEQ data file. This is very useful for expanding existing files without having to read in the entire existing file, write it back to a new file, then write the new data. An OPEN or DOPEN is not required to be used before an APPEND. The syntax is: APPEND#{file#},"{filename}"[,U{device#}][,D{drive#}] With the APPEND command, {filename} may contain wildcards and pattern matching characters. In such a case, the first file matching the pattern will be opened for appending data. For example: APPEND#3,"MORE DATA" will OPEN the file "MORE DATA" as logical file 3, to allow more data to be written to the end of the file. APPEND will only allow you to OPEN a file in write mode.
Communicating =============
Once a disk file has been OPENed, the computer can communicate directly with the file by reading with a GET# or INPUT#, or writing data with a PRINT# as appropriate. GET# In BASIC, data can be read from a disk file only in program mode. One of two commands can be used. The first one acts on a single character at a time and has the following syntax: GET#{file#},{variable list} where {file#} is the file number used in the OPEN or DOPEN command for that particular file and {variable list} is a list of string variables to which the data bytes will be assigned. Note that there must not be a space between GET and #. Similar to the GET command for keyboard entry, one byte will assigned to each variable in the list. If no more bytes are in the file, a null byte ("") is returned. For example: GET#2, A$,B$,C$ will read the next three bytes from file 2 and assign them to A$, B$, and C$ respectively. When the end of the file has been reached, the system status variable (ST) will change from a value of 0 to 64. This can be used as a simple method for detecting the end of a file. GET# will not hang up or wait if it reaches the end of a file and cannot read a byte. INPUT# The second file reading command acts on a sequence of bytes, either in numeric form or as a character string. The syntax is: INPUT#{file#},{variable list} The parameters have the same meaning as those in the GET# command and, again, there is no space between the keyword and #.
Similar to the INPUT command for keyboard entry, the {variable list} can contain any combination of numeric and string variables. However, the variable list must match the type of data in the disk file or a BASIC error code 22, FILE DATA, error will be generated.
If a given string is longer than 160 characters (the length of BASIC's input buffer), a BASIC error code 23, STRING TOO LONG, error will result. Normally, the maximum length for a string is 255 characters. The length of a string in a disk file is determined by the presence and location of delimiters in the file such as carriage returns and commas. If the file data are not properly delimited, INPUT# may attempt to read more than 160 characters (80 characters on the C-64, 88 on the VIC-20) creating the above error. An example would be: INPUT#2, A,B$,C,D This will read the values of A, B$, C, and D from disk file 2. A, C, and D are numeric, while B$ is a character string. It should be noted that unlike the GET# command, if there is not enough data left in the file to satisfy all of the variables in the list or the data are not properly delimited, INPUT# may hang up and wait forever for data which will not come. Because of this, INPUT# should only be used on a file containing known data. GET# should be used on unknown files. Proper delimitation of disk files is discussed in detail in 1571-5.TXT (Effective USe of Files). PRINT# Data can be written to a disk file in both immediate and program mode using the PRINT# command. This is the opposite of INPUT# and will work with any combination of single characters, numerics or strings. The syntax is: PRINT#{file#},{variable list} The parameters are the same as for GET# and INPUT#. Similar to INPUT#, the variable list may contain any combination of string and numeric variables. The list may also contain string or numeric constants. For example:
PRINT#1,A;B$;255;"MORE TEXT" will print the numeric data specified by A, the character string specified by B$, followed by the number 255 and the text string "MORE TEXT". If A=-2 and B$="SOME TEXT" then the data will look like this on the disk: -2 SOME TEXT 255 MORE TEXT<cr> where <cr> is the carriage return character (ASCII CHR$(13)). Print formatting functions, such as TAB(x), <cursor down>, etc., do not work properly with disk files. However, formatted output can be achieved using a form of BASIC 7.0's PRINT USING command. The syntax is: PRINT#{file#},USING"{definition string}";{variable list} A "{definition string}" is generally a series of "#" characters, each of which represents one numeric or character position. A decimal point can be used in the # string for numeric output to specify the placement of the decimal point in the output. An equal ("=") sign will centre a string in the field specified by the "#"'s, while ">" will right justify it. Both symbols count as an extra character space, just like an extra "#". For example: PRINT#1,USING "=#########";"TEXT" will centre the word "TEXT" in a 10 character field and send the 10 character string to file 1. Any other character in the definition string will be printed as a literal. The "PRINT#, USING;" statement can be used to delimit data by using a comma "," or carriage return in the definition string. The definition string can either be a constant as shown above, or a variable enclosed in parentheses such as: PRINT#1,USING(A$);"TEXT","NAME",255,27
By defining A$ as "######"+CHR$(13), a carriage return will be automatically inserted between data items. Note that multiple items in a data list for PRINT#,USING; are separated by commas. PRINT#,USING; is not available with BASIC 2.0. CMD Data can also be written to a disk file by redirecting the normal screen output to an open file. The BASIC command for output redirection to a peripheral device is: CMD{file#} where {file#} is any open output file. All output which normally goes to the screen, such as from a PRINT statement or a LIST command, will be sent to the specified file. An exception to this is DIRECTORY and CATALOG. These do not allow redirection of the output from the screen. (Because Commodore computers do not buffer the serial port, you cannot read and write to two different files or devices on the serial port simultaneously.) The following example will create a sequential file listing from any program currently in memory in text form which can be incorporated into a document with a word processor. (This technique was used extensively in the preparation of this document for adding the numerous program listings to the text.) OPEN 1,8,8,"LISTING,S,W" CMD 1 LIST PRINT#1:CLOSE1 RECORD The final communications type command applies to relative files only. The RECORD statement is used to position the relative file pointers to the correct record and, optionally, byte number within the record. The syntax is: RECORD#{file#},{record#}[,{byte#}] The {record#} can range from 1 to 65535, while {byte#} can range from 1 to 254. However, {byte#} cannot be greater than the record length specified when the file was opened. If {record#} is greater than the last record in the REL file, a DOS error code 50, RECORD NOT PRESENT, error will be generated. If writing the record, this is not really an error, just an indication that a new record is being created and the REL file is growing. An example would be: RECORD#2,45,12 This will position the relative file pointers for previously OPENed logical file 2, at record #45, byte 12. The data can then be read with INPUT# or GET# or written with a PRINT#. Commodore recommends that the RECORD command be given once before and again after attempting to read or write a relative file. This is probably essential before writing data to maintain file integrity. RECORD is not available with BASIC 2.0. For a more complete description of the creation, use and maintenance of all types of disk files, see 1571-5.TXT (Effective Use of Files). Breaking the Connection =======================
After all input and output has been performed on a file, it must be properly closed before the job is complete. This is especially important after writing to a file because the directory entry for the file will not contain the correct information about the file until after it has been closed and you could lose all of your data into a "splat" file. CLOSE CLOSE is the most common command for closing disk files. The command can be used either in immediate mode or from within a program. The syntax and usage is as follows: CLOSE {file#} closes the logical file specified by {file#}. This number will correspond to the logical file number under which the file was originally OPENed. The file can be an input or output file to a disk drive or any of the other devices such as cassette, printer, keyboard, etc. CLOSE acts on a single file only. For example: CLOSE 4 will CLOSE logical file 4. DCLOSE The BASIC 3.5 and 7.0 command: DCLOSE [U{device#}] will close all open files on the specified device, while the command: DCLOSE#{file#} closes the specified logical disk file. For disk files, it performs the same function as the CLOSE {file#} command. For example: DCLOSE will CLOSE all OPEN files (including the command channel) on device 8, drive 0. DCLOSE#4 performs the same function as CLOSE 4. DCLEAR The final closure command in BASIC 7.0 will close all disk files on the specified device and drive, then initialize the drive to its power up condition. The form of this command is:
DCLEAR [U{device#}][,D{drive#}] This is similar to the DOS "I0" command discussed in 1571-3.TXT (The 1571 Disk Operating System), except that OPEN files are properly CLOSEd. Utility Commands ================
BASIC 3.5 and 7.0 contain a number of utility commands which simplify the processes of copying, renaming and deleting files; reading disk directories; formatting new disks; etc. They are presented here in alphabetical order. These commands are not available in BASIC 2.0. However, the 1571 disk operating system supports commands to perform equivalent functions. These are described in greater detail in 1571-3.TXT (The 1571 Disk Operating System). BACKUP As its name might suggest, the BACKUP command is provided to make an exact duplicate of an entire source disk on a target disk. However, it only works with a dual disk drive and, as such, is not relevant for the 1571. (At one point, Commodore had planned to release a dual drive version of the 1571, dubbed the 1572. This command would have been used with that drive.) It is, however, included here for completeness. The syntax is: BACKUP D{source drive#} TO D{target drive#}[,U{device#}] The C-128 will prompt with the message "ARE YOU SURE?". Type in Y <RETURN> to proceed or any other entry to abort. BACKUP cannot be used with two single drives. It produces a DOS error code 31, SYNTAX ERROR, on the 1571. CATALOG and DIRECTORY BASIC 3.5 and 7.0 provide two commands for displaying the disk directory. The syntax for both CATALOG and DIRECTORY is identical, and the commands can be used interchangeably. Take your pick. They can be used either in immediate mode or from within a program. Unlike the BASIC 2.0 directory read method (LOAD"$",8), CATALOG and DIRECTORY do not overwrite the program space in RAM. Similar to the BASIC 2.0 method, both commands will also display the disk name and the number of blocks free. The syntax for the commands is: CATALOG ["{pattern}"][,U{device#}][,D{drive#}] (or DIRECTORY)
The "{pattern}" parameter allows the searching for certain filenames and/or file types. For example, CATALOG "MINE" will list only the file named "MINE", if it exists, while CATALOG "M*" will list all files beginning with the letter M. Multiple patterns can also be searched, such as: CATALOG "A*,B*,C*" will display all files beginning with either A, B, or C. The pattern can also include the file type to be searched. CATALOG "*=S" will display all of the SEQ (sequential) files on the disk. In the case of multiple pattern searches, only one file type can be searched. For example, CATALOG "A*,B*,C*=P" is valid, while CATALOG "A*=S, B*=P" is not. The valid file types for pattern searching are: S for SEQ, P for PRG, U for USR, and R for REL. Either the full (e.g. SEQ) or abbreviated (S) types can be used. Currently, the only way to produce a hard copy of a directory listing on a printer is with the BASIC 2.0 method LOAD"$",8 described previously. COLLECT The COLLECT command is provided to "cleanup" the disk block allocation map (BAM) which keeps track of used disk space. It is equivalent in function to the DOS VALIDATE command discussed in 1571-3.TXT (The 1571 Disk Operating System). On a heavily used disk, especially one which contains improperly closed files, this may free up some extra disk space for use. The syntax is: COLLECT [U{device#}][,D{drive#}] When the command has been issued, the computer will suspend operation until the cleanup has been completed. This may take several minutes or more, depending on how much of the disk has been used. COLLECT can be used either in program or immediate mode. Unlike the DOS VALIDATE command, COLLECT will cause the computer to suspend operation until it has completed its task.
COLLECT should not be used on GEOS disks, disks with a BOOT sector in use, or double sided disks in a 1541 (or a 1571 in 1541 mode) because each of these contain special flag values which will be destroyed by the action of the COLLECT command. More on this can be found in section 5.2.7. CONCAT CONCAT will concatenate (join) two disk files by appending one to the end of the other. The syntax is: CONCAT "{second filename}"[,D{second drive#}] TO "{first filename}"[,D{second drive#}][,U{device#}] The file specified under "{second filename}" remains unchanged on the disk by the procedure, while "{first filename}" contains its original data followed by a copy of the data from "{second filename}". For example: CONCAT "DATAFILE2" TO "DATAFILE1"
will join the data in "DATAFILE2" to the end of "DATAFILE1". The original "DATAFILE2" will remain on the disk intact. CONCAT works in both immediate mode and program mode with either SEQ or USR files. COPY The COPY command can be used for creating an exact copy of a file on the same disk (or on a second disk with a dual drive unit). The syntax is: COPY [D{source drive#}]"{source filename}" TO [D{target drive#}]"{target filename}"[,U{device#}] where the "source" refers to the disk file being copied from and the "target" is the new copy being made.
The COPY command is an excellent way to create multiple backup copies of a file on a disk. Since it works both in immediate mode and program mode, COPY can be automated in a short program such as the following: 10 INPUT"FILE TO COPY FROM";F$ 20 INPUT"NUMBER OF COPIES TO MAKE";N 30 FOR I=1 TO N 40 F2$=STR$(I)+"."+F$ 50 COPY (F$) TO (F2$) 60 NEXT This program will automatically make N copies of any file which you specify, each stored under a slightly different filename such as: "1.DATAFILE"; "2.DATAFILE"; "3.DATAFILE", etc. HEADER When a disk is first unpacked from a brand new box, it cannot be used immediately. The magnetic surface of the disk must be organized into packets called tracks and sectors that the disk drive can use for storing data. (Although you cannot see them with the eye, or even with a microscope, the formatted disk has a series of magnetic "marks" on its surface which divides it into easily recognizable areas for the disk controller.) This is generally called "formatting" a disk. With BASIC 3.5 or 7.0, it is done with the HEADER command: HEADER "{disk name}"[,I{disk ID}][,U{device#}][,D{drive#}] "{disk name}" is a general name for the disk, a sort of "volume label". It is the heading which appears in reverse characters at the beginning of a displayed disk directory. The {disk ID} is a secondary identifier for the disk. It can be any two character string and is used by the internal operating system of the 1571 to help keep track of whether or not a disk has been changed. It can also be used as a unique disk identifier for many disk cataloging programs. Because the {disk ID} is used by the operating system to distinguish between different disks, each of your disks should be given a different ID code (in theory anyway) to avoid the possibility of mixups, especially when writing to a disk. Once a disk has been formatted with a given ID code, the code cannot be changed. The ID code is written into the header bytes of every sector on the disk. These bytes cannot be changed by the user without totally erasing the data on the disk. If the {disk ID} parameter is omitted, only the directory and BAM sectors of the disk are reformatted (i.e erased). This is usually called a "short format" and is a quick way to erase old disks that have been previously formatted but you wish to re-use.
When operating in immediate mode, the C-128 will prompt with an "ARE YOU SURE?" message before it executes the HEADER command. No prompt appears in program mode. For both modes, the C-128 will suspend operation and wait for approximately 45 seconds, for a full format, until the disk drive has finished its task. For example: HEADER "MY DISK",ID 2K,U9 will format the disk in device 9 and give it the name "MY DISK" with an ID code of "2K". HEADER "MY SECOND DISK" will erase the disk directory and BAM of a previously formatted disk and rename it "MY SECOND DISK". The ID code remains unchanged. In addition, although the actual data are still on the disk they cannot be accessed without a sector editor because the directory and BAM have been destroyed. RENAME RENAME allows you to change the name under which a file appears in the disk directory. The syntax is: RENAME "{old filename}" TO "{new filename}" [,U{device#}][,D{drive#}] RENAME works with all file types in BASIC 3.5 or 7.0 in either immediate or program modes. Only the entry in the disk directory is changed, the file itself remains the same. For example: RENAME "APPLES" TO "ORANGES" will change the name of the file "APPLES" to "ORANGES". SCRATCH The final utility command is SCRATCH. The function of this often used command is to erase a file from the disk directory. The syntax is: SCRATCH "{pattern}"[,U{device#}][,D{drive#}] "{pattern}" can be a single file name, or a series of names represented by wildcard entries or multiple names separated by commas. Similar to the HEADER command, BASIC will prompt with the "ARE YOU SURE?" message before actually SCRATCHing the files. Some examples are: SCRATCH "BAD FILE" will erase the specified file from the directory. SCRATCH "A*" will erase all filenames that begin with the letter "A" from the directory. SCRATCH "ABC,FILE1,TES*" will erase the filenames "ABC" and "FILE1", along with all filenames beginning with the letters "TES". The computer will suspend operation until the SCRATCH has been completed. After the files have been deleted, a message will be displayed similar to: x FILES SCRATCHED where x is the number of files it has deleted.
Files created with certain names (often under error conditions) can be difficult to get rid of. A typical example involves an error in an OPEN statement that creates a file with the single character name such as a single quote mark ("), ":" (a colon) or "," (a comma). The simplest way to SCRATCH a file of this type is to use pattern matching characters, such as: SCRATCH "?" Other non-standard characters, such as shifted letters or graphics symbols may also cause similar problems when SCRATCHing.
Before doing a SCRATCH this way, however, you should make sure that there are no other files on the disk which match the pattern. If there are, you must RENAME these files or else they will be SCRATCHed as well.
It should be noted that the files themselves have not actually been deleted, only a flag set in the directory entry to indicate a scratched file. Although the disk space for the deleted files is also de-allocated, the disk file can still be recovered in most cases with fairly simple techniques, such as the C-128 DOS shell "UNSCRATCH" function. A file should be unscratched immediately if it has been accidentally SCRATCHed, and certainly before new data is written to the same disk. Failure to do so immediately may result in the irretrievable loss of some data as its disk space is re-used by other files. Error Handling ==============
With BASIC 3.5 or 7.0's error detecting and correcting routines, you can recover from most errors without crashing. There are two error detection methods available in BASIC 3.5 and 7.0, one for disk errors and the other for BASIC errors. DS and DS$ Disk errors can be conveniently detected using the reserved variables DS, DS$ and ST. DS contains the error code of the most recent disk access and DS$ contains the text description of the error. DS and DS$ depend only on the status of the last disk access, not the drive number, device number or file number where the error was caught.
A list of DOS error codes can be found in 1571-A.TXT. DS and DS$ remain at their error values until the next disk access is attempted. A simple statement such as: 10 IF DS THEN PRINT "DISK ERROR" DS$ will detect a disk error. DS and DS$ can be used either in program mode or direct mode, you do not need to open the disk command channel (#15) first. (With the C-64 and VIC-20, it was not possible to read the disk error channel in direct mode without a DOS wedge). In immediate mode, a simple PRINT DS$ will print the error message on the screen.
In BASIC 2.0, the disk error status can normally only be read in program mode or through the use of a DOS utility wedge. As a matter of good programming practice, you should read the disk error channel when ever you perform normal disk operations, such as OPEN, INPUT#, GET#, PRINT#, BLOAD, etc. The only exception is after issuing a burst mode command to the 1571 drive with a PRINT# statement. This may cause your program to crash because reading the error channel cancels the burst mode commands by temporarily commandeering the command channel and issuing a different direct access command to the drive.
Reading DS or DS$ will also turn off the flashing error light on the disk drive. In program mode, the BASIC 2.0 method of reading the disk error channel works equally as well on the C-128 with both BASIC and machine language routines. ST BASIC has another reserved varible, ST (or system status) which can be used in conjunction with disk files. The value of this variable reflects the results of the last attempted input or output operation. A value of 0 indicates success, while any other value could indicate an error condition. ST can have a range of values depending on which error bits are set. For serial port input and output (including disk operations), the ST bit pattern is outlined in Table 4. The most common use of ST is to check for the end of a file being read with a series of GET#'s. For example, the following short program will read a file then exit when it gets to the end: 10 OPEN 1,8,8,"DATAFILE" 20 GET#1,A$:PRINT A$; 30 IF ST=64 THEN CLOSE#1:END 40 GOTO 20 ST can also be used with machine language routines. The KERNAL READSS function at $FFB7 (decimal 65453) will return the most recent value of the status byte in the .A register. The ST value is also stored at RAM location $90 (decimal 144) for all Commodore machines. TRAP BASIC 3.5 and 7.0 support a command to divert program flow to a specific sub-routine if an error is encountered during execution. To enable BASIC error detection, your program needs a statement such as: 10 TRAP xx where xx is the first line of your error handling routine. The TRAP statement should be near the beginning of the program or major subroutine that you want to protect from error crashing. More than one TRAP statement can be included in a program, but only the most recently encountered one will be active. Thus you can have different error handling routines for different sections of your program. Error trapping is disabled with the statement: 100 TRAP with no line number specified. When an error is detected, the program flow is diverted to the error handling routine mentioned in the TRAP statement, just as if a GOTO had been encountered. Once in the error routine, you can determine the nature and location of the error using the reserved variables ER and EL. ER contains a numerical code corresponding to the type of error that was encountered. The disk and file related error codes are listed in 1571-A.TXT. Some of the errors, such as a SYNTAX ERROR, cannot be corrected unless you edit the program. Others, such as FILE NOT FOUND can usually be corrected by specifying a new value for a given variable. A text description of the error can be read with the function ERR$(ER). For example, to detect a FILE NOT FOUND error, you would use an expression such as: 10 IF ER=4 THEN PRINT ERR$(4).... etc. where .... etc represents your additional code. ER and ERR$() can be used in direct mode with a PRINT ER or PRINT ERR$(ER) statement.
The HELP command can be used in direct mode to list the line where the error occured and highlight the error causing portion in a multi-statement line. If HELP is used in program mode, execution will stop and the error line will be listed with the error highlighted.
The <RUN/STOP> key can be detected with TRAP and ER. It has been assigned an error code of 30. The reserved variable EL contains the line number where the error occured. If the error occured in direct mode, EL will have a value of 65535. To detect any error in a given line, say line 100, of your program, the error routine would contain a line such as: 20 IF EL=100 THEN PRINT "ERROR IN LINE 100".... etc. Two caveats should be mentioned about using EL and ER in programs. The line number referenced in an EL statement will not be renumbered when you use the RENUMBER command, so check all the EL's after renumbering a program. The second drawback is that you cannot TRAP an error within the error routine itself. TRAP is disabled until control is returned to the main program.
This brings up the next item of discussion: error recovery and returning control to the main program. An error detection routine would not be much use if you could not correct the error or at least return to the normal execution of the program. The error correction depends on the specific application and may involve setting default values for parameters or printing an error message and asking for a new input etc. Returning to the main BASIC program is done with one of the following statements: 100 RESUME 110 RESUME NEXT 120 RESUME xx where xx is the line number to continue with. With the first form, BASIC will try to re-execute the statement where the error occured. In the second form, BASIC will execute the statement immediately following the one where the error was encountered. In the third form, the program will resume execution at the specified line number. Until one of the RESUME statements is encountered, BASIC disables further error trapping. Thus if a second error occurs during the error handling routine, the program may crash. RESUME will not purge the stack of the most recently unused GOSUB, FOR-NEXT, DO-LOOP, etc. parameters before returning control to the specified line, so be careful of nested subroutines and loops.
An error trapping routine may have more than one RESUME statement: 100 IF EL=100 THEN RESUME 10 120 IF ER=4 THEN RESUME NEXT etc.
This helps in directing the flow of the program back to the specific area required after detecting and/or correcting the error. It also minimizes the chance of crashing during error trapping by allowing you to perform error correction outside of the main error detection routine.
After the execution of the RESUME statement, ER is set to a value of -1 and EL is reset to 65535, both indicating a no error condition. Error Priorities In reviewing the list of error codes for both BASIC and DOS, you may have noticed that some of them are the same. What happens in such cases? The first level of error detection is always with BASIC. Thus if you do not have a TRAP statement, and the BASIC error is encountered, the program will crash. For example, a "FILE NOT FOUND" error (BASIC error #4 and DOS error #62) should be handled by a TRAP statement. However, to turn off the blinking error light on the disk drive, you must also read the disk error channel with a statement such as: 50 ZZ=DS
TABLES:
Table 1: Abbreviations used in BASIC 3.5 and 7.0 disk commands
Letter Meaning Comments B BANK denotes the bank number (0 to 15) on the C-128 from which memory is saved or into which a disk file is loaded
D DRIVE denotes a drive number (0 or 1) of a dual disk drive. Must be 0 for a 1571 single drive
L LENGTH denotes the record length in bytes for REL files
P POSITION denotes the start or finish position for a memoery load or save operation
U UNIT denotes the device (unit) number for a disk drive. Usually 8 or 9. Always 8 if you have only one disk drive. ====================
Table 2: BASIC workspace pointers
Computer Start of BASIC Pointer End of BASIC Pointer Vic-20, C-64 $2b-$2c $2d-$2e SX-64, C-16 (43-44 dec) (45-46 dec) Plus/4
C-128, C-128D $2d-$2e $1210-$1211 (45-46 dec) (4624-4625 dec)
All pointers are in lo byte-hi byte format. =====================
Table 3: Noteworthy C-128 Memory Areas in Bank 0/Bank 15
Hex Decimal Comments 0400-07ff 1024-2047 40 col text memory 0b00-0bff 2816-3071 cassette buffer 0c00-0cff 3072-3327 RS-232 input buffer 0d00-0dff 3328-3583 RS-232 output buffer 0e00-0fff 3584-4095 sprite definitions 1000-10ff 4096-4351 function key definitions 1300-1bff 4864-7167 unused (good area for short ML programs) 1c00-1fff 7168-8191 320 x 200 graphics color map 2000-3fff 8192-16383 320 x 200 graphics bit map ======================
Table 4: Values for system status variable ST
Bit ST Value Comment 0 0 all OK, no error 0 1 no response while writing data (disk drive won't acknowledge) 1 2 no response while reading data (disk drive won't send data) 4 16 VERIFY error 6 64 EOF (end of read file) 7 128 device not present =======================
|
|
|
Post by jmpff3d on Nov 21, 2018 13:14:02 GMT
(Continued from Header post)
rev 1a 96-11-01 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * THIS DOCUMENT IS COPYRIGHT (C) 1988, 1996 BY HERNE DATA SYSTEMS LTD. THE MATERIAL CONTAINED HEREIN MAY BE FREELY USED FOR PERSONAL INFORMATION ONLY. IF YOU REPRODUCE IT, THIS COPYRIGHT NOTICE MUST NOT BE REMOVED. THIS MATERIAL MAY NOT BE EXPLOITED FOR COMMERCIAL PURPOSES. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Herne Data Systems Ltd., PO Box 250, Tiverton, ON N0G 2T0 CANADA. Voice/fax 519-366-2732, e-mail herne@herne.com internet: http://www.herne.com
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
The 1571 Disk Operating System The 1571 disk drive is an intelligent device. It contains its own microprocessor, interface hardware, RAM, and a ROM based operating system. The ROM based disk operating system (DOS) contains the software which normally controls the operation of the drive. The various DOS functions are activated by sending specific mnemonic type commands to the disk drive over the command channel. The disk drive then executes the command independently and reports back a status to the computer, generally over the same command channel.
The DOS can be divided into two main segments: the command interpreter and the controller. The interpreter receives the command from the computer and breaks it down into a series of elemental steps, such as requests to read or write specific disk sectors. The sector parameters are passed to the controller routines, which perform the actual task. The interpreter takes the result of the series of controller steps and passes it back to the computer as applicable. The current models of the 1571 operate on CBM DOS version 3.0. The DOS program is contained in the disk drive's ROM at address locations $8000 to $FFFF. Address location $8000 to $BFFF generally contains the new 1571 DOS routines, while $C000 to $FFFF contains the 1541 emulation DOS, essentially a duplicate of the true 1541 DOS with minor modifications to allow some of the new 1571 commands to be used in 1541 mode. The DOS commands can be sent to the drive either explicitly (via a PRINT# statement to the previously opened command channel) or indirectly using one of the BASIC 3.5 or 7.0 disk related commands (such as HEADER, COPY, etc.) The BASIC 3.5 and 7.0 disk commands work by translating the BASIC parameters into a series of DOS commands and sending them to the disk drive. Explicit commands must be used with BASIC 2.0 and are also used extensively with machine language programs. Because they are strictly user controlled, explicit DOS commands can also be more versatile than BASIC 3.5/7.0 disk commands. Unlike the BASIC disk commands, the explicit DOS commands generally will not suspend computer operations while they are being executed (unless further disk activity has been requested). This increases the apparent speed of operation of most disk intensive programs by allowing the computer and disk drive to operate independently. This document describes the use and operation of the disk drive command channel along with the function and use of each of the 1571 DOS commands, complete with examples. The examples are given in BASIC. A description of assembly language routines for performing similar functions can be found in 1571-4.TXT (DOS & The KERNAL). When listing the syntax for a particular command, square brackets, "[]", are used to enclose parts of the command which are optional. All other parts must be entered as listed. The DOS commands are generally grouped into five sets: general utility commands, block commands, memory commands, user commands and burst mode commands. The first four will be described in this document, while the burst mode commands are detailed in another document. The first four groups of commands will only work with Commodore GCR disks, while most burst mode commands can be used with either GCR or MFM disks. Each group of commands performs a range of functions, some of which may appear to be overlapping. In such cases, however, there are subtle differences in the way the commands act. These differences are detailed where they occur. The Command Channel All commands from the computer to the disk drive are passed through a special communication channel called the command channel. This channel is also used for passing error and status information from the drive back to the computer. Physically, the data are transferred to/from the disk drive over the serial bus, just like normal data. However, the command data are handled differently by the disk drive. The command channel can be opened with a standard BASIC 2.0 OPEN statement using the reserved secondary address, or channel number, of 15. For example: OPEN 1,8,15 [,"{command string}"] will open the command channel on disk drive device number 8 as logical file number 1 and, if specified, immediately send the command specified by {command string} to the drive. In this case and all cases outlined below, {command string} can be either a constant or a variable, or some combination of both. If variables are used, the quote marks should not be used around the variable portions. The command channel can be opened in either program or immediate mode. The BASIC 3.5/7.0 DOPEN command cannot be used to open the command channel because it has no provision for explicitly specifying a channel number. All subsequent communication to the disk drive can now be handled with a: PRINT#1,"{command string}" statement, where {command string} is chosen from one of the formats outlined in the remainder of this document based on the desired function. As in the previous case, the command string is sent to the input buffer of the 1571 drive, located in the disk drive RAM at address $200 (dec 512). Output to the command channel can be made in either program or immediate mode.
The command input buffer of the 1571 drive is 41 bytes long. Therefore, the command string, complete with all anciliary options specified, must be less than 41 characters long to be able to fit in this command buffer. This is not normally a problem, except when specifying multiple long file names during copy, scratch, rename, etc. type operations. The DOS commands can be abbreviated to two or three characters, which helps to alleviate the congestion. The command channel is also used for passing error and status reports, as well as data in some cases, from the drive back to the computer. In this case, it is often referred to as the "error channel" and can be read using standard INPUT# and GET# statements. Since Commodore BASIC can normally only accept input in program mode, the error channel is difficult to read in immediate mode without special programming techniques such as DOS wedges.
The following example demonstrates reading the error channel in program mode: 10 OPEN 1,8,15 20 INPUT#1,A1,A2$,A3$,A4$ 30 PRINT A1;A2$;A3$;A4$ 40 CLOSE 1 In this example, A1 will return the numeric value of the DOS error code, similar to reserved variable DS in BASIC 3.5 and 7.0. A2$ will return the description of the error, while A3$ and A4$ will return the track and sector where the error occured, if applicable. The text string formed by the combination of A1, A2$, A3$, and A4$ is the equivalent of the reserved variable DS$ in BASIC 3.5 and 7.0. This example will work with all versions of Commodore BASIC. Reading the error channel in this manner will also extinguish the flashing error light on the drive if it is on. It should be noted that when the command channel is closed, all other open disk channels (i.e. disk files and random buffers) on the device are closed within the drive and their internal channels and buffers are de-allocated. However, the logical files in the computer associated with these disk files remain open. If a subsequent attempt is made to read or write one of the de-allocated direct access buffers, a DOS error code 70, NO CHANNEL, error will be generated. For an open read file, an end of file indication will be returned in the status variable ST along with null data. For an open write file, a DOS error code 61, FILE NOT OPEN, error message will be generated. A BASIC CLOSE type of statement must be used for each OPEN file in order to properly close the file in the computer. This is especially important for write files. General Utility Commands The 1571 general utility commands provide housekeeping and file management functions. The commands are summarized in Table 1 and outlined below in alphabetical order. In all cases, either the full or abbreviated form can be used. However, the abbreviated version is often preferred due to the limitation of the size of the 1571's command input buffer, especially if many options are specified. All utility commands work identically in both 1571 mode and 1541 mode. TABLE 1: General Utility Commands .............................................................. Command Syntax .............................................................. COPY "C0:{target file}={sourcefile1} [,{sourcefile2}...]" INITIALIZE "I0" NEW "N0:{diskname},{ID code}" (full format) "N0:{diskname}" (short format) POSITION "P"+CHR$({channel#}+96)+CHR$({rec#lo})+ CHR$({rec#hi})+CHR$({byte}) RENAME "R0:{new name}={old name}" SCRATCH "S0:{pattern}[={filetype}]" VALIDATE "V0" ............................................................
Copy The COPY command is used to make a duplicate of a file or combine several files into a longer one on the same device. Since the 1571 is a single drive device, the copy must be on the same disk as the original. It must also have a different filename. COPY works by reading a sector from the source file, then writing it to the target file. This is repeated for as many sectors as there are in the file. The syntax for the command is: "COPY{drive#}:{target file}={drive}:{source1}[,{source2},...]" {target file} is the name of the file created by the copying process, while {source1} etc. are the file(s) being copied from. By specifying more than one source filename, each separated by a comma, several files can be joined into a single file. However, all files must of the same type and generally only SEQ files will produce meaningful results when combined in this manner. Multiple REL files cannot be combined in this way. With the 1571, {drive#} is always 0 (numeral zero). The total length of the command string cannot exceed 41 characters. Wild cards and pattern matching can be used in the source filenames, but the target filename must be unique. Alternatively, the word COPY can be abbreviated to the single letter "C" resulting in the following format: "C0:{target file}=0:{source1}[,{source2}...etc]" For example, the following will create a new file named FRUIT which will contain the combined contents of the three source files, in the specified order: OPEN 15,8,15,"C0:FRUIT=0:APPLES,0:ORANGES,0:PEACHES":CLOSE 15 The source files will remain untouched on the disk. If one of the specified source files does not exist, then a DOS error code 62, FILE NOT FOUND, error will be generated and the copying process will stop. If a file exists under the same name as the new file being created, a DOS error code 63, FILE EXISTS, error will occur and the copying process will stop. The following example demonstrates the use of variables in the command string in program mode: 10 OPEN 15,8,15 20 INPUT "FILE TO COPY FROM";F1$ 30 INPUT "FILE TO COPY TO";F2$ 40 PRINT#15,"C0:"+F1$+"=0:"+F2$ 50 INPUT#15,A,B$:IF A=0 THEN PRINT"DONE":CLOSE 15:END 60 PRINT"DISK ERROR "A;B$:GOTO 20 This short program will copy any file that you specify to any other filename. It is a simple method for making multiple backup copies of a program or data file on a single disk. It should be noted that the order of specifying source and target files for the DOS COPY command (target=source) is the opposite of the equivalent BASIC 3.5/7.0 command (COPY "source" TO "target"). This syntax is, however, identical to that used by CP/M's PIP utility for copying files (PIP target=source). Initialize INITIALIZE is used to log in a disk and read the block allocation (or availability) map (BAM) sectors into the disk drive RAM buffer #5 at memory location $700 (dec 1792) of the 1571 drive. The primary BAM sector is track 18, sector 0. A double sided disk also contains a copy of the second side BAM on track 53, sector 0 (corresponding to track 18, but on side 1). Performing an initialization is often useful as a precaution after changing disks to ensure that the correct BAM information has been read into the drive for future input and output operations. Initialization should also be performed after any disk error has been detected because certain errors and disk operations may leave an incorrect version of BAM in the drive buffer. It is especially important before performing any write operations (such as a SAVE) or any of the BLOCK commands described in the next section of this document.
The syntax is: "INITIALIZE{drive#}" No options or filenames are specified with this command. As with all 1571 commands, {drive#} is always 0. Alternatively, the command can be abbreviated to: "I0" An example of a disk initialization would be: OPEN 2,8,15,"I0":CLOSE 2 INITIALIZE will de-allocate the disk drive's internal channels associated with all open files thus cancelling but not properly closing them, so make sure that all of your open write files have been properly closed before you change disks and use this command. The logical files in the computer are also not closed by an INITIALIZE, and must therefore be closed separately.
The INITIALIZE procedure is often used as a "soft reset" for the disk drive. It will also clear the disk error channel. If no disk is in the drive or the BAM sector (track 18, sector 0) cannot be found or read, a DOS error code 74, DRIVE NOT READY, error will occur. INITIALIZE can only be used with Commodore GCR type disks. Other disk types will produce the DRIVE NOT READY error. New The NEW command is used to "new" or format a fresh disk or to erase a previously used disk that you wish to re-use. It is identical in function to the BASIC 3.5/7.0 HEADER command. The syntax is: "NEW{drive#}:{disk name}[,{id code}]" Alternatively, the command can be abbreviated to: "N0:{disk name}[,{id code}]" If {id code} is not specified, then only the disk directory and BAM are erased and the name is changed. In this case, the actual data on the disk are not destroyed. However, they are not readily accessible because the directory has been erased. This procedure is often referred to as a "short format". For example: OPEN 1,8,15,"N0:MYDISK,AA":CLOSE 1 will completely format (or re-format) a disk and give it the name "MYDISK" with an ID code of "AA". Unlike BASIC 3.5/7.0 HEADER command, NEW does not ask you to confirm your desire to format the disk. It also does not cause the computer to lock up until the formatting cycle has been completed. When in 1571 mode, NEW will always produce double sided disks. The formatting time for a full format is approximately 40 seconds, while for a short format the time is 5 seconds. In 1541 mode, single sided disk can be produced on either side of the disk. In this case, the full format time is approximately 80 seconds but the short formatting time remains at about 5 seconds. For a full description of formatting single sided disks, see the "U0>H" commands in the User Commands section at the end of this document. Position The POSITION command is used in conjunction with relative files. It is similar in function to the BASIC 3.5/7.0 RECORD# command for selecting the relative file record number and starting byte number to be used for a subsequent input or output operation. The syntax for the command is: "P"+CHR$({channel#}+96)+CHR$({lo})+CHR$({hi})[+CHR$({byte#})] where {channel#} is the secondary address used in the OPEN statement for the relative file, {lo} and {hi} are the low and high bytes respectively of the record number desired, and {byte#} is the optional starting byte in the record for the input/output operation. Although the starting byte is considered to be optional by Commodore DOS, it is a good idea to always specify it as a value of 1 when you want to start at the beginning of the record. This ensures that the record pointer is not left incorrectly positioned by any previous positioning commands and thus reduces the chance of positioning errors. For example, 20 PRINT#15,"P"+CHR$(98)+CHR$(3)+CHR$(1)+CHR$(5) will position the relative file pointer of the file associated with disk channel #2 to byte 5 of record 259 (1 x 256 + 3), assuming that the command channel has already been opened as logical file #15. As with the BASIC 3.5/7.0 equivalent, the P command should be given once before and again after a record has been accessed. If the requested record number is beyond the current end of file, a DOS error code 50, RECORD NOT PRESENT, error will be generated. If this error occurs prior to a write operation, the file will be expanded to include this new record, as well as any missing records between the previous end of the file and the requested one, when the write operation is actually performed. The missing records in between will be filled with blanks. If the error occurs prior to a read, you have gone past the current end of the file. This can be used to detect the highest record number in the file. Relative files are described in greater detail in "Effective Use of Files". Rename The RENAME command is used to change the name of an existing file in the disk directory to something else. The syntax of the command is: "RENAME{drive#}:{new name}={old name}" Alternatively, on the 1571, it can be abbreviated to: "R0:{new name}={old name}" DOS RENAME is identical in function to the BASIC 3.5/7.0 command of the same name. However, as with the COPY command, the order of specifying the file names is reversed to that of the BASIC command. The following example will rename the file APPLES to ORANGES: PRINT#1,"R0:ORANGES=APPLES" assuming that the disk command channel has been opened as logical file #1. The file type remains unchanged. If the file APPLES does not exist, a DOS error code 62, FILE NOT FOUND, error will occur. If a file named ORANGES already exists, regardless of its type, a DOS error code 63, FILE EXISTS, error will occur. As shown in the following example, variables can also be used with the RENAME command: 10 OPEN 15,8,15 20 INPUT "OLD FILE NAME";F1$ 30 INPUT "NEW FILE NAME";F2$ 40 PRINT#15,"R0:"+F2$+"="+F1$ 50 INPUT#15,A,B$:IF A=0 THEN PRINT"DONE":CLOSE 15:END 60 PRINT"DISK ERROR "A;B$:GOTO 20 In all cases, wild cards and pattern matching can be used in the old file name, but not in the new name. If pattern matching is used, DOS will rename the first file which matches the pattern. Scratch The SCRATCH command allows you to erase one or more files from the disk directory to get rid of old files and free up their disk space for re-use. The syntax of the command is: "SCRATCH{drive#}:{pattern1}[,{pattern2}...][={filetype}]" {pattern1}, {pattern2}, etc. can be single filenames or may include wild card and pattern matching characters. The total length of the command with all specified patterns cannot exceed 41 characters. On the 1571, the command string can be abbreviated to: "S0:{pattern1}[,{pattern2}...][={filetype}]" For example, OPEN 1,8,15,"S0:DATA*,FILE1":CLOSE 1 will erase any file beginning with the four characters "DATA" as well as a file named "FILE1". After scratching files, DOS will return with the status message error code 01, FILES SCRATCHED X, on the error channel where X represents the number of files actually erased. This confirms that files where in fact deleted. The indication of the number of files scratched is convenient as a check, especially if wild cards and pattern matching have been used in the filenames. To read the status message after using DOS SCRATCH, the error channel must be read manually. Unlike the BASIC 3.5/7.0 version of the command, you will not be asked to confirm your desire to perform the task. However, you will be able to carry on with other tasks while the disk drive is doing its job. DOS SCRATCHed files can be recovered under the same conditions described for BASIC's SCRATCH command in 1571- 2.TXT. WARNING: SCRATCH should not be used on SPLAT files. This could corrupt other valid files on the disk. To remove a SPLAT file, use the VALIDATE command described next. Validate A frequently used disk may contain a few blocks (or sectors) which have been "over looked" by DOS, especially if random files have been used, or SPLAT files are present. The VALIDATE command is used to "clean up" the BAM of a disk to recover these lost blocks. Lost blocks can be detected by adding up the number of blocks associated with each file, combined with the number of blocks free, and comparing the total to 664 for a single sided disk or 1328 for a double sided. If the total is less than the allowable maximum, then either some blocks have been allocated as random access files (see the BLOCK-ALLOCATE command below) or have been lost to SPLAT files. If SPLAT files are present, as indicated by an asterisk, "*", next to the file type in a directory listing, then the disk should be VALIDATEd to remove the SPLAT file. However, it should be noted that VALIDATE will also de-allocate any blocks which have been allocated by the BLOCK-ALLOCATE command, regardless of whether or not they contain data that you wish to keep. If the total of the block count is greater than the maximum values specified above, then some valid blocks may have been de-allocated as random access files, or two or more files have become "crossed" and corrupted. This is a dangerous situation. The disk should be VALIDATEd immediately before writing anything further to it. Failure to do so may result in hopelessly corrupted files. If the disk will not properly VALIDATE, some files might be hopelessly corrupted. In this case, all files should be immediately copied to a new disk, one at a time (i.e. do not use a whole disk duplicating program). The files can then be inspected individually to see which ones have been corrupted. The syntax for VALIDATE is: "VALIDATE{drive#}" On the 1571, this can be abbreviated to: "V0" For example: OPEN 15,8,15,"V0":CLOSE 15 will perform a validation of the disk in device 8, drive 0.
Similar to the INITIALIZE command, no file names or options are specified with VALIDATE. The command serves the same purpose as the BASIC 3.5/7.0 COLLECT command, but will allow you to procede with other computer functions while the disk drive is performing its function. As mentioned previously, VALIDATE should not be used on disks containing random files because the blocks associated with these files will become de-allocated and may be overwritten by DOS at a later time. This is especially important for C-128 programmers who use the BOOT sector on the disk. In addition, VALIDATE should not be used on a double sided disk when the 1571 drive is in 1541 mode or a 1541 drive is used. This action will reset the double sided flag in the BAM sector (track 18, sector 0, byte 3) to a value of 0 to indicate single sided and you will lose any data stored on the second side. It should be noted that this problem has been corrected with 1571 ROM releases of 4 and later (when used in 1541 mode). The disk can be reset to double sided mode using the following procedure:
10 OPEN 15,8,15,"I0" 20 OPEN 3,8,3,"#" 30 PRINT#15,"U1: 3 0 18 0" 40 PRINT#15,"B-P: 3 3" 50 PRINT#3,CHR$(128); : REM FLAG = 128 FOR DOUBLE SIDED 60 PRINT#15,"U2: 3 0 18 0" 70 CLOSE 3:CLOSE 15
The disk should then be re-VALIDATEd in 1571 mode to ensure that side 1 has been properly done.
The following short program can be used to VALIDATE a disk which contains a BOOT sector:
10 OPEN 15,8,15,"V0" 20 PRINT#15,"B-A: 0 1 0" : REM RE-ALLOCATE BOOT SECTOR 30 CLOSE 15
The above two examples do not perform any error checking, which can be added if you desire.
DOS VALIDATE and BASIC COLLECT should not be used on GEOS disks. The GEOS "validate disk" option should be used to preserve the special structure of the GEOS disk. Block Commands The block commands are used to access a disk on a sector by sector basis. In Commodore DOS, this type of disk access is known as a "random file", meaning random access to sectors on the disk. The commands all take the following format: BLOCK-{action}:{target} where {action} represents the primary function of the command and {target} generally represents the block (i.e. track and sector) affected by the specified action. The command syntax is generally quite loose with numerous alternate formats as outlined below. The most frequently used formats are summarized in Table 2.
TABLE 2: The BLOCK commands .............................................................. Command Syntax .............................................................. BLOCK ALLOCATE "B-A: 0 {track#} {sector#}" BLOCK EXECUTE "B-E: {channel} 0 {track#} {sector#}" BLOCK FREE "B-F: 0 {track#} {sector#}" BLOCK POINTER "B-P: {channel} {byte#}" BLOCK READ "B-R: {channel} 0 {track#} {sector#}" BLOCK WRITE "B-W: {channel} 0 {track#} {sector#}" ..............................................................
In order to use the BLOCK commands, two OPEN statements must be used: one to open the disk command channel, and the other to open a direct access buffer in the disk drive. The direct access buffer is opened with a statement similar to the following example: 10 OPEN 1,8,3,"#" This specific example will request the drive to open any available buffer as logical file #1, channel #3 on device 8. When the "#" symbol is used as a filename in an open statement, it indicates to DOS to assign any available buffer to that channel. If a specific buffer is requested, the format of the command becomes: 10 OPEN 1,8,3,"#2" where #2 represents the number of the buffer (from 0 to 3) requested, 2 in this case. (Buffer #4 is reserved for BAM operations and cannot be accessed with an OPEN command but it can be read or written using memory read and write commands.) In the first case if a buffer is not available, or in the second case if the requested buffer is not available, a DOS error code 70, NO CHANNEL, error will occur.
The BLOCK commands are sent to the drive over the command channel, while data being read from or written to the block as a result of the BLOCK command are transferred via the buffer channel outlined above. Once the random access buffer has been opened, data can be either read or written to it randomly. For example, 20 PRINT#1,"DATA" will write the specified data to the buffer opened above. It should be noted that the PRINT# and GET# statements for transferrring data use the logical file number associated with the buffer (#1 in the case of this example) while the BLOCK commands sent via the command channel use the channel number (#3 in this case) to identify the buffer. The computer uses the logical file number for I/O operations while the disk drive uses the channel number. This can be confusing at times, so a good practice is to use the same number in the OPEN statement for both the logical file number and the channel number, such as: OPEN 3,8,3,"#" When using BLOCK operations, it is a good idea to INITIALIZE the disk with an "IO" command before you start, and certainly after changing disks. This ensures that the most recent BAM information has been logged in the drive. Block-Allocate The BLOCK-ALLOCATE command is used to "allocate" blocks in the BAM so that they will not be overwritten by other DOS operations at a future time. The acceptable formats of the command are: "BLOCK-ALLOCATE:0" {track#};{sector#} "B-A:0" {track#};{sector#} "B-A 0" {track#};{sector#} where {track#} and {sector#} represent the track and sector of the block to be allocated. In the above examples, these parameters can be either constants or variables. If they are constants, then the parameters can be included inside the quotation marks, without the semi-colon delimiters in the form of: "B-A 0 {track#} {sector#}" It should be noted that with all of the BLOCK commands, the parameters are specified as an ASCII string, not as CHR$(x). For example, a zero is specified as "0" (CHR$(49)), the ASCII code for the character 0, not as CHR$(0). The following example will allocate track 1, sector 0 (i.e. the BOOT sector for a C-128 disk), assuming that logical file #1 is the disk command channel: PRINT#1,"B-A 0 1 0" To prevent existing data from being overwritten, you should always attempt to allocate a block before writing to it. If a DOS error code 65, NO BLOCK, error occurs, then the requested block is already in use by another file. The track and sector numbers returned with the error message indicate the next higher block available. If the track number is zero, all remaining higher blocks are full. If the disk is not yet full, try to allocate a lower track and sector number. Because of this, it is often convenient to start trying to allocating blocks beginning with track 1.
Remember: Always re-initialize the disk with the "I0" command after a NO BLOCK error. Block-Execute BLOCK-EXECUTE is a little used command that allows you to load a specific sector into one of the RAM buffers of the 1571 and execute it as a program. This is similar to the BOOT"{filename}" command in BASIC 7.0, except that the program is being executed entirely within the disk drive and it can be a maximum of 256 bytes long.
The following syntaxes can be used: "BLOCK-EXECUTE:"{channel#}; 0; {track#};{sector#} "B-E:"{channel#}; 0; {track#};{sector#} "B-E" {channel#}; 0; {track#};{sector#} An example of the procedure is: 10 OPEN 1,8,3,"#" 20 OPEN 15,8,15,"B-E 3 0 2 4" 30 CLOSE 1:CLOSE 15 This example will load track 2, sector 4 into the buffer associated with logical file # 1 (channel #3) and execute it within the disk drive, assuming that it contains a valid machine language program. Note that you should be very careful using this procedure because you could easily crash you disk drive, or worse, corrupt your disk if you inadvertantly program the drive incorrectly. A safer and more versatile approach is to use "&" type disk utility files, described in more detail in Effective Use of Files. Block-Free BLOCK-FREE is the opposite of BLOCK-ALLOCATE: it is used to de-allocate blocks that been allocated, either by DOS or by BLOCK-ALLOCATE. Once a block has been freed, it can used by either DOS or for further BLOCK-ALLOCATE operations. The syntax of the command is similar to the BLOCK-ALLOCATE command: "BLOCK-FREE:"0; {track#};{sector#} "B-F:"0; {track#};{sector#} "B-F" 0; {track#};{sector#} Because there is no way to tell whether a block has been allocated by DOS (i.e. it is part of a regular file) or by the BLOCK-ALLOCATE command, BLOCK-FREE should be used with a good deal of caution. If you de-allocate a block which belongs to a file, that block may be used by DOS for another file. If that happens, the first file will be corrupted and will be composed of part of the original file plus part of the second. The sector linkage to the remainder of the first file will also be lost, making this file virtually impossible to recover. Block-Pointer BLOCK-POINTER (also referred to as the buffer-pointer) is used to position the byte counter in a random access buffer to a specific byte. The syntax is: "BLOCK-POINTER:" {channel#};{byte#} "B-P:" {channel#};{byte#} "B-P"; {channel#};{byte#} The B-P command is used mainly in conjunction with the BLOCK-READ and -WRITE commands (and their replacements UA and UB or U1 and U2) outlined below. It allows access to a given byte in any given track and sector on the disk. Block-Read The BLOCK-READ command is used to read a specified block into one of the random access buffers. The syntax for the command is: "BLOCK-READ:"{channel#}; 0; {track#};{sector#} "B-R:"{channel#}; 0; {track#};{sector#} "B-R" {channel#}; 0; {track#};{sector#}
The BLOCK-READ command is rarely used as it has been virtualy replaced by the more versatile "UA" or "U1" command described later. There is, however, an important difference in how the two commands operate. BLOCK-READ uses the first byte of the block to indicate the number of bytes in the block that are to be read. Unfortunately, this is not desirable for most random disk read operations. (The first byte of a block written as part of a regular file contains the track number of the next block in the file.) U1 (or UA) will read the entire block into the buffer. Block-Write BLOCK-WRITE is the opposite of BLOCK-READ: it is used to write the data in a buffer to a specific block on the disk. The syntax is similar to the BLOCK-READ command: "BLOCK-WRITE:"{channel#}; 0; {track#};{sector#} "B-W:"{channel#}; 0; {track#};{sector#} "B-W" {channel#}; 0; {track#};{sector#} Similar to BLOCK-READ, BLOCK-WRITE uses the first byte of the buffer to indicate the number of bytes to write to the block and has been replaced by the more versatile command "UB" or U2". The Memory Commands The memory commands are used to access the RAM and ROM of the 1571 directly. Unlike other DOS commands, the syntax of the memory commands is quite rigid. Only abbreviated forms are used. Paramters are specified as CHR$(x) values rather than as ASCII string characters "x". Memory-Execute The memory-execute command is the equivalent of the SYS command in BASIC. The syntax is: "M-E" CHR$({lo}); CHR$({hi}) where {lo} and {hi} are the low and high bytes, respectively of the address in 1571 ROM or RAM where you wish to begin execution. This command allows to you access specific routines in the disk drive's ROM or to activate other routines which you have programmed into one of the RAM buffers of the drive. The following example will turn on the motor of the drive for a few seconds then turn it off again: 10 OPEN 15,8,15,"M-E" CHR$(100)CHR$(135):REM MOTOR ON 20 FOR I=1 TO 100:NEXT I:REM TIME DELAY 30 PRINT#15,"M-E" CHR$(112)CHR$(135):REM MOTOR OFF 40 CLOSE 15 This example can be used in conjunction with disk drive head cleaner, for example. The time delay can be adjusted to suit your needs by increasing or decreasing the value "100" in line 20. The motor on routine is at $8764 and the motor off is at $8770 of the drive's ROM. Other useful ROM routines can be found in the 1571 memory map listing in Appendix D. Memory Read Memory-read is equivalent to BASIC's PEEK() command, except that it operates in the disk drive's memory and more than one byte can be PEEKed at once. Up to 256 bytes can be read using memory read. The syntax for the command is: "M-R" CHR$({lo}); CHR$({hi}); CHR$({#bytes}) where {lo} and {hi} represent the low and high bytes, respectively, of the address in the 1571's RAM or ROM to begin reading at. {#bytes} specifies the number of bytes to read, from 0 to 255. If 0 is specified, then 256 bytes are read. The memory bytes are transferred back to the computer via the command channel and can be read with GET#'s or the KERNAL equivalent. The following example will read any part of the 1571's RAM or ROM and print out the decimal equivalent of the bytes: 10 OPEN 15,8,15 20 INPUT"STARTING MEMORY ADDRESS";SA 30 INPUT"# OF BYTES TO READ";NB:IF NB>255 THEN NB=0 40 SH=INT(SA/256):SL=SA-256*SH 50 PRINT#15,"M-R"CHR$(SL);CHR$(SH);CHR$(NB) 60 IF NB=0 THEN NB=256 70 FOR I=1 TO NB 80 GET#1,A$:PRINT ASC(A$);:NEXT I 90 CLOSE 15 Memory-read can also be used for accessing MFM type disks without resorting to burst mode data transfer. For a description of this procedure, see Burst Mode and BASIC.
Memory-Write Memory-write is equivalent to BASIC's POKE command except that it operates in the disk drive's memory and more than one byte can be POKEd at once. Up to 34 bytes can be written at once using memory-write. This limitation is due to the length of the 1571's command input buffer (6 bytes for the command + 34 data bytes + 1 end of line marker = 41 bytes). The syntax for the command is: "M-W" CHR$({lo}); CHR$({hi}); CHR$({#bytes}); CHR$(data bytes) The number of data bytes included should match the number of bytes specified by the {#bytes} parameter. However, if more bytes are included, then the extra ones are ignored. If fewer bytes are included, then the extra memory spaces are left untouched. Memory-write is most frequently used for creating custom disk program routines in one of the drive's RAM buffers or for changing some of the various flags and pointers in the 1571's RAM. It can also be used for directly programming the interface chips and the MFM disk controller chip. In the first case, the buffer should be "protected" from use by DOS by opening the specified buffer as a direct access buffer as outlined with the block commands. Under these circumstances, data can also be transfered to the buffer by writing to the file associated with the buffer. Memory-write can also be used for writing to the DOS buffers for MFM disks. User Commands The user commands perform a variety of tasks as outlined below. Most of the User commands are similar to those of other Commodore disk drives. However, the extended "U0>" set is unique to the 1571 and its close cousins, the 1570 and the 128-D (A slightly different set is also present in the new 1581 3-1/2 inch disk drive). The user commands all jump through an indirect vector at $0075 of the drive's RAM. This can be changed to point to your own routines if desired. The user commands are summarized in Table 4.
TABLE 4: The USER commands .............................................................. Command Syntax .............................................................. Soft Reset "U0" Device number change "U0>"+CHR$({device#}) Head (side) select "U0>H0" (side 0) "U0>H1" (side 1) Mode select "U0>M0" (1541 emulation mode) "U0>M1" (1571 mode) Number of error retries "U0>R"+CHR$({#tries}) DOS Sector interleave "U0>S"+CHR$({interleave}) ROM selftest "U0>T" Read sector "U1: {channel#} 0 {track#} {sector#}" Write sector "U2: {channel#} 0 {track#} {sector#}" Jump table "U3"; "U4"; ...; "U8" Reset "U9" Power up reset "U:" Execute NMI "U;" ..............................................................
Soft Reset The "U0" command with no additional characters specified is a "soft reset" which restores the default jump vector table in the disk drive's memory. This table, which is used to access some of the more important internal functions of the disk operating system via indirect jumps, (similar to some of the KERNAL jump vectors in most Commodore computers) can be changed by the experienced programmer to point to custom routines. The "U0" command clears the changed vectors and restores the original ones. This is also a harmless command which can be used to test the speed or mode of operation of the drive (i.e. 1571 mode or 1541 mode). It does not cause the drive motor to come on and will be recognized by other Commodore disk drives as a legitimate command. If the C-128's fast serial flag at $0a1c is set after issuing this command (or any other disk command) to the drive, the drive is in 1571 mode. If the flag is cleared, the drive is either a 1541 or a 1571 in 1541 mode. Device Number Change The device number of the 1571 drive can be temporarily changed using the following command: "U0>"CHR$({device#}) where {device#} is the desired new device number. The device number specified by this command takes precedence over that set by the hardware switches. The new device number remains in effect until it is changed with another device number change command or the drive is reset by either a power off-power on sequence or with the "UJ" command discussed below. The acceptable device number range is 8 to 30, although generally only 8 to 11 are used for disk drives. This procedure is equivalent to, but considerably easier than, the memory-write command required for earlier Commodore disk drives. The device number parameters are stored in disk drive memory location $77 and $78 (dec. 119 and 120), similar to other Commodore drives. Head Select The 1571 is normally a double sided drive. That is, it uses both the top and bottom surfaces of the floppy disk for recording data. When operating in single sided 1541 mode, the 1571 drive can use either side 0 or side 1 of the disk. (A true 1541 disk will only use side 0.) The command for selecting side 0 is: "U0>H0" To select side 1, the command is: "U0>H1" This feature allows you to use the second side of single sided disks without converting them to "flippies" (i.e. taking them out and turning them over to use the second side). The procedure will not, however, allow you to read or write flippy disks because the rotation direction of the disk is reversed when it is flipped over. It is, therefore, garbled when read this way (similar to playing an audio tape or record backwards). The only data which can be picked up in either direction are the synch bytes. This is what sometimes causes the 1571 with the older ROM versions to hunt around for a few moments when initializing a flippy disk. It can detect synch bytes on side 1, but cannot read anything else so it tries again a few times before it can decide whether the disk is single or double sided. The head select command can also be used to "hide" secret data on the back side of a disk for security or copy protection purposes. Of course, such data cannot be accessed with a true single sided drive, such as the 1541, which has no way of reading the second side. After selecting a side, the drive will perform an "I0" type initialization and attempt to read a BAM sector. If the selected side has not been formatted, the drive will report a read error. Head select can also be used in 1571 mode to access the back side of a single sided disk. Mode Select The 1571 has two operating modes: double sided 1571 and single sided 1541. The 1571 mode is automatically selected when the drive is connected to a C-128. In this case, the fast serial bus is also enabled. The 1541 emulation mode can be specifically switched in during C-128 operation with the command: "U0>M0" This converts the drive into a single sided drive, using the side specified in a "U0>Hx" type command (or side 0 if no such command has been issued to the drive). In 1541 mode, the fast serial bus is disconnected and only the normal 1541 slow bus is available. Burst mode commands are not available in 1541 mode, although the "U0>" series is. The 1571 microprocessor clock speed is set to 1 MHz when in 1541 mode. If the 1571 drive is connected to any other computer, 1541 emulation mode is automatically enabled on startup. In order to access some of the advanced features of the 1571, the drive can be forced into 1571 mode with the following command: "U0>M1" It should be noted that only the C-128 will support the fast serial bus and burst mode data transfers. However, other computers can still access many of the burst mode features, such as the ability to read and write MFM disks, using memory reads and writes over the 1541 serial bus. In 1571 mode, the disk drive's microprocessor speed is set to 2 MHz, regardless of which serial bus speed is selected. The following example demonstrates the head select and mode select commands by formatting both the top and bottom sides of a disk as separate single sided disks. 10 OPEN 15,8,15,"U0>M0":REM SELECT 1541 MODE 20 PRINT#15,"U0>H0":REM SELECT SIDE 0 30 PRINT#15,"N0:FRONT SIDE,00":REM FORMAT SIDE 0 40 INPUT#15,A:REM WAIT TILL DONE 50 PRINT#15,"U0>H1":REM SELECT SIDE 1 60 PRINT#15,"N0:BACK SIDE,11":REM FORMAT SIDE 1 70 INPUT#15,A:REM WAIT TIL DONE 80 PRINT#15,"U0>H0": REM BACK TO SIDE 0 90 CLOSE 15 Now to test the effect, write a small program (or even the above test program) and save it to disk. Use the directory command to check that the file is indeed there. The disk name should also be displayed. Switch to side 1 with: OPEN 15,8,15,"U0>H1":CLOSE 15 Check the directory again. The file is not there! Save a second program on this side under a different name. Check the directory again to confirm it, then go back to side 0. Check the directory again, and the first program will be there but not the second. While this technique may seem to be a bit of a gimmick, it can be used to recover "unused" disk space on the flip side of disks which were formatted on single sided drives. Try Again Occassionaly while reading data from a disk, an error may occur. This is detected by the disk drive when it compares its calculated checksum for a sector with that recorded as part of the sector data. If the two don't agree, the drive will attempt to re-read the sector. Normally, three attempts will be made before a read error is reported. The number of re-tries can be set to any other value by the following command: "U0>R" CHR$({#}) where {#} represents the number of re-tries to be performed after an error. If the error was caused by a transient, such as electronic noise or a slightly dirty head, re-reading the sector a couple of times may often result in getting a matched checksum and therefore an acceptable read. If, however, there is a definite physical reason for the error, such as bad disk, misaligned head, or a deliberate error used by a disk copy protection scheme, the drive head may chatter around a few times while successive read attempts are being made. Although the 1571 does not have the physical head stops which cause the loud knocks in other Commodore disk drives, head bouncing can be a serious problem eventually leading to alignment problems. The head knocking can be eliminated by setting the number of re-tries to 1. However, in doing so, you also run the risk of getting more disk read errors because the drive will stop and report an error after the first attempt, even if it may have been able to extract the data on a second or third attempt. Conversely, if you are trying to extract data from an old or damaged disk, the number of re-tries can be temporarily increased to improve the chances of being able to recover at least some of the data. Sector Interleaves On a Commodore DOS disk, the sectors on a given track are arranged in numerical order. That is, sector 0 is next to sector 1, which is next to sector 2, etc. However, DOS does not fill the sectors in sequential order. The reason for this is to improve disk access speed. Consider the case of reading a file (LOADing a program, for example) which contains a number of linked sectors. We will start at say track 20, sector 0. This first sector is read. The data are then decoded from GCR to ASCII format and transmitted to the computer, one at a time. All the while that this is happening, the disk is rotating at a speed of about 300 RPM. By the time the drive is ready to read the next sector, the head might be over say sector 4 or 5. If the next sector in the file was sector 1, the drive must wait almost one complete revolution before it will come to sector 1 again. Even at 300 RPM, one revolution takes about 0.2 seconds. That is 200 milliseconds, a long time in computer terms. If the file contains 100 sectors, a full 20 seconds are wasted just waiting for the next sector. On the 1571, data sectors normally are filled with an interleave of 6. This means that the next sector to be filled in our example above would be track 20, sector 6. Since this sector has not been passed during the data processing and transmission time, it can be read in quick succession. The next sector in this sequence would be sector 12, then 18. Since track 20 only goes up to sector 18, it will then loop back to sector 5. The process then repeats: sector 5, sector 11, sector 17, etc. This of course, assumes that none of the sectors in the series has been allocated to other files. In a disk which contains numerous scratched files, many files will be disjointed or scattered throughout the disk. Sectors in the 1571 directory are normally filled with an interleave of 3, starting at track 18, sector 1. (This does not include the BAM sector at track 18 sector 0.) The sector interleave for the data area can be changed using the command: "U0>S" CHR$({#}) where {#} is a value from 0 to 255 which represents the interleave factor. A value of 0 has the same effect as a value of 1 (i.e. sequential filling of the sectors) while values greater than about 10 will result in very complicated sector interleave patterns and may produce unpredictable results. The sector interleave on the directory track is 3 and is not affected by this command. Changing the interleave factor can speed up file access in certain well defined circumstances. Setting it to a number other than 6 may improve the file read speed of some files, while slowing down access to other files. Table 5 is a summary of the times required for the LOAD speed of a 27 block test file which was SAVEd with different interleave factors. (To eliminate possible errors caused by head movement times, the file was erased and re-written to the same track of the disk with each new interleave factor. Times were taken using CIA #2 TOD clock.)
Table 5: Effect of Interleave Factor on File LOAD Times ............................................................ Interleave Value LOAD Time (27 Blocks, in seconds) ............................................................ 0 6.7 1 6.7 2 6.6 3 6.6 4 1.8 5 2.8 6 3.3 10 4.7 15 6.8 .............................................................
The file write speed is not normally affected by the sector interleave because the freshly written sector is immediately re-read to confirm that it has been properly written. This process requires the disk to travel a minimum of one complete revolution or 0.2 seconds for each sector written, in addition to the data transmission, data write and read operations. This is the major reason why disk write operations on the 1571 are much slower than disk reads and are, in fact, only marginally faster than the normally much slower 1541 drive. It should be noted that this command is totally different in function and application than the burst mode sector "interleave" command discussed in "Burst Mode Commands". ROM Check The 1571 DOS contains a command for testing itself based on a computed checksum for the machine language code contained in the ROM. The command is: "U0>T" If the test fails (that is, if the computed checksum from memory location $8002 to $FFFF does not match the value stored at ROM location $8000-8001), the green "drive busy" light will flash. This is the same self test that the drive goes through on power up, and is a simple diagnostic procedure that can be used to test the drive in the event that damage to the ROM's might be suspected after the drive has been turned on. Otherwise, it serves no real purpose. Sector Read As mentioned previously, the BLOCK-READ command does not work quite the way that one might expect in most cases. To overcome the problem, B-R has been replaced by the more versatile "sector-read" command. The sector-read command will read the entire specified sector into the indicated random access buffer. Similar to the BLOCK-READ command, a random access buffer must be opened along with the command channel prior to using the command. The command has the following syntax: "U1"{channel#};0;{track#};{sector#} Similar to the B-R command, the "0" character refers to the drive number. Since the 1571 is a single drive, this is always zero. {channel#} specifies the channel (secondary address) which was opened as the random buffer while {track#} and {sector#} specify the track and sector to read. For a single sided disk, {track#} can be in the range of 1 to 35, while for a double sided disk the range is 1 to 70. All parameters are specified as ASCII strings rather than CHR$(x) values. Note that similar to the Block commands, the channel number is used in this command, not the logical file number. Alternatively, the "U1" can be replaced by any of: "U1:", "UA", or "UA:". The remaining parameters are identical for each of the alternate formats. All disks are assumed to be single sided unless they have been initialized by the "I0" command and the double sided flag in the BAM sector (byte 2 of track 18, sector 0) has been set to a value of $80 (decimal 128). However, you can still attempt to read the second side of a "single sided" disk by using the head select command to switch to side 1 of the disk ("U0>H1") before issueing the sector-read command. The {sector#} parameter must be in the valid range for the specified track as given in Table 6.
Table 6: Sector Number Ranges ................................................ Track range Sector Numbers ................................................ side 0: 1 to 17 0 to 20 18 to 24 0 to 18 25 to 30 0 to 17 31 to 35 0 to 16 side 1: 36 to 52 0 to 20 53 to 59 0 to 18 60 to 65 0 to 17 66 to 70 0 to 16 ................................................. Attempting to access a track or sector outside of the valid range will result in a DOS error code 66, ILLEGAL TRACK AND SECTOR xx,yy error. The two numbers represented by xx and yy indicate the illegal track and sector, respectively, that you tried to access. Once the sector has been read into the disk buffer, its contents can be recovered using a series of GET#'s (or possibly INPUT#'s, if you realize the implications of doing so) or the KERNAL equivalent from the random buffer channel. The BLOCK-POINTER command can be used to position the byte pointer before using a GET#, if access to a specific byte is desired. The following example can be used to read the BAM sector of a disk in device 8 into the array BA(x): 10 DIM BA(255) 20 OPEN 15,8,15,"I0":OPEN 1,8,2,"#" 30 PRINT#15,"U1 2 0 18 0" 40 FOR I=0 TO 255:GET#1,A$ 50 BA(I)=ASC(A$):NEXT 60 CLOSE 1:CLOSE 15 Once in the array BA(x), you can easily determine which sectors of the disk are being used. Sector-Write Analogous to the Sector-Read command, a true Sector-Write command is also provided in the 1571 DOS. The syntax is: "U2" {channel#}; 0; {track#}; {sector#} The parameters are identical to those used for the Sector-Read command. Alternatively, the "U2" portion can be replaced by any one of: "U2:", "UB", or "UB:". The procedure for writing data to a specific track and sector on the disk can be summarized as follows: - OPEN the command channel - OPEN the random channel buffer - PRINT# data to the random channel buffer (use B-P first, if desired) - write sector to disk with U2 command - repeat for other sectors, if desired - CLOSE random buffer channel - CLOSE command channel The Sector-Write command is most often used for applications such as modifying the directory track, copying disks, and writing BOOT sectors on C-128 disks, editing the data stored directly on a disk. It is also frequently used by some commercial data base programs. User Jumps The U3 to U8 (or UC to UH) commands are used to access a mini custom jump table in the disk drive's RAM. They are similar to the memory-execute command discussed previously, but no address is specified because each command will begin execution at a specific address as listed in Table 7.
Table 7: User jump locations ....................................... Command Begin Execution At ....................................... "U3" or "UC" $0500 "U4" or "UD" $0503 "U5" or "UE" $0506 "U6" or "UF" $0509 "U7" or "UG" $050c "U8" or "UH" $050f ...................................... Note that the entry points are spaced 3 bytes apart. This allows you to create your own jump table. Each entry would be of the form: JMP $xxxx where $xxxx is the address of your machine code in RAM or a routine in the 1571's ROM. This instruction takes up three bytes, hence the spacing of the entry points. Of course, if you only needed one entry point, you could begin your code at $0500 directly without any further indirect jumps. However, in such a case you should not use U4 to U8 because they will more than likely cause the disk drive to crash. The code for the jump table can be written to the 1571's RAM by either the memory-write command, or by opening buffer #2 as a random access buffer and using the sector-read command to get a machine code program from a specific disk sector. More Resets Three general reset type commands, operating at different degrees, are provided in 1571 DOS. Each command is issued as is, with no additional parameters specified. The first command is a "soft" reset. This performs the function of cancelling any previously issued commands, except the device number change, mode select and head select, and return the drive to its standby state waiting for the next command. The syntax is: "U9" (or "UI") In 1541 mode, UI is also used to select the serial bus speed. "UI+" goes to 1541 bus speed (for a C-64, C-16, Plus/4) while "UI-" goes to the slightly faster 1540 bus speed (for a VIC-20 only). A "hard" reset can be performed using the "U:" or "UJ" command. This will cancel all previous commands, including those not affected by the "U9" command, and restore the drive to its initial power on state. This command will also activate the DOS error code 73, CBM DOS V3.0 1571, message allowing the computer to determine which type of drive is connected. The final reset command is unique to the 1571: it actuates the drive's interupt routine. Normally, this command will not appear to do anything, unless the interupt vector at $02a9 of the 1571's RAM has been changed to point to a custom routine or the job loop parameters have been altered through memory writes. The syntax for this command is: "U;" (or "UK") DOS Internals
The internal operation of the disk drive is complicated indeed. The command interpreter takes instructions from the computer over the serial bus, digests them into individual "jobs" and passes the job instructions to the controller for action. In this respect, the interpreter is much like a construction supervisor and the controller is like the workers who actually do the physical work.
For example, to LOAD a program, the interpreter receives the LOAD command from the computer along with a filename. The interpreter must then assign a buffer for the directory and tell the controller to read the first directory sector into this buffer. The interpreter then scans the buffer and looks for a match with the desired filename. If none is found, then the interpreter tells the controller to fetch the next directory sector, and so on, until either the filename is matched or the last directory sector has been read. If the filename has not been matched, the interpreter returns a "file not found" error message to the computer. If the file is found, the interpreter gets the starting sector number from the directory entry and proceeds to tell the controller the sequence of sectors to read. After each sector is read, the interpreter sends the data to the computer and determines the next sector in the chain. The process is repeated until the interpreter decides that there are no more sectors in the file.
The sequence of operations for a file SAVE is even more complex because it involves reading the BAM, deciding what sectors are free, writing then verifying the file sector by sector, updating the BAM and writing the new BAM as well as directory back to the disk.
The Job Queue For most applications, the high level (relatively speaking) DOS commands discussed in the previous sections of this document are more than adequate. However, for highly specialized applications, such as esoteric disk copy protection schemes, you can also program the drive directly to do things that it was not originally intended to do by using the "job queue". This is essentially the lowest level that you can operate in DOS, short of writing your own DOS code. (Since the 1571 is limited in the amount of available RAM, writing your own extensive DOS routines is not very practical.) It should be noted that although the job queue seems to be straight forward enough to use, it should be used with a good deal of caution. Its main weakness (and also its main strength, depending on your point of view) is that no error checking is done on the job paramters (track and sector values) before attempting to execute a command. The controller assumes that the command interpreter has caught any errors before setting up the job. The job queue is located in zero page memory of the 1571's RAM. It is merely a series of locations (call them pigeon holes if you like) which are used to send messages between the two parts of the operating system: the command interpreter and the controller. The associated memory locations for the job parameters are summarized in Table 8.
The command interpreter takes the high level command, such as to read data from a file, and breaks it down into a series of discrete jobs, each of which involves only one action (such as read, write, or verify) on one specific track/sector combination. The appropriate parameters are passed to the controller through the job queue. During the interupt cycle (about 100 times per second), the controller scans the job queue memory locations. If a value of $80 or greater is found in the queue, the controller performs the command using the corresponding track and sector data and data buffer locations and then places the result status code back into the queue and continues to scan the remainder of the queue.
For example, if memory location $00 contained a value of $80, location $06 had a value of 1 and location $07 had a 4, then the controller would read track 1, sector 4 into data buffer #0 which is at $300 to $3ff. The specific command codes and result status codes will be discussed in the next section of this document. Note that although space has been allocated in the job queue for six buffers, the last buffer (#5) is not actually present in RAM.
Table 8: Job Queue Memory Addresses ............................................... Job Queue Track Sector Data Buffer ............................................... $00 $06 $07 $0300-$03ff $01 $08 $09 $0400-$04ff $02 $0a $0b $0500-$05ff $03 $0c $0d $0600-$06ff $04 $0e $0f $0700-$07ff $05 $10 $11 (not used) ...............................................
Commands Table 9 summarizes the individual job queue commands. Note that all command codes have values of $80 (decimal 128) or greater.
Table 9: Job Queue Command Summary ............................................................... Job Code Action (hex) (dec) ............................................................... $80 128 read sector $88 136 read another sector on same track $90 144 write sector $a0 160 verify sector $b0 178 look for sector header $c0 192 home to track 1 $d0 208 execute program in buffer $e0 224 combine program in job loop $f0 240 format disk ................................................................
After each command has executed, it will return one of the error codes listed in Table 10. Note that all error codes have values of less than $10 (decimal 16).
Table 10: Job Return and Error Codes ......................................................... Return/Error Code Meaning ......................................................... $0 or 1 no error $2 sector header not found $3 sync mark not found $4 data block not found $5 data block checksum error $6 formatting error $7 verify error $8 write protect on during write $9 header checksum error $a (10) data block too long $b (11) disk ID mismatch $c (12) (not currently used) $d (13) index hole not found $e (14) burst mode syntax error $f (15) drive not ready/no disk .........................................................
As mentioned previously, the controller does not check to see if track and sector locations contain "valid" numbers before it tries to execute the command in the job queue. This allows you the complete flexibility to read and write beyond the normal 1 to 35 track per side range and outside of the normal sector number range for a given track. This is most useful for "hiding" data on the disk for use in copy protection schemes because such data cannot be read by most disk copiers, unless they are specifically designed for it. Even then, the copier must know the exact paramters that you have specified before it can find the hidden sectors.
Programming the Job Loop Because the job queue is scanned very rapidly, you must set up the tracks and sector parameters (and place the data in the buffer if doing a write operation) before putting the job code in the job queue.
Batch File Programming on the C-64 and C-128 SYS 65478 seems like an innocent and well documented routine. Opening a file as an input channel. What could be simpler? While this may be its normal use from within a program, the KERNAL CHKIN routine can be put to some much less obvious uses in immediate mode. One of the more interesting of these enables a Commodore 64 or 128 (and other CBM machines with a similar KERNAL structure) to execute a series of commands contained in a disk file, similar to an MS-DOS batch file or a CP/M submit file. The CHKIN routine resets the input device flag (normally 0 to indicate the keyboard) at zero page location hex $99 (on the VIC-20, C-64 and C-128), or $98 (on the Plus/4 and C-16) to a value coresponding to the device from which normal input would be received. The main BASIC immediate mode input loop checks this location before trying to fetch an input byte. If the value is 0, a normal entry occurs by fetching a byte from the keyboard buffer. However, if the value is 8, for example, the fetch routine will attempt to read a byte from serial port device 8 (usually a disk drive). If device 8 has an open file capable of giving output, a byte is read from this file and placed in BASIC's input buffer, just as if it had been entered from the keyboard. If a carriage return is encountered, the commands contained in the input buffer are executed, assuming there is no line number at the beginning. Thus you can execute commands contained in a disk file. It should be noted that even with normal input redirected to respond to an external device, the keyboard is still scanned by the IRQ interupt handling routines and the results placed in the keyboard buffer only, up to the maximum number of characters allowed for the buffer. BASIC's input editor will not see any of these characters until control has been restored to the keyboard. To avoid confusion, I call this type of SEQ file a sequential disk command (SDC) file. The procedure for using an SDC is not the same thing as LOADing and RUNnig a disk PRG program file. There are several major differences: A regular PRG type file contains a series of tokenized BASIC lines (complete with link addresses and line numbers) or machine language op codes (see the section in chapter 7 on PRG files for a full description). The sequential disk command file, on the other hand, contains a series of immediate mode commands written out in plain English, just as you would type them in from the keyboard. In addition, a PRG file must be resident in RAM for execution. In most 8 bit computer operating systems (Commodore KERNALs included), only one program can be in memory for execution at a given time (assuming that you have not artificially partitioned the memory into separate work spaces.) A SDC type program is not memory resident! It resides entirely in a disk file and is called up and executed statement by statement, without affecting any program(s) stored in the computer's main RAM unless you deliberately want to. However, since it resides on disk, a SDC program may take longer to execute than a RAM resident program because of Commodore's notoriuosly slow disk access speeds. SDC's are useful as utility and easy to use reference data files since they can be called up and executed without fear of erasing main computer memory. This allows a programmer to interupt work, call up and consult an on-line data table, for example, and then resume the task at hand, all with relative ease and speed. SDC's can also be used for storing customized keyboard macros. (For those unfamiliar with the concept, a keyboard macro is an oftem lengthy and/or complicated series of frequently used keystrokes/commands that can be automatically invoked by using a shorter, easier to remember key sequence.) These can be very useful for setting up sprites, sound and graphics on older CBM machines such as the C-64 which lack hich level commands for doing so. (Can you honestly remember the POKE sequence to play the first few bars of HAPPY BIRTHDAY on the C-64?) Setting up an SDC A sequential disk command file is very easy to create. You use your favorite word processing program to create a SEQ file containing a series of immediate mode BASIC commands, just as you would type them in to execute from the keyboard. BASIC keywords (such as PRINT) can be entered in either their long or abbreviated forms. Of course, the same limits on line length as for normal programming apply to the lines in your SDC file (e.g. 80 characters for the C-64, 160 characters for the C-128, etc). This is a restriction imposed by the size of the input buffer on the computer. Any immediate mode command can be used except for disk access commands. You should not use OPEN, LOAD, SAVE, DIRECTORY, etc. because these commands will reset the input device to the default keyboard value after they have executed, thus cutting off the rest of your command file. A DIRECTORY can be used as the last item in an SDC because it will return control to the keyboard upon excution which is desirable in this case. The program mode only commands, such as INPUT, GET, GOTO, GOSUB, etc., cannot be used in SDC's because an SDC is executed in immediate mode not under program control. Line numbers should not be used in SDC's (except on the C-128 where they provide a different effect as outlined later). In order to be properly interpreted when they are read in, the BASIC keywords must be typed in a style that allows them to be saved as un-shifted PETSCII characters in a disk file. (This is the way that you would normally enter them from the keyboard.) With most word processors, this means that they are typed in lower case only and the file is saved as a SEQ file. Word processors which use PRG type screen code files only should not be used for creating SDC's. Making a graceful exit from an SDC back to keyboard input can be somewhat tricky. The easiest way is to include a statement at the end of your SDC which POKEs a 0 value back into the input device flag location described earlier. Simply CLOSEing the disk file from within the SDC or using the BASIC commands END or STOP, will not return control to the keyboard because they do not automatically reset the input device flag. Another way to exit is to include a garbage statement or deliberate syntax error as the last line. Upon reaching such an error, the SDC will crash and control will be returned to the keyboard. The least elegant way to exit is by the familiar <RUN-STOP>/<RESTORE> key combination. Crude but effective. Once the SDC has been entered, it should be saved as a SEQ disk file with an appropriate name. Executing the SDC Now comes the fun part. Executing the SDC is really quite simple. All that is required is to open the disk file in immediate mode with a statement such as: OPEN 1,8,8,"filename" Second, you must activate the CHKIN routine. On the C-128, with the above OPEN statement you would use: SYS 65478,0,1 With a C-64 or similar type machine, a double statement is required: POKE 781,1 followed by: SYS 65478 This latter technique is also applicable to other Commodore machines which lack BASIC 7.0's enhanced SYS command. Of course, the 781 should be replaced by the appropriate memory location for the X shadow register on other machines. The commands in the SDC will then be executed, one line at a time until control is returned to the keyboard by one of the methods previously outlined. You will note that the actual commands are not printed to the screen before they are executed, but the "READY." message is printed after each line has been executed. Once the SDC has finished executing, the disk file should be closed with a DCLOSE or CLOSExx as applicable for your machine. Example SDC's The following are some short examples of SDC's. While it may appear that some of the statements are repetitive, it should be remembered that they were created with a word processor. (If your word processor does not have cut, paste and copy commands, then perhaps it is time to splurge for a new one!!) The first example is for the C-64 or C-128 (in 40 or 80 column mode) which prints out a simple calendar for the month of January 1987. You will note that most of the lines begin with the sequence "PRINT UP$". "UP$" is defined in the first line as three cursor ups. This allows you to get around the nasty habit of immediate mode BASIC of printing a few carriage returns and a READY after each line it executes. UP$ is included to properly format the screen display in this case. Note also that special control characters are given as their CHR$() values. This is the only way to enter them with a word processor. UP$=CHR$(145)+CHR$(145)+CHR$(145):POKE53280,6:POKE53281,6 PRINT CHR$(147)CHR$(5); PRINT UP$" JANUARY 1997" PRINT UP$" SUN MON TUES WED THUR FRI SAT" PRINT UP$"+----+----+----+----+----+----+----+" PRINT UP$" 1 2 3 4 " PRINT UP$"+----+----+----+----+----+----+----+" PRINT UP$" 5 6 7 8 9 10 11" PRINT UP$"+----+----+----+----+----+----+----+" PRINT UP$" 12 13 14 15 16 17 18" PRINT UP$"+----+----+----+----+----+----+----+" PRINT UP$" 19 20 21 22 23 24 25" PRINT UP$"+----+----+----+----+----+----+----+" PRINT UP$" 26 27 28 29 30 31 " PRINT UP$"+----+----+----+----+----+----+----+" POKE 153,0 This next example will print out a handy hex-dec conversion chart on the 80 column screen of the C-128. It is similar in nature to the above example, but works in C-128 FAST mode. The 80 column screen is required because of the width of the table.
UP$=CHR$(145)+CHR$(145)+CHR$(145):PRINT CHR$(147)CHR$(5);:FAST PRINT UP$" HEX-DEC CONVERTER" PRINT UP$" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" PRINT UP$"+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+" PRINT UP$" 00 : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" PRINT UP$" 10 : 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" PRINT UP$" 20 : 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" PRINT UP$" 30 : 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63" PRINT UP$" 40 : 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79" PRINT UP$" 50 : 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95" PRINT UP$" 60 : 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111" PRINT UP$" 70 :112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127" PRINT UP$" 80 :128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143" PRINT UP$" 90 :144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159" PRINT UP$" a0 :160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175" PRINT UP$" b0 :176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191" PRINT UP$" c0 :192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207" PRINT UP$" d0 :208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223" PRINT UP$" e0 :224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239" PRINT UP$" f0 :240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255" PRINT UP$:SLOW:POKE 153,0
The final example is interesting for several reasons. It is essentially a self contained data file which can read and display itself on the screen automatically! The WAIT and POKE values of 208 in the lines are set up for a C-128. For a C-64, change all of the 208's to 198's. This is the location of the keyboard buffer flag which indicates the number of characters in the buffer. The file will display one entry at a time and wait for you to press a key before displaying the next entry. (Remember, the keyboard scan and buffer filling still occurs when a SDC is being executed even if its input is ignored by the BASIC input routine, hence the need to clear the buffer by POKEing a 0 to it before reading the next entry.) To stop the display before it reaches the end, a <RUN-STOP>/<RESTORE> should be used. This last example demonstrates that although you cannot read the keyboard directly with GET's, INPUT's, etc, you can still obtain data from the keyboard via direct PEEK's and POKE's to the keyboard buffer areas. UP$=CHR$(145)+CHR$(145):PRINT CHR$(147)CHR$(5); PRINT UP$" MINI PHONE FILE ":PRINT PRINT UP$" NAME: JOHN DOE PHONE:123-456-7890" WAIT 208,1:POKE 208,0 PRINT UP$" NAME: JANE SMITH PHONE:123-456-7890" WAIT 208,1:POKE 208,0 PRINT UP$" NAME: FRED BLACK PHONE:123-456-7890" WAIT 208,1:POKE 208,0 PRINT UP$" NAME: JACK & JILL PHONE:123-456-7890" WAIT 208,1:POKE 208,0 (ETC. more names and addresses) POKE 153,0
Effective Use of Files The 1571 disk operating system recognizes a number of different types of files: sequential, user, program and relative. Each file type is denoted in the disk directory listing by the corresponding 3 character code listed after the filename: SEQ, USR, PRG, and REL. Each file type is normally reserved for a different primary use. However, this does not mean that a given file type cannot be used for a different job. This chapter will outline how to create and access each of the file types with BASIC. Examples will be given of both standard and non-standard uses for these files. Other file types, such as VLIR (very large indexed relative used by the GEOS operating system), "random files" and CP/M files are not discussed here. (The random file is not really a file in the true sense, but a loose collection of user associated sectors on the disk.)
SEQuential Files: Sequential files are primarily used for the storage and retrieval of data used by other programs. The data can be numeric or text strings, or both in any combination. As its name might suggest, the data in a SEQ file are read or written in a pre-determined sequence. In order to get to an item in the middle of a SEQ file, you must first correctly process (that is, either read or write) everything in the file preceding that point. Sequential file access is a three step process: 1. open the file 2. read OR write the data 3. close the file After each step, it is a good idea to check the disk drive error status through either the command channel (BASIC 2.0) or the reserved disk status variables DS and DS$ (BASIC 3.5 or 7.0). This is especially important after write operations and will keep you informed about potentially disasterous situations such as writing to the wrong disk, disk full, bad or unformatted disks, hardware malfunctions, etc. OPENing the File A SEQ file can only be accessed through an appropriate OPEN type statement. (You cannot use LOAD or SAVE type commands for a SEQ file.) In addition, the file can be opened for either reading OR writing, but not both simultaneously. (If you wish to include simultaneous input and output capability for your data files, then they should be initially created as relative files. These are described later in this document.) Several options are available for opening a SEQ disk file, depending on what action is to be taken with the file after it has been opened. The following are some typical examples for opening a file in read mode on device 8, drive 0: 10 OPEN 8,8,8,"PEACHES,S,R" 10 DOPEN#1,"PLUM,S" 10 OPEN 1,8,4,"0:ACCOUNTS,S" 10 DOPEN#2,"PAYMENT*" In each of the above examples, the ",S" and ",R" are optional. BASIC will default to the read option if "R" is not specified. In the last example, since "S" is not specified, BASIC will open the first file which matches the specified file name pattern, regardless of its type. If "S" is explicitly specified, as in the other examples, and the file listed in the directory under the name that you requested is not a SEQ file, then a DOS error code 64, FILE TYPE MISMATCH, error will occur along with a BASIC "FILE NOT FOUND" error. The DOPEN command is not available in BASIC 2.0. Note also that all of the examples are preceded by a line number. This is because normally you can read a file in program mode only. BASIC will not let you use INPUT# or GET# in immediate mode. If you are using BASIC 2.0, the disk command channel should also be opened at this time to allow reading of the disk error status. The typical command used is: 20 OPEN 15,8,15 The disk error status is then read using a standard INPUT# statement: 30 INPUT#15,E1$,E2$,E3$,E4$ E1$ will represent the disk error code number, E2$ the description of the error and, E3$ and E4$ the track and sector where the error occured (if applicable). In BASIC 3.5 or 7.0, you can check the error status with DS (disk error code) or DS$ (error code plus description). The BASIC 2.0 DOS wedge can also be used for reading the error status without opening the command channel. The following examples illustrate opening a SEQ file for writing:
OPEN 8,8,8,"PEACHES,S,W" DOPEN#1,"PLUM,S",W OPEN 1,8,4,"0:ACCOUNTS,S,W" DOPEN#2,"PAYMENT",W Files can be opened for writing in either program or immediate mode. With the BASIC 2.0 version, both "S" and "W" must be specified to open a sequential file for writing. If "S" is omitted from the OPEN or DOPEN command, BASIC will automatically default to a SEQ file. Note that wildcards and pattern matching are not allowed in filenames opened for writing. In each of the above examples, a new file bearing the indicated filename will be created for subsequent writing. If a file of the same name already exists in the disk directory (regardless of its type), a DOS error code 63, FILE EXISTS, error will be generated. However, the file number specified in the OPEN statement is still active to BASIC. You must first CLOSE this file before attempting to re-open a new file with the same number under a different name. If you attempt to write to the original file, the computer and/or disk drive may lock-up. The 1571 also allows SEQ files to be opened for appending data to the end of the existing file. The following are examples of this procedure: OPEN 8,8,8,"PEACHES,S,A" APPEND#1,"PLUM,S" OPEN 1,8,4,"0:ACCOU*,S,A" Note that in the third example a wild card character is used. Wild cards are allowed with the append option, but the same restrictions outlined above for opening a file in read mode apply. The fourth mode of the OPEN command, M for MODIFY, is designed for recovering information from improperly closed or "SPLAT" files. These are files which are indicated in the directory with an asterisk (*) next to the file type. The MODIFY mode of 1571 SEQ file handling allows you to read and recover the data in such a file for processing and re-writing to another file. The following are examples of such an open statement:
10 OPEN 8,8,8,"SPLAT FILE,S,M" 10 OPEN 1,8,3,"SPLAT*,S,M" In each case, the file type ("S") can be replaced by any other valid file type (U, P, or R). Once a file has been opened in the modify mode, it can be read just like any other data file. Care should be taken to check the data in the file to ensure that the bytes transferred still represent desired data. Because a SPLAT file will not have a valid end of file marker (EOF), it may contain some garbage bytes at the end. These should be discarded before writing the new file. More on SPLAT files can be found in section 7.1.5 of this chapter. Reading In BASIC, a disk file can only be read in program mode. Two commands can be used: INPUT# and GET#. Both commands are available in all versions of Commodore BASIC, however, the effect of the INPUT# command may vary slightly depending on the version of BASIC due to the length of BASIC's input buffer. On the C-128, the input buffer is 160 characters long. This is the same buffer which is used for input from the keyboard. (On the C-64 and VIC-20 it is 88 characters long.) Just like keyboard entry, the INPUT# command is limited to reading a maximum of that many characters at a time. If a character string in the disk file is longer, then you must either read the string with a series of GET#'s or insert one or more carriage returns into the string to divide it up into smaller chunks. The following are examples of INPUT# and GET#: 10 INPUT#3,A$,B$,C$,D 20 GET#3,A$,B$ The first example will read three strings and one numeric value from logical file 3. The second example will read the next two characters from logical file 3. Although both INPUT# and GET# work with either numeric or string arguments, it is often convenient to treat all disk data in string format, then convert to numeric form with the VAL(string) function where required. This is because BASIC will accept anything from disk as a valid string input but will produce a BASIC error code 24, FILE DATA, error if an attempt is made to read string data into a numeric variable. This is similar to the TYPE MISMATCH error generated when string data is entered from the keyboard in response to a prompt for numeric input. The following short example will read a specified SEQ file and print its contents to the screen: 10 INPUT"FILENAME TO READ";F$ 20 OPEN 8,8,8,F$ 30 GET#8,A$:IF ST THEN 50 40 PRINT A$;:GOTO 30 50 CLOSE 8:END The above example will work with all versions of Commodore BASIC. It will also work with all file types, although generally only SEQ and REL files will contain data that will make any sense when read and printed in this manner. (Remember: what you GET# is what you see!) In order for INPUT# to work correctly, the data in the file must be properly separated or "delimited". Failure to do so may result in an attempt to read more characters than the input buffer can handle, thus causing a BASIC error code 23, STRING TOO LONG error, or an attempt to read too many data items resulting in the equivalent to the keyboard input warning message EXTRA IGNORED and the loss of these data items. Even if these error conditions do not occur, the data from the file may be corrupted. Both numeric and string data will combine into longer non-usable entries if not properly delimited. Proper data delimitation is discussed in the next subsection on writing files. Delimitation is not required for reading a file with GET# commands since this method returns one character at a time, regardless of its value. The variable list included with the INPUT# statements must be properly formulated to match how the data are stored in the disk file. If sufficient data are not available in the file to fill all of the requested variables, INPUT# will essentially wait forever expecting to read more data from a file which has no more to give. GET# will return a null character for extra requests, but will not cause lock up. In addition, multiple data items which have been delimited in the disk file by commas must be read with a single INPUT# statement.
Writing In BASIC, writing to SEQ files is done using the PRINT# (or PRINT#, USING;) command. A simple example would be: PRINT#1,A$ More frequently, a number of items will be written to the same data file. In this case, the data must be properly separated or "delimited" to allow the data to be read again correctly. Commodore BASIC and DOS recognize a number of delimiters. The simplest, and perhaps most versatile, is the carriage return (ASCII character code 13). A carriage return can be placed in a file by two methods: explicitly or automatically. The explicit method has the following format: PRINT#1,A$;CHR$(13);B$;CHR$(13);C$; ... etc The carriage return can also be asssigned to a string variable: CR$=CHR$(13) PRINT#1,A$;CR$;B$;CR$;C$; ... etc The second method of inserting carriage returns into a file takes advantage of the automatic carriage return added by BASIC to the end of a PRINT or PRINT# statement with no continuation character (, or ;) specified at the end of the variable list. For example:
PRINT#1,A$ PRINT#1,B$ PRINT#1,C$ will automatically place a carriage return after each item. The second delimiter recognized by Commodore BASIC and DOS is the comma ",". This must be added to the file explicitly in a form such as: CO$="," PRINT#1,A$;CO$;B$;CO$; ... etc The third delimiter is the colon ":". This acts in its own peculiar manner: everything after a colon, up to a carriage return, will be skipped over. Data formatted in this manner can only be read with a series of GET#'s. Although all three of the above delimiters can be used to separate data, there is a significant difference in the way that the carriage return acts compared to the comma. Similar to the INPUT statement for keyboard data input, commas are used to handle multiple data items which will always be read by a single INPUT# statement, while carriage returns will work for both single and multiple INPUT#'s in any combination. The fourth type of delimitation involves the use of quotation marks around strings. The quotes can be used to delimit strings which contain commas or colons. If, for example, you wanted to write the following to a disk file as a single string: LAST NAME, FIRST NAME : GOLF HANDICAP it must be enclosed in quotes or else it will be interpretted as three different strings. Quotes must be added explicitly to a data file: PRINT#1,CHR$(34)+"LAST NAME, FIRST NAME : GOLF HANDICAP" +CHR$(34) In order to insert quotes, you must use the expression CHR$(34) or assign it to a string variable (such as Q$=CHR$(34)). You cannot insert a quote by simply printing it PRINT#1," because BASIC uses the quote mark to delimit string constants for printing. Alternatively, the trailing quote can be replaced by either an explicit or automatic carriage return. Unfortunately, there is no way to use INPUT# to read a string which contains a quote mark from a disk file. In this case, you must use a GET#. The PRINT# USING command can be used to write preformatted output to a disk file. This may seem to be of less use than sending such formatted output to the screen or printer, unless the file is intended to be input for a word processor document or another program which expects data in a rigid format. However, PRINT# USING can be used to delimit data. For example, the following statement will output properly delimited numerical data to logical disk file 1: PRINT#1,USING "####.##,";A,B,C Note the comma at the end of the definition string. Each item in the list will be printed separated by a comma. Alternatively, a carriage return can also be used in the following manner: B$="####.##"+CHR$(13):PRINT#1,USING (B$);A,B,C The use of "=" or ">" in the format definition string will allow text to be centered or right justified in the character field. Disk files can also be written with the aid of the CMD command which re-directs the normal screen output to an OPEN file. For example, the most common way to create a disk file listing of a BASIC program or assembly language source code (perhaps to include it in a document or to upload it to an electronic bulletin board system (BBS)) is: OPEN 8,8,8,"LISTING,S,W" CMD 8 LIST PRINT#8:CLOSE 8 This is generally done in immediate mode, although the same technique can be used in program mode to re-direct a program output from the screen to a disk file or printer. In this latter case, regular PRINT statements will send output to the open file. The CHAR statement used in BASIC 7.0 for placing text at certain locations on a text or graphics screen will always send output to the screen. CHAR cannot be used for writing to a disk file. Closing the File A SEQ file is closed using a standard CLOSE {file#} or DCLOSE type of statement. It is especially important to properly close a file which has been written to. Failure to do so will result in a SPLAT file which may lead to lost or corrupted data, not only in the guilty file but in other files on the disk as well. A simple PRINT# statement with not variable list is often used before closing the file. This ensures that the last of the disk buffer is written to the disk before the file is closed. An example would be: PRINT#1 CLOSE 1 The PRINT#1 statement in the above example is not applicable if the file was opened in read or modify mode. Recovering Data From SPLAT Files "SPLAT" files are created when a file is not properly closed after being written to. This is most frequently caused by sloppy file management practices or improper error recovery. The SPLAT file can be easily identified in the disk directory by an asterisk (*) next to the file type and 0 blocks showing for the file length. Even though 0 blocks are indicated in the directory entry, the file does contain data and has been allocated disk space. Usually this can be seen in a reduced value for the "blocks free" parameter in the directory listing.
The correct method for recovering data from, then deleting, a SPLAT file is outlined below: 1. OPEN the SPLAT file in M for modify mode. 2. Read the data in the file with GET#'s and store it somewhere. 3. OPEN a new write file. 4. Write data to new file. 5. CLOSE the new file. 6. Validate (COLLECT) the disk to erase the SPLAT file. A SPLAT file can also be opened as a standard read file. However, a DOS error code 60, WRITE FILE OPEN, error will occur and the recovered data may not be as reliable as that read in the M mode. The following short program can be used for SPLAT file data recovery: 10 INPUT "SPLAT FILENAME";OF$ 20 INPUT "FILE TYPE (S, P, OR U)";FT$ 30 INPUT "NEW FILENAME";NF$ 40 OD=8 : ND=8 : REM DEVICE NUMBERS 50 OPEN 1,OD,5,OF$+","+OT$+",M" : REM OPEN IN MODIFY MODE 60 OPEN 2,ND,6,NF$+","+OT$+",W" 70 GET#1,A$ : PRINT A$; : PRINT#2,A$; 80 GET B$ : IF B$ THEN 100 90 GOTO 70 100 PRINT#2 : CLOSE 2 : CLOSE 1
This program will prompt you to enter the splat filename and type (S, P, or U), and the new filename. The new file need not be on the same device as the old (you can change the device numbers in line 40 if you wish). The splat file will be read character by character and printed to both the screen and the new file. When you see something on the screen that looks like it should not be part of the file (i.e. random garbage from the end of the splat file) you have come to the end of the usable data. (This method works best with files that contain alphanumeric text only.) Press any key to abort the file reading and the new file will be properly closed. At this point, the disk with the splat file should be VALIDATEd to remove the splat file.
USeR Files: User files have no pre-defined structure: they can be totally defined by the user to suit virtually any purpose. They are sometimes used by commercial programs for data files because they can be easily distinguished in the disk directory by checking the file type. In most respects, they can be handled identically to SEQ files by merely changing the file type designator from S to U.
USR files are also used by GEOS as the basis for VLIR files for data and application program storage.
DOS Utility "&" Files USR files can also be used for some very special purposes such as automatic programming of the disk drive. In Commodore DOS these are known as ampersand or "&" files, due to the command used to execute them.
The USR program is "loaded" into the drive's RAM and then executed with a command such as: OPEN 15,8,15,"&0:{filename}" Note that with the 1571, the "&0:" is not actually part of the filename. (This i s different from earlier drives, such as the 1541, which required the "&0:" to be part of the filename).
To create the file, a standard OPEN type statement is used, followed by PRINT#'ing the data to it, just like a SEQ file.
For example the following forms can be used in BASIC 2.0 and 3.5/7.0 respectively. OPEN 8,8,8,"0:DOSPRG,U,W" DOPEN#1,"DOSPRG,U",W Note that the file type specifier "U" must be explicitly stated, otherwise a SEQ file would result.
The format of a DOS "&0:" file must be strictly adhered to as outlined below. The file consists of a header, which tells DOS where to put the file in the drive's memory; the machine language program, in standard 6502 machine code; followed by a checksum byte. The specific byte usage is summarized in Table 7-1.
Table 7-1: DOS "&" File Structure ............................................................. Byte Usage .............................................................. 0,1 Track and sector link (for long files) (normally handled by DOS automatically)
2,3 Load address (lo byte/hi byte). This is also the start of execution address. To avoid crashing, this should be one of the disk buffers.
4 Number of bytes to follow (except for checksum).
5-end Machine code bytes.
end+1 Checksum = sum of lo and high bytes of bytes 2 to end added together. ................................................................
The length refers to the length of the machine language and can be up to 250 bytes long. If your program is longer, it must be broken up into 250 byte segments, each with its own length, load address and checksum bytes. This leads to the interesting observation that a DOS "&" file does not need to occupy contiguous areas of RAM. For example, the address bytes for the first sector might specify $400 as the starting point, while a second sector in the file may jump to $700.
Since the 1571 is controlled by a 6502 processor, the code can be developed on any standard C-64/VIC-20/C-128 etc. type of assembler or monitor. Control is returned to DOS from your program by a simple RTS at the end. The file can be written as a byte string or you can use a sector editor. If using a sector editor, you must properly select the track and sector link bytes yourself.
The check sum consists of the sum of bytes 2 through to the end of the machine language. (The address bytes, and length byte are included in the sum.) The low and high bytes of this sum are added together to form the checksum byte which is included at the end of the file.
It should be noted that if you wish your program to remain active in the drive after it stops executing, you will have to allocate the buffer into which it was placed by opening it as a direct access buffer. If you do not do this, DOS will use the buffer for its own purposes and overwrite your program possibly causing a spectacular crash. The following simple example will turn off the re-try after disk errors to prevent head chatter on heavily copy protected disks:
10 OPEN 1,8,1,"0:KNOCK-IT-OFF,U,W" 20 FOR Z=0 TO 8:READ X:PRINT#1,CHR$(X);:NEXT:CLOSE 1 30 DATA 0, 3, 5, 169, 197, 133, 106, 96, 199
The machine code represented by the data bytes 169 to 96 is: 169 197 LDA #$c5 133 106 STA $6a 96 RTS
The file will load and execute at $300. It is not necessary to protect this buffer, because the program is not required to be memory resident.
The second example will turn off the "verify after write" of the 1571 for job buffer #0. This will greatly speed up writes to the disk from this buffer, but it should be used with extreme caution because it changes some very important vectors in the drive's operating system and may cause a crash if not used properly. It is shown here as an example only. Be aware of what you are doing and the possible consequences before trying to use it yourself.
1 REM CREATE & FILE 10 OPEN 8,8,8,"V-OFF,U,W" 20 FOR I=0 TO 32 : READ X : PRINT#8,CHR$(X); : NEXT 30 PRINT#8 : CLOSE 8 40 DATA 0, 6, 28 : REM FILE HEADER 50 DATA 120, 169, 13, 141, 169, 2, 169, 6 : REM CODE 60 DATA 141, 170, 2, 88, 96, 72, 165, 0 70 DATA 201, 160, 208, 4, 69, 0, 133, 0 80 DATA 104, 76, 222, 157 90 DATA 86, 0, 0 : REM CHECKSUM AND FILLER
1 REM TO USE THE FILE 10 OPEN 9,8,9,"#3" : REM RESERVE BUFFER #3 20 OPEN 15,8,15,"&0:V-OFF" : REM ACTIVATE CODE 30 (. . . the rest of your program) . . . 60000 PRINT#15,"UJ" : REM RESET DRIVE BEFORE GOING 60001 CLOSE 9 : CLOSE 15
Note that you should not close either the command channel or the buffer channel until just before exiting from your program. The above routine should be used in program mode only, for special cases, such as a disk copying program, which operate under well defined conditions. The program can cause considerable damage to a disk if not used properly.
The source code for the machine language portion is as follows:
.ORG $300 ; NOT RELOCATABLE SEI ; DISABLE INTERUPTS LDA #<VEROFF ; LOW BYTE OF NEW INTERUPT VECTOR STA $02A9 ; SAVE IT LDA #>VEROFF ; REPEAT FOR HIGH BYTE STA $02AA CLI ; RESTORE INTERUPTS RTS ; AND EXIT VEROFF =* ; NEW INTERUPT HANDLER PHA ; SAVE .A LDA $00 ; GET JOB CODE FOR BUFFER #0 ; CAN USE OTHER BUFFERS ALSO CMP #$A0 ; CHECK FOR VERIFY BNE CONTINUE ; NO? THEN JUMP TO OLD INTERUPT EOR $00 ; YES? THEN CANCEL IT STA $00 ; AND STORE IT BACK CONTINUE =* ; EXIT TO OLD INTERUPT ROUTINE PLA ; GET BACK .A JMP $9DDE ; AND EXIT
PRoGram Files:
Program or PRG files generally contain an image or exact copy of a given area of a computer's RAM. The first two bytes of the file, known as the load address, contain the starting address (in standard low byte/high byte format) of the area of memory contained in the file. The potential length of a PRG file is limited only by the amount of RAM available in the computer. Since the maximum addressable RAM in the 65xx/75xx/85xx series of microprocessers used in most of the Commodore 8 bit computers is 64 K bytes (65535 bytes), a file of this type is, therefore, limited in length to somewhat less than 64 K bytes. (Actually, it is equal to 65535 - load address). Although a PRG file will normally contain a program (either machine language, tokenized basic or some other tokenized language), it can contain anything stored in the computer's memory. This may be a high resolution graphics screen, sprite definitions, color maps, text files from a word processor, assembly language source code, etc. It can even be used to store data from the variables definition area of the BASIC workspace.
Creating a PRG File A PRG file is normally created with the SAVE type commands (SAVE, DSAVE, BSAVE, MONITOR-S, or their KERNAL equivalents). With a disk drive, a PRG file can also be used for storing data, identical to a conventional SEQ (sequential) file. (The DOS makes no distinction internally as to how data are stored in a SEQ and PRG file. The only difference to DOS is the value of the file type flag in the directory entry.) The procedure for accessing a PRG file as a data file is the same as for a SEQ file except that the {file type} specifier in the OPEN statement must be "P". PRG files can be either read or written this way. SEQ files are discussed in more detail in the first section of this chapter. The following example can be used to save a segment of memory to a disk file using BASIC 2.0. (In BASIC 3.5, this can be done with the MONITOR-S command, while in BASIC 7.0 it can be done with either MONITOR-S or BSAVE.)
10 INPUT"FILENAME";F$ 20 INPUT"STARTING ADDRESS";LO 30 INPUT"END ADDRESS";HI 40 OPEN 8,8,8,"0:"+F$+",P,W" 50 L2=INT(LO/256):L1=LO-L2*256 60 PRINT#8,CHR$(L1);CHR$(L2); 70 FOR I=LO TO HI:PRINT#8,CHR$(PEEK(I));:NEXT 80 PRINT#8:CLOSE 8 Although the example is given as a program, the same technique can be used in immediate mode if LO and HI are explicitly defined instead of INPUT from the keyboard. Reading a PRG File A PRG file is generally recovered from disk using the LOAD commands (LOAD, DLOAD, BLOAD, BOOT, RUN, MONITOR-L, or their KERNAL equivalents). A PRG file is the only type of file which can be recovered by the LOAD commands. It can, however, be opened and read with the SEQ file handling commands GET#, and even INPUT# if the data are in a suitable format. The following is a short BASIC 3.5/7.0 demonstration program which will open a PRG file as a sequential file, read the first two bytes and determine the load address. This is handy if, for example, you have forgotten the SYS address to start a machine language program, or you want to examine it with a disassembler and don't know where it resides in memory. 10 TRAP 70 20 INPUT"ENTER FILENAME";F$ 30 DOPEN#1,F$+",P" 40 GET#1,A$,B$:DCLOSE#1 50 AD=ASC(A$)+ASC(B$)*256 60 PRINT"PROGRAM "F$" START ADDRESS =";AD:END 70 PRINT"ERROR "ERR$(ER):RESUME 20 The TRAP statement in line 10 is used to divert the program to line 70 if an error occurs. The specific error that may be encountered is a "FILE NOT FOUND". This error will be detected by BASIC before it is picked up on the disk error channel.
The equivalent program in BASIC 2.0 would be: 10 INPUT"ENTER FILENAME";F$ 20 OPEN 1,8,1,"0:"+F$+",P,R" 30 GET#1,A$,B$:CLOSE 1 40 AD=ASC(A$)+ASC(B$)*256 50 PRINT"PROGRAM "F$" START ADDRESS =";AD:END It should be noted that the BASIC 2.0 version will not trap the FILE NOT FOUND error. If the specified filename is not on the disk, the program will simply stop. Combining BASIC Programs Many people tend to write BASIC programs in a "modular fashion". You can create a library of standard subroutines, or program modules, to perform such tasks as selectively reading a disk directory, bubble sorting, disk file handling, screen formatting and graphics, etc. When creating the first draft of a new program, much of it can be produced by combining some of these standard subroutines. Customization and optimization are relatively minor tasks compared to retyping a number of twenty to thirty line, or more, subroutines every time you want to use them.
The whole concept of combining several subroutines or program files into a single program usually depends on the existence of a BASIC command called MERGE, which performs this task automatically. Although Commodore DOS supports the combining of two or more data files (using APPEND or CONCAT), a MERGE command for BASIC programs is not available as an intrinsic command in any version of Commodore BASIC, not even the newest version 7.0 on the C-128. MERGE is available, however, with several BASIC extenders such as the Programmers' Aid Cartridges for the VIC-20 and C-64. In order to understand how appending BASIC programs works, we must first take a look at the structure of a Commodore BASIC program (PRG) file. A BASIC program line starts with two link bytes followed by two bytes representing the line number. This four byte sequence is followed by the text of the BASIC line with keywords in tokenized form (i.e. BASIC keywords, such as INPUT, PRINT, INT(, etc. are stored as single or double byte "tokens" rather than spelled out). The end of the line is designated by a zero byte (not the character zero "0", but ASCII character code zero CHR$(0)).
The key to creating a program file with many lines is to establish the line links. These links are absolute addresses in low byte high byte format which tell the BASIC interpreter where to find the next BASIC line link. A program file ends with three zero bytes (an end of line zero byte plus two zero bytes to indicate that there are no more line links). Normally, when you add more BASIC lines from the keyboard or edit an existing line, the link bytes are automatically adjusted to reflect the new situation. In addition, in most Commodore computers, BASIC programs are relocatable when they are LOADed, that is, programs will be LOADed back into memory at the start of BASIC pointer, regardless of where they were originally SAVEd from. This allows you to temporarily move the BASIC workspace around in memory (to reserve memory for graphics screens, for example) and to LOAD programs written on one computer with a different one. In this process, the link bytes are automatically adjusted during program execution or LISTing to reflect the new RAM address locations. It is very simple to combine any number of program modules into a single BASIC program. Depending on the computer used and the length of the modules, one of several methods can be used. Although it will work with program modules of any length, the first method is generally only used when at least one of the program modules is relatively short (less than about 20 lines). This method, which incidentally can be used on any brand or model of computer which features a full screen BASIC editor, consists of only a few simple steps and does not involve any additional programming. The procedure is as follows: 1. LOAD the shorter program module into memory. 2. LIST on the screen the section of the module to be combined with the other program. If you are using a C-128, you should switch to the 80-column display if possible because you can fit more on the screen. You can even use both the 40 and 80 screens, if you have two monitors or a switchable one. On all Commodore computers you must leave at least five blank lines at the bottom of the screen. If you can't LIST everything on the screen at once that's OK, you can repeat steps 1 to 5 as many times as you wish. 3. Without erasing any of the LISTing of the first program on the screen, cursor up as far as you can go (it is OK to erase the "READY" message) and type in the command to LOAD the second program into memory. Normally when you do this, the program originally in memory will be erased and replaced by the newly LOADed program. Fortunately, some of the old program can be "protected" in the form of a screen listing. 4. With the cursor control keys, cursor up to the lines of the first program that you wish to add to the second and then press the <RETURN> key. Before pressing <RETURN>, you can change the line number or perform any other editing on the line that you wish. Do this for all the lines displayed on the screen that you wish to add to the second program. The lines from the first program will be added to the second program just as if you had re-typed them on the keyboard. 5. SAVE the combined program under a new or temporary filename. 6. Repeat steps 1 to 5 until all desired lines have been added to the new program. This technique takes advantage of the fact that the BASIC input editor can read data and program lines directly from the screen. LISTing a program, or a portion of a program, on the screen will "protect" the listed portion from being erased when you LOAD in a new program. The protection lasts only as long as the lines are displayed on the screen. When you clear the screen, the protected program will be erased also. Obviously, the longer the programs being merged with this technique are, the more times you will have to repeat the above procedure to complete the merger. The process is, however, extremely flexible in that it works on virtually any computer and you can select and edit the individual lines from one program to be merged with the other. The main disadvantage of the procedure is that you are limited in the number of lines which can be displayed on the screen and thus merged at a given time. The second method works on entire program files rather than just a segment of one. It involves LOADing one program into memory, setting the start of BASIC pointer to the end of the first program and LOADing the second program. The start of BASIC pointer is then re-set back to its original value and the combined program is SAVEd. This can be done in either immediate or program mode. On a C-64, C-16, Plus/4 or VIC-20, the immediate mode procedure is as follows:
1. LOAD"first program",8 This puts the first program into memory normally. This step may be skipped if the desired program is in memory already.
2. PRINT PEEK(43),PEEK(44) Note down these two numbers for future reference in step 7 (referred to as "{first#}" and "{second#}", respectively).
3. HI=PEEK(45)+PEEK(46)*256-2 <RETURN> This is the end of file address for the first file. Remember to include the "-2". This will compensate for the two zero bytes used as the end of file marker.
4. POKE 44,HI/256:POKE 43,HI-PEEK(44)*256 <RETURN> This sets the start of BASIC pointer to the end of the first file.
5. NEW <RETURN> This resets all of the other BASIC pointers to match the new start address.
6. LOAD"program 2",8 <RETURN> The second program is now loaded in at the end of the first.
7. POKE 43,{first#}:POKE 44,{second#} <RETURN> This resets the start of BASIC pointer to its normal location. The combined program is now ready to be SAVEd or used.
The following short program can be used on the C-64 to perform the same task in program mode: 10 REM AUTO PROGRAM COMBINER 20 POKE 56,PEEK(44)+5:CLR:DE=8:L0=PEEK(56)*256+PEEK(55) :POKE L0-1,0 30 INPUT" ROOT FILE NAME >>";RF$ :INPUT" APPEND FILE NAME >>";AF$ 40 INPUT" MERGED FILE NAME >>";MF$ :F$=RF$:L=L0:GOSUB 60:L=MH-2:F$=AF$:GOSUB 60 50 SA=1:F$=MF$:L=L0:GOSUB 60:PRINT" ***DONE***":END 60 PRINT" *INSERT "F$" DISK*":PRINT"THEN PRESS A KEY" :POKE 198,0:WAIT 198,1 70 FOR I=1 TO LEN(F$):POKE 827+I,ASC(MID$(F$,I,1)):NEXT :POKE 780,LEN(F$): POKE 781,60 80 POKE 782,3:SYS 65469:POKE 780,1:POKE 781,DE:POKE 782,SA :SYS 65466:IF SA THEN 100 90 POKE 780,0:POKE 782,L/256:POKE 781,L AND 255 :SYS 65493:GOTO 120 100 POKE 252,L/256:POKE 251,L AND 255:POKE 780,251 110 POKE 782,MH/256:POKE 781,MH AND 255:SYS 65496 120 OPEN 15,8,15:INPUT#15,DS,DS$:CLOSE 15 :IF DS THEN PRINT" "DS;DS$:END 130 MH=PEEK(781)+256*PEEK(782):RETURN When you run the program, you will be prompted to enter three things: the ROOT file name; the APPEND file name; and the MERGED file name.
The ROOT file is the program or module which contains the lowest line numbers as outlined below. The APPEND file is the program or module which contains the highest line numbers. The relative lengths of the ROOT and APPEND files are not important. The MERGED file name is the name under which you wish to store the new combined program file. All three file names should be different unless you want to use the infamous "SAVE with replace" DOS option for saving the new file. The example will also work on the VIC-20 and on the C-128 when it is in C-64 emulation mode. In addition, because of its simplicity of operation it is easily adaptable to any other Commodore computer which is capable of relocating BASIC programs during a normal LOAD. The program will prompt you to insert the disk with each file on it then press a key to continue. When the ROOT and APPEND files have been loaded, the program will prompt you to insert the disk to save the combined file on and then save it. The three files need not be on the same disk. There is one relatively minor restriction on the line numbering of the programs being appended either in immediate or program modes. Since these methods work by appending all of the lines in one program module to the end of the another program module, the line numbers of the second program module must all be greater than the line numbers of the first module. If you do not adhere to this restriction, some odd things may happen to the combined program. For example, when you list the program you may find that line 50 comes after line 100 or before line 20. This can be avoided by ensuring that the line numbers are in correct sequence before merging the programs. In BASIC 3.5 or 7.0, the combined programs can be renumbered with a RENUMBER command. If duplicate line numbers are included, as above, they will be correctly renumbered to reflect their sequence in the combined program but, references to the duplicate lines such as GOTO's and GOSUB's will not be correctly renumbered. The third way to combine program files on the C-128 is to use the programmable function keys to store the command sequence for the immediate mode method. The following is a short BASIC program which redefines function key <F2> and SAVEs the new key definitions in a disk file called "MERGE". Enter and run this program first to create the MERGE file. 10 REM FUNCTION KEY MERGE SETUP 20 A$=CHR$(13)+"POKE 250,PEEK(45):POKE 251,PEEK(46): X=PEEK(4624)+PEEK(4625)*256-2:POKE 45,X AND 255: POKE 46,X/256"+CHR$(13) 30 B$="DLOAD(ME$)"+CHR$(13)+"POKE 45,PEEK(250): POKE 46,PEEK(251)"+CHR$(13): KEY 2,(A$+B$) 40 BSAVE"MERGE",B0,P4096 TO P4352 To use this MERGE file, follow these three easy steps: 1. Before you start an editing session, type in: BLOAD"MERGE",B0 <RETURN> to retrieve the special function key defintions. 2. LOAD the first program (the ROOT program as described above) or enter it into memory from the keyboard. 3. When you want to add a previously SAVEd program module (equivalent to the APPEND file described above), type in ME$="filename2", then press function key <F2> instead of the <RETURN> key. Step three can be repeated as often as you wish without having to reload the MERGE definition file each time. A series of BASIC commands will be printed on the screen and executed while the disk drive comes on for a moment. The function key method of appending requires the same care with line numbers as the other program appending methods outlined above. It should also be noted that no error checking is done by the function key, so make sure that the file you specify as ME$ actually exists on your current disk or strange things may happen. Not to worry though, a simple <RUN/STOP>-<RESTORE> key sequence will abort the function key commands if necessary. The final method takes advantage of a peculiarity in the KERNAL ROM of the C-128 and involves MERGEing a SEQ file listing with a BASIC program using the CHKIN in immediate mode technique described in Chapter 6 as a sequential disk command file (SDC). The input routine in the C-128 KERNAL will accept line numbers in SDC's. These line numbered files will "execute" exactly as if the line, complete with line number, had been entered from the keyboard. That is, it will be added to any program currently in memory. This little trick is a simple yet powerful way to MERGE two or more program files on the C-128. Unlike other psuedo merge routines which merely append one program to the end of another, this technique allows full intermeshing of line numbers. Only a few steps are required. First, LOAD one of the programs into memory in the normal manner or enter it from the keyboard. Next convert it into a SEQ disk file listing with a series of commands such as: OPEN 8,8,8,"0:filename,S,W":CMD8:LIST PRINT#8:CLOSE8 LOAD in the second file or enter it from the keyboard. With the second file now in memory, read in the SEQ listing of the first file with: DOPEN#8,"filename" SYS 65478,0,8 After a brief period, the programs will be fully merged. The merge will terminate with a mysterious "OUT OF DATA" error message. This is a good sign: the process worked. The error message is caused by the last line of the listing in the disk file "READY.". (Commodore BASIC listings always include this.) The computer, not being able to recognize its own writing, thinks that someone typed in "READ Y". Since there are no accompanying DATA statements, the out of data error occurs and control is restored to the keyboard. If the READY. message did not appear at the end of the listing (for example if you edited it out with a word processor to give it a neater appearance), keyboard control would only be restored with a <RUN-STOP>/<RESTORE>. The programs will, however, be merged correctly. This technique can also be used for "loading" program listings produced on machines with incompatible keyword tokens and programs transferred into SEQ files via a modem download. This method is perhaps the easiest way to implement a MERGE command on the C-128. In additon, it is the only one of the methods presented which gives you the fully MERGEd program in RAM, ready to run when it is completed. RELative Files: Relative (REL) files are a convenient method for storing and retrieving a series of similar or otherwise related data that may be edited, added to, searched, sorted, etc. REL files are the only type in Commodore DOS that allow the simultaneous input and output of data to the same open file. In other operating systems, such as CP/M or MS-DOS, this concept is known as a "random" file. (Unfortunately, Commodore uses the term "random file" to signify access to specific tracks and sectors on a disk.) Whatever you choose to call it, a relative file handles data in packets of uniform size called "records". Each record may be further subdivided into logical or physical "fields", each of which contains a specific item. For example, a record may be an page in an address file. Each record in this example would typically have a field for name, address, city, zip code, phone number, etc. The total length of the record is the sum of the lengths of each of the fields. This physical record length is fixed when the file is first created.
The length of each logical field within the record is usually fixed and the fields are always entered in the same order. Variable length (floating) physical fields can also be used, but the total length of a record still remains fixed. The choice of fixed or floating fields must be made when the datafile is first created because it will affect how data are read from and written to the file. Once the choice has been made, you must stick with it. In Commodore DOS, a relative file consists of two parts: "data sectors" and "side sectors". The data sectors contain the file data in a format identical to other file types. The side sectors contain the sector linking information for all data sectors in the file. This is used by the operating system to access each record selectively without having to trace through the sector link chain. Creating and maintaining REL files has never been easier than with the C-128. The procedure can be broken down into four easy steps: 1. OPEN the file 2. find the record you want 3. read or write the desired data 4. CLOSE the file. Each step will be discussed in turn below. Opening a Relative File To create a REL file, or to read or edit an existing file, the DOPEN statement is used in BASIC 3.5 or 7.0. This takes the form of:
DOPEN#{file#},"{filename}",L{record length}
In BASIC 2.0, the following procedure is used: OPEN {file#},{device#},{channel#},"{filename},L," +CHR$({record length}) In both cases, {file#} is the logical file number associated with the file for future input and/or output operations; "{filename}" is the name under which the file is stored in the disk directory (Relative files are denoted by the identifier REL listed after the filename in the disk directory.); and {record length} is the length in bytes to be used for each record in the file. {record length} need only be specified when a file is first created. The record length for a previously created file is stored as part of the file pointers in the directory entry and the side sectors. You should not attempt to change the length once a file has been created. If you incorrectly specify the record length for an already existing file, a DOS error code 50, RECORD NOT PRESENT, error will occur. If "{filename}" does not exist, then a new file will be opened automatically, otherwise the existing file is opened for reading and/or additions. The {channel#} parameter in the BASIC 2.0 version is used in conjunction with the record positioning procedure used by BASIC 2.0. Because the {channel#} is not specified explicitly with the BASIC 3.5/7.0 version, you should not mix the record specifying methods. That is, if you OPEN the file using BASIC 2.0, you should use the BASIC 2.0 record positioning method. Likewise, if you DOPEN the file from BASIC 3.5 or 7.0 you should use the BASIC 3.5/7.0 record positioning method. When setting up a relative file with fixed length fields, it is necessary to add up all of the character spaces required for each field, including punctuation, carriage return characters, and "hidden" items such as leading spaces associated with numeric values, to determine the required record length. It may be wise to add a few extra spaces for contingency. The other approach is to define a series of "floating" fields. The length of each individual field can vary from record to record, however, the total length of the record remains fixed and can be estimated using the procedure outlined above for fixed fields. In either case, the maximum record size allowed by Commodore DOS is 254 characters and the minimum is 2. The optimum record sizes based on speed and ease of access at the internal DOS level is either 127 bytes or 254 bytes. This corresponds to a half or whole data sector. It should be noted that Commodore DOS, as used on the 1571 drive, only permits one relative file to be OPEN at a time. This is due to a limitation on buffer space in the disk drive.
When a REL file is first set up, it is initially assigned 1 data sector and 1 side sector. Unused record space is filled with a CHR$(255) in the first position of a record followed by a series of CHR$(0)'s in the remainder of a record. As the file grows, more data sectors are assigned, one at a time, and side sectors are added as required. The number of side sectors used depends on the number of records. A maximum of 6 side sectors can be associated with a single relative file. This will map out a total of 720 data sectors, although only 612 are actually used. Each additional data sector is automatically filled with a CHR$(255) and CHR$(0)'s as it is allocated. The following BASIC 7.0 technique can be used to determine the record length of an unknown file. The method relies on detecting the DOS error code 51, OVERFLOW IN RECORD, error. 10 DOPEN#1,"filename" 20 FOR I=2 TO 254:RECORD#1,1,I:IF DS<>51 THEN NEXT 30 PRINT "RECORD LENGTH IS"I-1" BYTES":DCLOSE The equivalent in BASIC 2.0 is: 10 OPEN 1,8,1,"filename":OPEN 15,8,15 20 FOR I=2 TO 254 30 PRINT#15,"P"+CHR$(97)+CHR$(1)+CHR$(0)+CHR$(I) 40 INPUT#15,DS:IF DS<>51 THEN NEXT 50 PRINT"RECORD LENGTH IS"I-1" BYTES":CLOSE 1:CLOSE 15 Finding a Record Once the REL file has been opened, it is ready to accept or provide data to the host program. Since the file is actually a collection of records, you must first determine where in the file you wish to read or write the data. Unlike conventional SEQ data files, where you must read or write each byte in sequence before you can read or write the next, you can read from, or write to, any record (in any order) with REL files. The same file can also be used for both reading and writing simultaneously. In BASIC 3.5 and 7.0, the RECORD command is used to position the relative file pointers to the correct location for subsequent input and/or output. The syntax of the command is: RECORD#{file#},{record#}[,{byte#}] This allows data to be read from or written to {record#} of logical {file#}, optionally beginning at {byte#}. The {byte#} parameter is generally only used for fixed field length record formats. A {byte#} value of 1 can be included for floating fields to ensure that the byte pointer is always positioned at the first character in the record. If {byte#} is greater than the record length, a DOS error code 51, OVERFLOW IN RECORD, error will result. In BASIC 2.0, record pointers are set through the disk drive command channel in the following manner (assuming file #15 has been previously opened as the disk command channel): PRINT#15,"P"+CHR$(96+{channel#})+CHR$({rec#lo})+CHR$({rec#hi}) [+CHR$({byte#})] where {channel#} is the channel (secondary address) used in the OPEN statement for the file, and {rec#lo} and {rec#hi} are the low and high bytes of the desired record number, respectively. {byte#} has the same meaning as in the BASIC 7.0 RECORD command. Although apparently not technically necessary for the 1571 drive, a 96 is added to the {channel#} parameter for consistent operation with other Commodore disk drives. The 1571 Disk Drive Users Guide recommends that the RECORD command (or its BASIC 2.0 equivalent) be issued once before and again after any attempt is made to read or write the file. While this precaution may seem unnecessary to some, it is wise to use it to prevent the corruption of data in your file if a major error occurs during attempted file access and, more importantly, to allow adequate time for DOS to locate and read in the desired record. This is especially important for "odd" sized records. (That is, anything but a half sector (127 bytes) or full sector (254 bytes) record size.) Another method frequently used to wait until the record pointers have been correctly set is to read the disk error status immediately after issuing the record positioning pointers. This will "tie-up" any further communication between the computer and the disk drive until after the record has been located and read into the buffer. This latter method is often used with machine language and KERNAL calls. It is also necessary to reposition the pointers for a direct read-after-write or write-after-read, to ensure correct placement of the data. If you attempt to read or write to a record which is outside of the range currently defined for the file, a DOS error code 50, RECORD NOT PRESENT, error may occur. If trying to read a record, this error indicates that you have gone past the last data sector used in the file. (Depending on the size of your record, you may be able to go one or two records past the current last record before you get this error. This is because the assigned space for the record is already included in the sectors previously read into the disk buffer.) An unused record can always be identified by a CHR$(255) as the first character in the record. If trying to write a record, this is not an error message but only an indication that you are writing a new record. The maximum record number allowed is 65535. The minimum is 1. The maximum file size (# of records x record length) that is currently supported on the 1571 drive is 167,132 bytes (163 K bytes). This is the same for both single and double sided disks. (I.e. relative files must all fit on one side of the disk.) Relative files have a habit of growing uncontrollably. You cannot "delete" records from the file. The most you can do is copy good records from a file into a new file, then scratch the old file. If the file grows to take over your whole disk, a DOS error code 52, FILE TOO LARGE, error will result. It is then time to split the file into several smaller ones, or too purge out all the unused records. Input and Output With the relative file pointers correctly positioned, you can proceed with input and output using the normal disk file commands such as INPUT#, GET#, PRINT#, and PRINT#,USING. In this respect, relative files are no different than any other type of disk file. However, because each record length is fixed, you should always check the length of anything being written to disk to make sure that it will fit. If you try to overrun a record boundary (i.e. there is not enough space in the record to contain all of the data), a DOS error code 51, OVERFLOW IN RECORD, error will occur. When this happens the extra characters will be dropped or truncated from the end of the record to make it fit. It will not, however, affect the rest of the file. It should be noted that not all input and output operations will appear to activatethe disk drive, especially if the record length is short. This is because the disk drive will read two sectors into the buffer at once. If the record you try to access is already contained entirely in the sectors in the buffer, no visible disk activity will take place. (When two sectors are in the disk buffers, at least one record of 254 bytes or less will always be in memory, no matter how it is physically contained on the disk.)
Input On the C-128, the input buffer is 160 characters long. Therefore, the INPUT# command can only read 160 characters at a time. If your record length is less than 160 characters, then there is no problem: the entire record can be read at once. If it is greater than 160, then you must either read the record with a series of GET#'s or insert one or more carriage returns into the record to divide it up into smaller chunks (or physical fields). Breaking up a record into physical fields (as opposed to logical fields used in fixed field length format records) is the basis of the floating field method of relative file construction. Each field is separated by a carriage return and therefore, can be read directly with a separate INPUT# statement. Of course, you can use floating fields with a record length of less than 160, but remember: in both cases each carriage return character counts as one character in the record length. In many respects, floating fields are much easier to use in BASIC simply because you do not have to keep track of the lengths of individual fields. The disadvantage of using floating fields is that each record acts like a sequential file, that is each field must be read or written in the proper order. It is generally more difficult (although by no means impossible) to read and write floating fields in the selective manner of fixed fields. Assuming a file with a record length of 80 characters and four data fields, the following is an example of how to read a floating field record format (neglecting the error channel for a moment): 10 DOPEN#1,"DATAFILE",L80 20 INPUT "RECORD # TO READ";NU 30 RECORD#1,(RE),1 40 INPUT#1, F1$,F2$,F3$,F4$ 50 RECORD#1,(RE),1 . . . (the rest of your program) In the above example, each field can be any length, as long as all four totaled do not exceed 80 characters. The field lengths are quite often different from record to record. Note that the ",1" is included at the end of each RECORD statement. This is to ensure that the byte pointer is correctly positioned to the first character in each record. Assuming each field was 20 characters long, and lines 10, 20 and 30 are identical to those above, a similar program for reading fixed field records would appear as : . . . 40 INPUT#1,A$ 50 RECORD#1,(NU),1 60 F1$=LEFT$(A$,20):F2$=MID$(A$,20,20) 70 F3$=MID$(A$,40,20):F4$=RIGHT$(A$,20) . . . When using the fixed field model, no field can exceed 20 characters (in this example) even if some of the other fields contain less than the maximum number of characters. If the record length is to be longer than 160 characters, then either a carriage return would be required to break the record up into less than 160 character chunks, or the record should be read using a series of GET#'s. In the first case, line 40 in the example would read: 40 INPUT#1,A1$,A2$ Each of A1$ and A2$ would then be divided into its logical fields, using a method similar to line 60 in the above example. In the second case, line 40 might read: 40 A$="":FOR I=1 TO {record length}:GET#1,A1$:A$=A$+A1$:NEXT A$ would then be divided into its constituent logical fields as in the previous example. Output Output to relative files is similar to other disk file types: it is done using the PRINT# (or PRINT#, USING;) command. Of course, the data must be output to the file in the correct order and, if using floating fields, each item must be separated by a carriage return. The following example will write the data represented by F1$, F2$, F3$, and F4$ to the file created for the input demonstration outlined above using floating field format. 90 CR$=CHR$(13) 100 INPUT "RECORD #";NU 110 INPUT "FIELD #1 DATA";F1$ 120 INPUT "FIELD #2 DATA";F2$ 130 INPUT "FIELD #3 DATA";F3$ 140 INPUT "FIELD #4 DATA";F4$ 150 R$=F1$+CR$+F2$+CR$+F3$+CR$+F4$+CR$ 160 IF LEN(R$)>80 THEN PRINT"RECORD TOO LONG":GOTO 100 170 RECORD#1,(NU),1:PRINT#1,R$:RECORD#1,(NU),1:GOTO 100 Note the carriage return character specifically placed between each field. If you are absolutely certain that the total record length will not exceed the specified record size, the following can be substituted for lines 150 to 170: 150 RECORD#1,(NU),1 160 PRINT#1,F1$:PRINT#1,F2$:PRINT#1,F3$:PRINT#1,F4$ 170 RECORD#1,(NU),1:GOTO 100 The above example using fixed field records may look like: 90 SP$="{20 SPACES}" . . . 150 F1$=LEFT$(F1$+SP$,20):F2$=LEFT$(F2$+SP$,20) 160 F3$=LEFT$(F3$+SP$,20):F4$=LEFT$(F4$+SP$,20) 170 R$=F1$+F2$+F3$+F4$:RECORD#1,(NU),1 180 PRINT#1,R$;:RECORD#1,(NU),1:GOTO 100 Alternatively, for fixed field lengths, the following may be substituted in the above example beginning at line 170: 170 RECORD#1,(NU),1:PRINT#1,F1$:RECORD#1,(NU),1 180 RECORD#1,(NU),21:PRINT#1,F2$:RECORD#1,(NU),21 190 RECORD#1,(NU),41:PRINT#1,F3$:RECORD#1,(NU),41 200 RECORD#1,(NU),61:PRINT#1,F4$:RECORD#1,(NU),61 This last method has the advantage that each field can be written separately. It should be noted, however, that with any output method, DOS will always fill to the end of the record with CHR$(0)'s, regardless of whether or not the record contains any more valid data. Therefore, the selective field writing should only be done if all subsequent data in the record is going to be updated. Otherwise, you must write the entire record at once. Closing the File A relative file is closed using a standard CLOSE {file#} or DCLOSE type of statement. In this respect, it is identical to all other types of disk files. However, you should not use a dummy PRINT# statement before closing the file as is customary for other types of write files. Relative File Demo The following program is a short demonstration of the principles of relative file creation and input/output. It can be used to store addresses and phone numbers. Each record has a length of 80 bytes, consisting of 7 floating fields representing the following data: ................................... Field name Estimated length ................................... NAME 20 STREET ADDRESS 20 CITY 15 STATE 5 ZIP 5 AREA CODE 5 PHONE # 10 ===== total # bytes 80 ................................... 10 DOPEN#1,"DATAFILE",L80:IF DS THEN GOSUB 410:DCLOSE:END 20 PRINT"{CLR}RELATIVE FILE DEMO":CR$=CHR$(13) 30 PRINT"1: READ":PRINT"2: WRITE":PRINT"3: QUIT" 40 INPUT"ENTER SELECTION";S:IF S<1 OR S>3 THEN 20 50 ON S GOSUB 90,240,430:GOTO 20 60 : 70 REM READ RECORDS 80 : 90 WR=0:PRINT"{CLR}READ RECORDS":INPUT"RECORD NUMBER";NU: IF NU=0 THEN RETURN 100 RECORD#1,(NU),1:IF DS THEN GOSUB 410:GOTO 90 110 INPUT#1,N$,S$,C$,SA$,ZI$,AC$,PH$ 120 RECORD#1,(NU),1 130 IF N$=CHR$(255) THEN PRINT"RECORD NOT PRESENT": GOSUB 420:GOTO 90 140 PRINT" NAME >> "N$ 150 PRINT" ADDRESS >> "S$ 160 PRINT" CITY >> "C$ 170 PRINT" STATE >> "SA$ 180 PRINT" ZIP >> "ZI$ 190 PRINT"AREA CODE >> "AC$ 200 PRINT" PHONE >> "PH$:IF WR THEN RETURN:ELSE GOSUB 420: GOTO 90 210 : 220 REM WRITE RECORDS 230 : 240 PRINT"{CLR}WRITE RECORDS":INPUT"RECORD NUMBER";NU: IF NU=0 TNEN RETURN 250 RECORD#1,(NU),1:WR=1 260 IF DS=50 THEN PRINT"NEW RECORD":ELSE IF DS THEN GOSUB 410: GOTO 240 270 IF DS<>50 THEN GOSUB 100 280 INPUT" NAME >> ";N$:N$=N$+CR$ 290 INPUT" ADDRESS >> ";S$:S$=S$+CR$ 300 INPUT" CITY >> ";C$:C$=C$+CR$ 310 INPUT" STATE >> ";SA$:SA$=SA$+CR$ 320 INPUT" ZIP >> ";ZI$:ZI$=ZI$+CR$ 330 INPUT"AREA CODE >> ";AC$:AC$=AC$+CR$ 340 INPUT" PHONE >> ";PH$:PH$=PH$+CR$ 350 R$=N$+S$+C$+SA$+ZI$+AC$+PH$ 360 IF LEN(R$)>80 THEN PRINT"RECORD TOO LONG":GOSUB 420:GOTO 280 370 RECORD#1,(NU),1:PRINT#1,R$:RECORD#1,(NU),1:GOTO 240 380 : 390 REM ERROR AND EXIT ROUTINES 400 : 410 PRINT"DISK ERROR >> ";DS$:GOSUB 420:RETURN 420 PRINT"PRESS A KEY TO CONTINUE":GETKEY Z$:RETURN 430 DCLOSE:END Index Files Sequential data files are often used as index or "key" files for relative files. Commodore DOS will allow one SEQ and one REL file to be OPEN at the same time with a 1571 disk drive. An index can be read from a SEQ file and stored in the computer's memory in array form. Of course this method is limited by the size of the array, and hence, the computer's memory, but it does allow for a quick and easy way to search and sort data in relative files. (The index arrays are searched or sorted entirely in memory, then only the selected records are actually read from the relative file.)
Index files can be used in a variety of ways. Often it is quicker to search an index for a specific record than searching the entire relative file. The only disadvantage is that index files require a bit of time to create and must be updated each time you change the relative file. However, if you have a large database that is fairly static (i.e. you are not constantly changing it) the advantages far outweigh the disadvantages. For example, supposing you had a relative file, similar to the one used in the example in the previous section, which contained 1000 records. You wish to find the address of a given name. There are two ways that this can be done. One is to read each record in sequence to until you find a match with the name field. This can be very time consuming for a large database. The second method is to build an index and scan it instead. Once the index has been built, it can be used to speed up subsequent searches.
The following example demonstrates the creation and use of an index file. (Assume that the database file has been created similar to the one used in the example above.)
0 REM CREATE INDEX 1 REM RELATIVE FILE = DATAFILE 2 REM INDEX FILE = INDEX 10 DIM N$(1000) : REM SET UP INDEX ARRAY 20 OPEN 1,8,4,"INDEX,S,W" : REM OPEN INDEX FILE 30 OPEN 8,8,8,"DATAFILE" : REM OPEN REL FILE 40 RE=1 : REM FIRST RECORD 50 RECORD#8,(RE),1 : REM GET RECORD 60 IF DS THEN 100 : REM CHECK FOR END OF FILE 70 INPUT#8,NA$ : REM READ NAME 80 N$(RE)=NA$ : REM STORE IN ARRAY 90 RE=RE+1 : GOTO 50 : REM NEXT RECORD 100 RE=RE-1 110 PRINT#1,RE : REM SAVE # OF RECORDS 120 FOR I=1 TO RE 130 PRINT#1,N$(I) : NEXT : REM PRINT INDEX 140 PRINT#1 : CLOSE 1 : CLOSE 8
0 REM USE INDEX 10 DIM N$(1000) 20 OPEN 1,8,4,"INDEX" 30 INPUT#1,NU : REM NUMBER OF RECORDS 40 FOR I=1 TO NU 50 INPUT#1,N$(I) : NEXT : REM RECOVER INDEX 60 CLOSE 1 70 OPEN 8,8,8,"DATAFILE" : REM OPEN REL FILE 80 INPUT "NAME TO SEARCH FOR";NS$ 90 RE=0 : FOR I=1 TO NU : REM SEARCH INDEX 100 IF NS$=N$(I) THEN RE=I : I=NU+1 : REM RECORD FOUND 110 NEXT : IF RE=0 THEN 160 : REM CONTINUE SEARCH 120 RECORD#8,(RE),1 : REM GET RECORD 130 INPUT#8,N$,S$,C$,SA$,ZI$,AC$,PH$ 140 PRINT N$,S$,C$,SA$,ZI$,AC$,PH$ : REM DISPLAY RECORD 150 GOTO 80 : REM TRY AGAIN 160 PRINT "NAME NOT FOUND" : GOTO 80
Another type of index file might contain the sorted order of the records by various fields. For example, in the above demonstration, we wish to print out the entire list arranged alphabetically by name or by area code. A single index file can be used to contain the sorted parameters. Such a file may look like: 57, 102, 1, 17, 27, etc. where these numbers represent the sequence of records according to the specified sorting. In this case, the records are not actually moved in the file but you access them in a given sequence. Multi dimensional arrays can be used to contain a master cross reference of all the records according to all possible sorting mechanisms. For example, an index file might contain: 57, 1, 32, 4 19, 57, 22, 1 9, 7, 1, 2 etc. Each line of this example represents four distinct sorting creiteria. The records in sequence 57, 19, 9 might be the arrangement of the records by name, while 1, 57, 7, might be the arrangement by area code, 32, 22, 1 by city and 4, 1, 2 by zip code, etc.
This shows that index files have a great deal of flexiblity and can be used for many purposes. The key factor is that it is faster to search a table in memory for a given entry number them get that entry directly rather than search each entry on disk looking for a match.
Burst Mode Commands The 1571 disk operating system (DOS) supports an extended set of commands, collectively called "burst mode" or the "burst command instruction set" (BCIS), which enables the drive to, among other things, create, read and write disks in a wide variety of formats with relative ease. Many, but not all, of the burst mode commands have analogous commands in standard Commodore DOS. Why then is there a need to duplicate them in burst mode? The answer is twofold: speed and versatility. The data transfer rate of a 1541 drive (or a 1571 in 1541 mode) is about 350 bytes per second. The 1571 in fast mode can read about 1600 bytes per second. With burst mode, data can be read at the blistering rate of 4000 or more bytes per second! In fact, the ultimate read speed is not limited by the hardware. It is controlled by the efficiency of the driving software. (That is, burst mode can, in theory, transfer data as fast as you can send it in machine language.) In addition, burst mode allows you to read and write disks formatted in the industry standard MFM (maximum (or modified) frequency modulation) format as well as Commodore GCR (group coded recording) format. The MFM format is used by most CP/M computers such as Kaypro, Epson, Osborne, etc., as well as MS-DOS (IBM-PC and compatible) and TRS-80 machines. Apple ][ type computers use a modified form of GCR which is not compatible with either MFM or Commodore GCR.
Command Syntax Burst mode commands are accessed by sending a specific set of characters through the disk command channel, just like the usual Commodore DOS commands such as "n0:", "s0:", "b-p:", etc. Because the first two characters of the command string for all burst mode commands are "u0", burst commands are often referred to as the extended user 0 commands.
Burst mode can be accessed by a BASIC statement such as:
OPEN 15,8,15,"U0"+CHR$({command})+additional characters
or the machine language equivalent. Alternatively, if the command channel has been opened previously, the "OPEN 15,8,15" can be replaced by "PRINT#15". The "additional characters" depend on the desired command and its options. These characters generally select secondary options. Not all of the secondary option characters need to be sent in some cases because default values can often be assumed.
The burst mode command byte can be divided into two parts: Bit 7 6 5 4 3 2 1 0 <-- switches --> <-- command -->
The command parameter values for the low nibble are summarized in Table 9-1. The high four bits contain the primary command options. These are a series of up to four on/off switches which control some of the main features of the command. The switch values are outlined in Table 9-2 and detailed in the appropriate sections of this chapter for each of the commands. It should be noted that not all commands use all switches, but a given switch that is used in several of the commands always occupies the same bit position. For example, the MFM side select switch is always controlled using bit 4 of the command byte.
Table 9-1: Burst mode command byte low nibble values ....................................................... Command Nibble Value Command -------------------- Binary Hex ....................................................... 0 0 0 0 0 Sector read 0 0 1 0 2 Sector write 0 1 0 0 4 Inquire disk 0 1 1 0 6 Format disk 1 0 0 0 8 Sector interleave 1 0 1 0 A Query disk format 1 1 0 0 C Inquire status 1 1 1 0 E Set utilites * 1 1 1 1 F Fast load * ....................................................... * Note: these commands also require bit 4 to be set to 1.
Table 9-2: Summary of burst mode command switches ................................................................ Designation Bit Applicable Action Commands ................................................................ B 5 Read; Write 1 = transfer from disk to buffer for read (buffer to disk for write) with no burst transfer between computer and drive. 0 = include burst transfer between computer and drive.
C 6 Inquire status 1 = disk has been changed since last log-in. 0 = disk has not been changed since last log-in.
D 5 Format (MFM) 1 = format double sided disk. 0 = format single sided disk.
E 6 Read; write 1 = ignore disk errors. 0 = stop on error.
F 7 Query disk 1 = step head to offset value before analysing. 0 = analyse track 0 only.
I 6 Format (MFM) 1 = write track index mark. 0 = don't write index mark.
M 7 Format 1 = format MFM disk. (second command byte) 0 = format GCR disk.
P 7 Format 1 = partial disk format only. 0 = format entire disk.
S 4 (most) 1 = use side 1 of MFM disk. 0 = use side 0 of MFM disk.
T 7 Read; write 1 = don't transfer data via burst mode. 0 = transfer data via burst mode.
W 7 Inquire status 1 = read old value. and Sector 0 = set new value. interleave
X - dummy filler bit. Value not important and is ignored. Usually set to 0.
Y* 7 Fast load 1 = file does not have to be PRG type to LOAD. 0 = file must be PRG type.
Z* 6 Format (MFM) 1 = include sector skew table. 0 = use default sector table. ................................................................. * Note: This switch label has been changed from that used in the 1571 User's Guide to avoid confusion with a different switch given the same label for another command.
Many of the burst mode commands will return a "status" byte as part of the action of the command. The status byte gives the calling program information on the success or failure of the requested operation. In some cases of failure, it may also be a signal that no burst mode data will be sent. The format of the status byte is as follows:
Bit 7 6 5 4 3 2 1 0 Mode Drive Sector Size <- controller status ->
Where: Mode = 0 for GCR disk = 1 for MFM disk
Drive = 0 (always)
Sector size: 0 0 128 bytes/sector 0 1 256 1 0 512 1 1 1024
The controller status values are summarized in Table 9-3. It should be noted that in some cases the bit patterns have different meanings depending on the type of disk (MFM or GCR) in the drive. These differences are included in the table. In addition, not all status codes are used by all burst mode commands. For example, the "Write protect on" error is used only by the "Sector write" and "Format" commands.
Table 9-3: Burst mode controller status values ............................................................ Bit Pattern Error Condition ............................................................ 0 0 0 x All OK 0 0 1 0 Sector not found 0 0 1 1 GCR: No sync mark detected MFM: No address mark detected 0 1 0 0 GCR: Data block not found MFM: (not used) 0 1 0 1 GCR: Data checksum error MFM: Data CRC error 0 1 1 0 Formatting error 0 1 1 1 Verify error 1 0 0 0 Write protect on during write 1 0 0 1 Header checksum error 1 0 1 0 GCR: data extends into next block MFM: (not used) 1 0 1 1 Disk has been changed since log-in 1 1 0 0 (not used) 1 1 0 1 (not used) 1 1 1 0 Syntax error in burst command 1 1 1 1 Drive not present ............................................................
It should be noted that error checking is not performed on any of the parameters before they are passed to the 1571. For the most part, error checking on parameter values (such as track and sector numbers) is not performed by the 1571 either. This must be done by the calling program before the bytes are sent.
An example of sending a complete burst mode command from BASIC is given below (in this example it is assumed that the disk command channel has already been opened as logical file 15):
PRINT#15,"U0"+CHR$(16)+CHR$(5)+CHR$(2)+CHR$(1)+CHR$(4)
This command will tell the drive to read 1 sector of data, beginning at track 5, sector 2 on side 1 of an MFM disk, stop if an error is encountered, else transfer the data to the computer via burst mode and then step the head to track 4 to await the next command.
Sector-Read Burst mode "SECTOR-READ" allows you to read one or more sectors from a disk and transfer the data to the computer. In addition to increased speed it has the added advantage over the DOS "u1" and "Block-Read" commands in being able to read more than one sector of data with a single stroke and being able to read MFM disks as well as standard Commodore DOS GCR disks.
The command string for "SECTOR READ" is:
"U0"+CHR$({read})+CHR$({track})+CHR$({sector}) +CHR$({#sectors})+CHR$({next track})
where {read} has one of the values listed in Table 9-5; {track} and {sector} are the track number and sector number to begin reading at; {#sectors} is the number of sectors to read in the sequence; and {next track} tells the drive where to park the head to await the next command. The {#sectors} parameter should be limited to one track's worth. If it is larger, the command will keep re-reading sectors from the same track until the specified number has been read. Although {next track} is optional, its inclusion is desirable, even if its value is set to {track}, because it minimizes the head movement between subsequent reads and writes. If {next track} is omitted, the head will go back to track 0 after each operation, causing a lot of head bouncing and wasting time.
For a GCR disk, {track} values of 1 to 35 are used to access side 0, while 36 to 70 are used for side 1. For MFM disks, the side is selected by the value of the "S" switch (bit 4 of the command byte), as outlined in Table 9-5.
When the "E" switch (bit 6) is set, errors are ignored. This means that even if the requested sector is not found or an error occurs during the reading process, the command will transfer whatever is already in the buffer to the computer. In this case, the calling program in the computer should analyse the status byte to determine if the data are valid.
Bit 0 is actually a switch to determine the drive number. Since the 1571 is only a single drive unit, this bit is always clear. If the 1572 dual drive is ever produced, the corresponding burst mode commands for drive 1 will have values of 1 greater than those listed in Table 9-5.
When both the "T" and "B" switches (bits 7 and 5) are set (command values of $A0, $B0, $C0, and $D0), no burst mode data transfer occurs. Instead, the specified sector is read into the 1571 data buffers beginning at address $300. (For best results, the "T" and "B" switches should be used in tandem and always have the same value (i.e. either both on or both off)). The data can then be read from the drive over the normal serial bus (either fast or slow) with a series of "MEMORY READ"s. This allows other computers, such as the C-64, to access MFM disks. With this option, only one sector can be read at a time, due to limitations with buffer space in the 1571.
It should be stressed that before reading a disk for the first time (or after removing and re-inserting the same disk), you must "log-in" the disk using one of "QUERY DISK FORMAT", "INQUIRE DISK" or "INQUIRE STATUS". This process is required to set-up the 1571 electronics to handle the particular type of disk involved. These commands are described later in the chapter.
For each sector read, a status byte will be returned first. If the error checking is enabled, and an error occurs, then no data will follow. Otherwise, data bytes from the sector will then be transferred, if burst transfer has been specified. The total number of bytes transferred is thus 1 + number of bytes per sector (including status byte): Sector size Total bytes sent 128 129 256 (MFM or GCR) 257 512 513 1024 1025
For multi sector reads, the sectors are read in the order specified with the burst mode "SET SECTOR INTERLEAVE" command, described later in this chapter. If no interleave has been specified, the sectors are read in the order which they appear on the disk. This may result in incorrect reading of the disk if it has an unusual physical sector skew (that is the sector 2 might not be physically adjacent to sector 1). To be safe, you should always specify a sector interleave prior to multi sector transfers, even if it is only a value of 1 which will force a read in numerical order regardless of how the sectors are physically located on the disk.
Table 9-5: Values for burst mode SECTOR READ command byte ............................................................... Value Action ------------ hex decimal ............................................................... 0 0 read specified sector on MFM disk side 0 or GCR disk, transfer data to computer via burst mode, stop if error. 10 16 same as above, but for MFM disk, side 1. 40 64 same as value 0, but ignore errors. 50 80 same as value $10, but ignore errors. A0 160 read specified sector on MFM disk side 0 or GCR disk into data buffer only. Stop on error. Data can be transferred using DOS MEMORY READ command over normal serial bus. B0 176 same as above, but for MFM disk, side 1. C0 192 same as value $A0, but ignore errors. D0 208 same as value $B0, but ignore errors. ............................................................... Note: the command byte values are based on the following model. Switch designations are described in Table 9-2.
Bit 7 6 5 4 3 2 1 0 <-- switches --> <-- command --> T E B S 0 0 0 0
Sector-Write Burst mode "SECTOR-WRITE" allows you to write one or more sectors to a disk after transferring the data from the computer. Although the speed increase over normal DOS commands is marginal (due to the limitations of the "verify after write" procedure that is used for each sector written), it has the advantage over the DOS "u2" and "Block-Write" commands in being able to write more than one sector of data with a single stroke and being able to write to MFM disks as well as standard Commodore DOS GCR disks.
Similar to the "SECTOR-READ" command described above, it works on a block-by-block basis. That is, it cannot be used to automatically SAVE an entire file. Each block (or group of sectors) of the file must be written individually under user control.
The command string for "SECTOR WRITE" is:
"U0"+CHR$({write})+CHR$({track})+CHR$({sector}) +CHR$({#sectors})+CHR$({next track})
where {write} has one of the values listed in Table 9-6; {track} and {sector} are the track number and sector number to begin writing at; {#sectors} is the number of sectors to write in the sequence; and {next track} tells the drive where to park the head to await the next command. The {#sectors} parameter should be limited to one track's worth or less. If more sectors are specified, the 1571 will keep over-writing sectors on the same track until the specified number has been reached. This may result in a corrupted disk. Although {next track} is optional, its inclusion is desirable, even if its value is set to {track}, because it minimizes the head movement between subsequent reads and writes. If {next track} is omitted, the head will go back to track 0 after each operation, causing a lot of head bouncing and wasting time.
For a GCR disk, {track} values of 1 to 35 are used to access side 0, while 36 to 70 are used for side 1. For MFM disks, the side is selected by the value of bit 4 of the command byte, as outlined in Table 9-6.
When the "E" switch (bit 6) is set, errors are ignored. This means that even if the requested sector is not found or an error occurs during the writing or verifying process, the command will continue. In this case, the calling program in the computer should analyse the status byte to determine if the write was successful and the data are valid.
Bit 0 is actually a switch to determine the drive number. Since the 1571 is only a single drive unit, this bit is always clear. If the 1572 dual drive is ever produced, the corresponding burst mode commands for drive 1 will have values of one greater than those listed in Table 9-6.
Table 9-6: Values for burst mode SECTOR WRITE command byte ............................................................... Value Action ------------ hex decimal ............................................................... 2 2 transfer data to drive via burst mode and write specified sector on MFM disk side 0, or GCR disk. Stop if error. 12 18 same as above, but for MFM disk, side 1. 42 66 same as value 2, but ignore errors. 52 82 same as value $12, but ignore errors. A2 162 write buffer to specified sector on MFM disk, side 0 or GCR disk. Stop on error. Data can be written to buffer first using DOS MEMORY WRITE command over normal serial bus. B2 178 same as above, but for MFM disk, side 1. C2 194 same as value $A2, but ignore errors. D2 210 same as value $B2, but ignore errors. ............................................................... Note: the command byte values are based on the following model. Switch designations are described in Table 9-2.
Bit 7 6 5 4 3 2 1 0 <-- switches --> <-- command --> T E B S 0 0 1 0
When both the "T" and "B" switches (bits 7 and 5) are set (command values of $A2, $B2, $C2, and $D2), no burst mode data transfer occurs. Instead, the specified sector is written directly from the 1571 data buffers beginning at address $300. (For best results, the "T" and "B" switches should be used in tandem and always have the same value (i.e. either both on or both off)). Prior to this, the data can be written to the buffers over the normal serial bus (either fast or slow) with a series of "MEMORY WRITES"s. This allows other computers, such as the C-64, to access MFM disks. With this option, only one sector can be written at a time, due to limitations with buffer space in the 1571.
It should be stressed that before writing a disk for the first time (or after removing and re-inserting the same disk), you must "log-in" the disk using one of "QUERY DISK FORMAT", "INQUIRE DISK" or "INQUIRE STATUS". This process is required to set-up the 1571 electronics to handle the particular type of disk involved. These commands are described later in the chapter.
For multi sector writes, the sectors are written in the order specified with the burst mode "SET SECTOR INTERLEAVE" command, described later in this chapter. If no interleave has been specified, the sectors are written in the order which they appear on the disk. This may result in incorrect writing of the disk if it has an unusual physical sector skew (that is the sector 2 might not be physically adjacent to sector 1). To be safe, you should always specify a sector interleave prior to multi sector transfers, even if it is only a value of 1 which will force a write in numerical order regardless of how the sectors are physically located on the disk.
A burst mode status byte will be returned after each sector written. This byte will give the result of the requested operation.
Inquire Disk Before reading or writing disk sectors via burst mode, it is necessary to set the 1571 electronics to the proper mode (GCR or MFM) and initialize various track and sector addressing parameters which depend on the disk characteristics. This is generally done using one of the burst mode "log-in" commands.
The INQUIRE DISK command is perhaps the simplest way to log-in a new disk prior to burst mode reads and writes. The command string is: "U0"+CHR$({inquire}) where {inquire} has a value of: Hex Dec 4 4 to check a GCR disk (either side) or an MFM disk (side 0) 14 20 to check an MFM disk (side 1).
After logging in the disk, the INQUIRE DISK command will return a single status byte of the type outlined previously. The value of the status byte can be used as an indication of the success of the log-in as well as give some basic information about the organization of the disk. The status byte will give the mode (GCR or MFM) as well as the sector size. It will not return information on the number of sectors on the track, physical sector interleave, or the sector numbering system. For that level of detail, the QUERY DISK FORMAT command (discussed later) must be used. In addition, INQUIRE DISK will only look at the first track on the disk.
Bit 4 of the command byte is the side select switch for MFM disks. It is normally only used to test whether side 1 of an MFM disk is used or not. In rare cases (such as in custom disk formats), side 1 may not have the same sector size (or even mode) as side 0. Setting the switch will allow the drive to log in side 1 without looking at side 0.
The following procedure can be used to distinguish among GCR disks; single and double sided MFM disks: 1) INQUIRE DISK on side 0. The result of this will tell you whether the disk is GCR or MFM. 2) INQUIRE DISK on side 1. The result of this will tell you if side 1 is used.
Note that this procedure can be fooled by a re-used disk that was once formatted as a double sided MFM disk.
If the object is merely to log-in the disk and the value of the returned status byte is not important to the calling program, then the program can immediately procede to the read/write operation without first reading the status byte. The process of sending the next command over the serial bus will cancel the need to read the status byte.
Format The 1571 is capable of formatting disks in both GCR mode and MFM mode. The burst mode FORMAT command can be used in both cases. It should be noted that burst mode format commands do not require any actual burst mode data transfer. They can be used entirely with the conventional fast or slow serial bus, even from BASIC.
Format GCR The command string bytes for a GCR format are: "U0"+CHR$(6)+CHR$(0)+"{id}" where {id} is the normal two character ID code used in the standard DOS format (N0:diskname,ID) command. It should be noted that no directory or BAM is created on the disk with this command. Because of this, its usefulness is limited to creating GCR CP/M disks, custom GCR disks or perhaps for a whole disk backup program. The speed of formatting with this command is not significantly faster than with the normal DOS command.
Format MFM The command string for formatting an MFM disk is: "U0"+CHR$({format})+CHR$({type})+CHR$({interleave})+ CHR$({sector_size})+CHR$({last_track})+ CHR$({#sectors})+CHR$({start_track#})+ CHR$({start_track_offset})+CHR$({fill_byte})+ CHR$({sector_table})+... The values and switches for {format} and {type} are given in Table 9-7. The actions of the various switches will be detailed later.
{interleave} specifies a physical sector interleave or skew factor. Normally a default of 0 is assumed (that is, no physical skew). If any of the remaining parameters are included, then it must be specified.
{sector_size} has one of the following values, depending on the desired disk characteristics: 0 for 128 bytes/sector 1 for 256 2 for 512 3 for 1024 The default value is 1 for 256 byte sectors, however, if any of the remaining parameters are specified, it must be included also.
{last_track} is the highest track number to be formatted on the disk. Normally this is 39 (the default). This corresponds to the 40 tracks per side available on a standard MFM disk (numbered 0 to 39). However, some MFM disk types (such as some older CP/M types and TRS-DOS types) only use 35 tracks per side. In that case, {last_track} would have a value of 34. Similarly, if you wanted to squeeze every last byte out of a custom disk format, you may be able to go a few tracks beyond the normal range and increase the value up to 41 or 42. {last_track} can also be used in conjunction with the P flag (for partial format) of the command byte to create disks that have different sector sizes or numbering schemes in different areas of the disk. This is used by some CP/M disk types (typically smaller sector sizes in the first few tracks of the disk than on the bulk of the disk) or can be used as the basis of a customized copy protection scheme.
{#sectors} gives the number of sectors on each track. Combined with the sector size, it is a direct measure of the total storage capacity of the disk type. The values depend on the desired disk type and can be in the following range:
Sector Size {#sectors} Default 128 1 to 27 26 256 1 to 18 16 512 1 to 10 9 1024 1 to 5 5
{start_track} is the number to be used in the sector header for the first track on the disk. This is normally 0, but can take on other values, especially when used in conjunction with the P switch of the command byte and {last_track}.
{start_offset} specifies the physical track position to begin formatting at. Again, this would normally be a value of 0, but may take on other values for custom disk types. This is the mechanical position that the head is stepped to prior to commencing the formatting procedure.
(fill_byte} is the byte value written to the empty sectors on the disk. The default value is $E5 (decimal 229) which is the standard for positive polarity CP/M disks. Other disk operating systems may use different values.
The {sector_table} parameters are used to create custom disk types with non-standard sector numbering schemes. The optional table consists of the sequence of sector numbers to be used on the disk. The numbers do not have to be contiguous, although non-contiguous numbers may cause problems later when you try to log-in this disk any read or write to it. It is best used with the partial format switch bit to create a few tracks somewhere on the disk with strange sector numbering for copy protection purposes.
Table 9-7: Values for burst mode Format MFM command bytes ............................................................ Value for {format} Action -------------- Hex Dec ............................................................ 6 6 Format full disk, single sided, side 0, do not write index marks. 16 22 same as above, but for side 1. 26 38 Format full disk, double sided, do not write index marks. 46 70 Format full disk, single sided, side 0, write index marks. 56 86 same as above, but for side 1. 66 102 Format full disk, double sided, write index marks. 86 134 Format partial disk, single sided, side 0, do not write index marks. 96 150 same as above, but for side 1. A6 166 Format partial disk, double sided, do not write index marks. C6 198 Format partial disk, single sided, side 0, write index marks. D6 214 same as above, but for side 1. E6 230 Format partial disk, double sided, write index marks.
............................................................
Note: the command byte values are based on the following model. Switch designations are described in Table 9-2.
Bit 7 6 5 4 3 2 1 0 <-- switches --> <-- command --> P I D S 0 1 1 0
{type} values: ............................................................ {type} value ------------- hex dec Action ............................................................ 80 128 Format MFM, use standard sector numbering beginning with sector #0. 81 129 Format MFM, use standard sector numbering beginning with sector #1. 8+x 128+x Format MFM, use standard sector numbering begining with sector x. C0 192 Format MFM, use included sector numbering table. ............................................................
{type} byte values are based on: Bit 7 6 5 4 3 2 1 0 switches <-- starting sector # --> M Z x x x x x x Note that the starting sector # (x) is a 5 bit value and can range from 0 to 63. For MFM disks, M is always 1.
C-128 CP/M has automatic read and write support for a number of standard MFM type CP/M disk formats when used with a 15
Burst Read Protocol Burst mode reading can be very fast, up to 4000 bytes per second or more. This is considerably faster than the normal 1571 "fast" mode (via the KERNAL) of about 1600 bytes per second and the 1541 (or 1571 slow mode) rate of about 350 bytes per second. (When you take into account the "overhead" such as disk initialization, track to track jump time and sector seeking time, the effective burst mode speed is about 2200 bytes per second while the average value for 1571 fast mode is about 1100 bytes per second.) Only the C-128 has the necessary hardware for burst mode data transfer. Therefore, the procedures described in this chapter, apply to the C-128 only. For other computers, data can be transferred using direct memory reads and writes.
During normal data transfers (e.g. those using GET#'s, LOAD, etc) between a C-128 and a 1571 or 1541 drive, a significant fraction of time is taken up by the convoluted path through the computer's KERNAL ROM and drive's DOS ROM that must be followed for each byte to be sent. Burst mode eliminates much of this inefficiency by sending data based on a much simpler, and therefore much faster, hardware handshake. Data are exchanged directly between the data register of the complex interface adaptor (CIA) of the 1571 and the data register of the C-128 CIA#1 based on a simple signal from the normal C-128 serial bus controller (CIA#2). (It should be noted that 1571 "fast" mode is actually based on burst mode but is accessed via the KERNAL. The tortuous KERNAL path results in slower access speeds because of the software overhead.) There are five simple steps to performing a burst mode read operation. These are:
(a) open the command channel and log in the disk (if required). (b) send the appropriate command string to access a burst mode read; (c) initialize the C-128 CIA's; (d) read the data; and (e) restore the default I/O devices.
Burst Read Setup The first step can be performed by either machine language or BASIC statements. If you are reading a given disk for the first time using burst SECTOR READ or FAST LOAD, you must first "log in" the disk using either the "INQUIRE DISK", "INQUIRE STATUS" or "QUERY DISK FORMAT" commands. This sets up the 1571 electronics to read the particular format, whether it be GCR or an MFM format. A typical BASIC statement may be as follows: OPEN 15,8,15,"U0"+CHR$(10) This particular example will OPEN the disk command channel, then ask the 1571 to use the QUERY DISK FORMAT command to analyze the format of the first track on side 0 of a disk.
The equivalent in simplified assembly language might look like: LDX #$00 ; GO TO BANK 15 STX #FF00 LDA #$0F ; FILE NUMBER LDX #$08 ; DEVICE NUMBER LDY #$0F ; CHANNEL NUMBER JSR $FFBA ; (KERNAL SETLFS ROUTINE) LDA #$00 ; NO FILE NAME JSR $FFBD ; (KERNAL SETNAM ROUTINE) JSR $FFC0 ; (KERNAL OPEN ROUTINE) LDX #0F ; FILE NUMBER JSR $FFC9 ; (KERNAL CHKOUT ROUTINE) LDA #$55 ; ("U") JSR $FFD2 ; (KERNAL BSOUT ROUTINE) LDA #$30 ; ("0") JSR $FFD2 LDA $#0A ; (CHR$(10)) JSR $FFD2 JSR $FFCC ; (KERNAL CLRCHN ROUTINE) The first two instructions are the machine language equivalent of BASIC's BANK 15 statement. This procedure should be used in machine langauge whenever you want to call KERNAL routines because they are all located in BANK 15. Attempting to call one while in another BANK will probably cause a crash. All of the C-128 KERNAL addresses are the same as for other Commodore computers. (The C-128 also has several new KERNAL routines, one of which is of interest for burst mode. The SPIN/SPOUT routine, which is used for burst writes, will be discussed in Chpater 11.) The BANK switching is handled automatically by BASIC before using the OPEN statement (or any other BASIC command). The remainder of the routine performs standard KERNAL type serial port output.
If the command channel is already OPEN, you can use the following in BASIC, or its machine language equivalent: PRINT #15,"U0"+CHR$(10) If there is a possibility that the disk drive connected to the C-128 is not a 1571 or that the 1571 has been set to 1541 mode, you should test bit 6 of the FAST SERIAL flag (RAM location $0a1c, decimal 2588) before proceeding with the actual burst mode operations. If this bit is set after an OPEN operation (either BASIC or KERNAL) then the drive is a FAST device (i.e. a 1571 or 1581 in FAST mode) capable of handling burst mode. The value of the FAST SERIAL flag should be greater than or equal to $40 (decimal 64) for burst mode transfers. With current ROM versions, it will be equal to this value. In BASIC, the test could be similar to: 100 IF PEEK(2588) AND 64 <> 64 THEN PRINT"NOT A 1571 DRIVE":END The equivalent in machine language might look like: LDA $0A1C ; FAST FLAG AND #$40 ; MASK BIT 6 BEQ (TO ERROR AND EXIT ROUTINE) (CONTINUE WITH CODE) This test should be done to prevent a lock up or crash if burst mode is attempted on a slow device such as a 1541. Note that "FAST" and "SLOW" used in this context refer to the serial bus speed and not to BASIC 7.0's FAST and SLOW commands.
If you are interested in the values returned by the QUERY DISK FORMAT or INQUIRE DISK commands, you will have to read them via burst mode. If you are not interested in any of the parameter values (i.e. they are already known to you), you can continue without reading them. The command will be cancelled with the next command that you send.
Sending the Command String
After the disk has been logged in, you can send one of the burst mode SECTOR READ or FAST LOAD commands, along with its parameters, to the drive using a standard BASIC PRINT# or KERNAL CHROUT (BSOUT) type routine. It should be noted that you must not try to read the disk drive error channel between sending a burst mode read command and actually reading the data. If you do so, you will cancel the read command and probably lock up the computer because it will wait for burst mode data from the drive which will never come.
Reading the Data The first step of the read sequence is to initialize the interupt register of the C-128's CIA#1 and tell the serial bus that you are ready to receive data. This should be done immediately after sending the burst mode command and only using machine language. (A short machine language program can easily be poked into RAM from BASIC and called with a "SYS" statement.) The machine language instructions are as follows: SEI ; DISABLE INTERUPTS BIT $DCOD ; WAIT FOR CIA LDA $DD00 ; GET "CLOCK" VALUE EOR #$10 ; FLIP IT STA $DD00 ; AND SET IT If this procedure is being called as a subroutine from either BASIC or machine language, then you will need to add an "RTS" after the last instruction to return to the calling program. The first instruction disables the normal processor interupts such as keyboard scanning, etc. This has the effect of increasing the amount of time that the hardware can dedicate to data transfer and eliminating the trapping of interupts which may cause errors during data transfers. The "BIT" instruction is used to reset the interupt control register (ICR) of CIA#1. Alternatively, a LDA $DC0D can also be used to clear the ICR. The final three instructions toggle the state of the acknowledge and ready for data (ARFD) line which is used as a clock (or handshake signal) during the burst transfer. This is a signal to the 1571 that we are ready to recieve data. The next step is to read the actual burst data. Again, this can only be done in machine language, both for speed and simplicity. The subroutine for reading burst data bytes is quite simple: LDA #$08 ; BIT 3 VALUE WAIT BIT $DCOD ; WAIT LOOP BEQ WAIT ; TILL BIT SET LDA $DD00 ; TOGGLE CLOCK EOR #$10 STA $DD00 LDA $DC0C ; GET DATA RTS The first three instructions create a wait loop until bit 3 of CIA#1's ICR is turned on. This indicates that a byte has been received. The next three instructions toggle the state of the ARFD line causing the next data byte to be transferred (or to signal that the final byte has been received). The final two instructions read the data byte from the CIA#1 data register and return it to the calling program in the "A" register. To store the returned byte, an indexed "STA" instruction similar to: STA ($FA),Y is normally used (assuming zero page locations $FA and $FB contain the low and high bytes of the data buffer address and the Y register is used as an index). In order to use the I/O block and KERNAL routines, the C-128 must be set for BANK 15. Unfortunately, this also limits the maximum size of a data buffer to 8 k bytes (BANK 0 RAM below $4000 is visible in BANK 15 also.) This can be overcome by "playing with" the memory management unit (MMU) configuration register ($FF00 - all BANKs) to switch between BANK 0 and BANK 15 "on the fly". Your machine code must be in an area visible to both BANKs (i.e. below $4000 such as the cassette buffer) for this to work. In this case, the indexed "STA" instruction mentioned above should be replaced with: LDX #$3F ; MMU CONFIGURATION FOR BANK 0 STX $FF00 ; SET BANK 0 STA ($FA),Y ; STORE DATA LDX #$00 ; BACK TO BANK 15 STX $FF00 The first two instructions set the C-128 to BANK 0. The data byte is then stored in the correct BANK 0 location. The last two instructions switch back to BANK 15. This simple technique allows you to use up to about 60 k bytes of BANK 0 as a data buffer (all of it except for the overhead such as screen RAM, system use areas, etc. and your BASIC or machine language program). There is no need to protect the unused RAM in BANK 0 from being overwritten by BASIC variables (they are in BANK 1) but don't forget to start your buffer above any machine language or BASIC program that may be occupying BANK 0 (including the hi-res graphics screen if used). The read subroutine is often called from an indexed loop, especially when reading blocks of data. It is important that you keep track of the number of bytes transferred and that your indexing method can handle the number of bytes involved. (Remember that the number of bytes transferred for a sector read is 1 + the number of bytes per sector. Commodore 1571 type GCR disks have 256 bytes per sector, while MFM format disks may have sector sizes of 128, 256, 512, or 1024 bytes per sector. FAST LOAD GCR sectors have 254 bytes per sector. The number of bytes transferred for other burst mode commands depends on the command. In addition, the number of sectors that can be transferred with a multi sector read (except for FAST LOAD) should be restricted to one track's worth. Although the multi sector read command can handle a larger number than one track worth, the specified number of sectors will be read from the same track. That is, the read head will re-read sectors on the same track until the specified number of sectors has been read. This is normally a waste of time and buffer space. A summary of annotated assembly language routines needed to read each of the burst mode commands is given in TABLE 10-1. These routines can be entered directly on the C-128 with its built in MONITOR command by replacing the labels (wait, next, etc.) with absolute addresses and removing the comments. The listed routines can be easily converted for use with most assemblers as well. The most convenient location for the ml is the cassette and RS-232 buffers beginning at $0B00 (dec 2816). The combined buffer space gives you 768 bytes for machine language. An alternate location is the unused area of BANK 0 RAM from $1300 to $1BFF (dec 4864 to 7167).
End of Transmission The final step after all data have been transferred, processed, stored etc. is to close the disk channel and restore the default input and output devices. In machine language, this is done with: CLI JSR $FFCC ; (KERNAL CLRCHN ROUTINE) LDX #$0F ; COMMAND CHANNEL JSR $FFC3 ; CLOSE IT It is very important to include the "CLI" instruction. This re-enables the processor interupts which were turned off by the initial "SEI" instruction. In BASIC, the file can be closed with a: DCLOSE #{file#} or equivalent type of statement. This automatically will do all of the required operations. That in a nutshell is how to read data in burst mode.
TABLE 10-1: SUMMARY OF ASSEMBLY LANGUAGE BURST MODE READ ROUTINES
General read-a-burst-byte routine (used by all subroutines below)
READ1 LDA #$08 WAIT BIT $DC0D BEQ WAIT ;WAIT FOR BIT 3 OF CIA#1 ICR READ2 LDA $DD00 EOR #$10 ;TOGGLE CLOCK STA $DD00 LDA $DC0C ;GET DATA BYTE RTS NOTE: Before using any of the following routines, you must load zero page locations $fa and $fb with the low and high bytes of the start of your data buffer and call the appropriate burst mode command. Single Byte Read: (used for INQUIRE DISK, INQUIRE STATUS and read SECTOR INTERLEAVE) LDY #$00 ;RESET POINTER SEI ;DISABLE INTERUPTS BIT $DCOD ;CLEAR CIA#1 ICR JSR READ2 ;SIGNAL WHEN READY JSR READ1 ;READ BYTE STA ($FA),Y ;STORE BYTE CLI ;RESTORE INTERUPTS JSR $FFCC ;CLEAR I/O CHANNELS RTS Multi byte read: (used for QUERY DISK FORMAT) LDY #$00 ; RESET INDEX SEI BIT $DC0D ; LETS GO JSR READ2 JSR READ1 JSR STORE CMP #$02 ; CHECK DISK TYPE BCC EXIT ; GCR DISK AND #$0E CMP #$00 ; CHECK ERRORS BNE EXIT ; MFM ERROR JSR READ1 ; READ ANOTHER STATUS BYTE JSR STORE AND #$0E CMP #$00 ; CHECK ERRORS FOR SECOND BYTE BNE EXIT ; MFM ERROR JSR READ1 ; # SECTORS/TRACK JSR STORE JSR READ1 ; # LOGICAL TRACK FOUND JSR STORE JSR READ1 ; MINIMUM SECTOR # FOUND JSR STORE JSR READ1 ; MAXIMUM SECTOR # FOUND JSR STORE JSR READ1 ; CP/M HARD INTERLEAVE JSR STORE EXIT CLI ; EXIT JSR $FFCC RTS STORE STA ($FA),Y ; STORE BYTE SUBROUTINE INY ;INCREMENT POINTER RTS Read N sectors of data: ; 128 BYTE MFM SECTORS: LDX #NUMBER_OF_SECTORS ; SET COUNTER STX $FC ; AND SAVE IT LDX #00 ; #SECTORS ACTUALLY READ STX $FD ; AND SAVE IT SEI BIT $DC0D ; LETS GO JSR READ2 NEXT2 LDY #$00 JSR READ1 ; READ STATUS BYTE AND #$0E ; MASK STATUS BITS CMP #$00 BNE END ; END IF ERROR NEXT1 JSR READ1 ; ELSE READ DATA BYTES LDX #$3F ; GOTO BANK 0 STX $FF00 STA ($FA),Y ; SAVE DATA LDX #$00 ; GOTO BANK 15 STX $FF00 INY ; NEXT BYTE CMP #$80 ; END OF SECTOR? BNE NEXT1 ; GET NEXT BYTE LDX $FD ; NEXT SECTOR INX CPX $FC ; LAST SECTOR? BEQ END STX $FD TYA ; SET FOR NEXT 128 BYTES CLC ADC $FA ; INC PNTR 128 BYTES BCC NEXT2 ; READ NEXT SECTOR INC $FB JMP NEXT2 END CLI ; EXIT JSR $FFCC RTS ; 256 BYTE GCR OR 256*N BYTE MFM SECTORS: LDX #NUMBER_OF_SECTORS ; #SECTORS TO READ STX $FC LDX #$00 STX $FD LDX #SECTOR_SIZE/256 ; NUMBER OF PAGES IN SECTOR STX $FE ; SAVE TWICE STX $FF SEI ; RESET AND GO LDY #$00 BIT $DC0D JSR READ2 NEXT2 JSR READ1 ; CHECK STATUS AND #$0E CMP #$00 BNE END ; END IF ERROR NEXT1 JSR READ1 ; READ DATA LDX #$3F STX $FF00 STA ($FA),Y LDX #$00 STX $FF00 INY CPY #00 BNE NEXT1 LDX $FE ; CHECK FOR ALL PAGES READ DEX STX $FE ; NEXT PAGE INC $FB CPX #00 ; END OF SECTOR? LDX $FF ; RESET PAGE COUNT STX $FE BNE NEXT1 LDX $FD ; CHECK SECTORS READ INX CPX $FC ; LAST SECTOR? BNE NEXT2 END CLI JSR $FFCC RTS ; FAST LOAD ENTIRE FILE (254 BYTE GCR SECTORS): SEI ; RESET AND GO BIT $DC0C JSR READ2 NEXT2 JSR READ1 ; READ STATUS STA $FC CMP #$02 BCS LAST ; LAST SECTOR IN FILE? NEXT JSR READ1 ; READ DATA LDX #$3F STX $FF00 STA ($FA),Y LDX #$00 STX $FF00 INY CPY #$FE ; ONLY 254 DATA BYTES TYA CLC ADC $FA ; SET POINTERS STA $FA BCC NEXT2 ; CARRY SET? INC $FB ; INCREMENT PAGE JMP NEXT2 LAST JSR READ1 ; GET # BYTES IN LAST SECTOR LDY #$00 NEXT3 JSR READ1 ; READ LAST SECTOR LDX #$3F STX $FF00 STA ($FA),Y LDX #$00 STX $FF00 INY CPY $FC ; LAST BYTE? BNE NEXT3 CLI JSR $FFCC RTS
Burst Write Protocol The 1571 disk drive Burst Command Instruction Set (BCIS) contains a single command for writing data to a disk. The burst WRITE SECTOR command is somewhat analogous to the standard Commodore DOS "Sector-Write" ("ub:" or "u2:") command. Unfortunately, there is no "FAST SAVE" command (corresponding to the burst mode "FAST LOAD" command) which would allow you to write an entire file in burst mode. Similar to most other burst mode commands, the write command will work with either MFM or GCR disks. The burst write is also faster than normal KERNAL controlled writing to the 1571, although the difference in speed is not as great as the difference in reading speeds. The average speed for a burst write using 256 byte sectors is about 500 bytes per second. The corresponding figure in normal 1571 mode is about 400 bytes per second and in 1541 mode it is about 300 bytes per second. In 1571 and burst modes, the write speeds are a factor of 3 to 5 slower than the corresponding read speeds. The main reason for this is because all sector write operations (including burst mode) are followed by a sector read to verify that the sector was written correctly. In order to do this, the drive must wait until the just written sector is positioned under the head again. This requires one full disk revolution, or about 0.2 seconds at 300 RPM, of overhead or dead time for each sector written. Unlike DOS's Sector-Write, burst write can be used to write multiple sectors in succession (up to one track worth). This Chapter gives a step by step procedure for writing data to disks in burst mode. There are six basic steps to follow for a burst mode write operation. These are: (a) log in the disk and send the burst write command string; (b) set the serial port to fast output mode; (c) send the data; (d) set the serial port to fast input mode; (e) read the burst status byte (repeat steps (b) to (e) for a multi sector write); (f) restore default I/O.
Burst Write Setup
Similar to the burst mode read described in 1571-7.TXT, the easiest way to log in the disk is to use the burst mode INQUIRE DISK command by sending the command string: "U0"+CHR$(4) This command will normally return a burst status byte which indicates the condition on the drive controller. If you are interested in its value, the status byte can read using the technique outlined in 1571-7.TXT. If you are not interested in the status byte (i.e. you are sure of the type of disk in the drive) you can ignore the status byte by sending the burst WRITE command string immediately following the INQUIRE DISK command. For a write operation, it may be a good idea to read the status byte to check that you do have the correct disk in the drive. This is more important than for a read operation, because if you do not have the correct disk, you can seriously damage the data on the disk that you do have if you are not careful.
The command string for a burst write is: "U0"+CHR$(xx)+CHR$(track#)+CHR$(sector#)+CHR$(# of sectors) +CHR$(next track) where xx can have the following values: Dec Hex 2 2 for a write to a GCR disk (either side) or MFM disk (side 0), stop writing if error detected; 18 12 same as above but for MFM disk side 1; 66 42 same as value 2 but ignore errors; 82 52 same as value 18 but ignore errors. The "track#" and "sector#" are used to specify where you want to begin reading on the disk. As with the burst sector read command, the maximum number of sectors which can be written is equivalent to one track. The actual number depends on the disk format (for MFM disks) or the track number (for GCR disks). If you try to write more than one track worth of sectors, you will overwrite sectors on the same track until the number of sectors specified by the "# of sectors" parameter have been written. This could hopelessly corrupt your disk, so be careful when specifying the number of sectors to write. The next sector to be written for multi sector writes depends on the value of the burst mode SET SECTOR INTERLEAVE command. For example, if an interleave of 3 was specified, and the first sector written was #1, then the next sector to write in a multi sector transfer would be #4. The "next track" parameter is useful if you are writing several tracks in succession. Normally before a write or read operation, the disk head will return to its "park" position before going to the specified track and sector. If you specify the next track option, the head will go to this next track directly without going to park first. This saves both time, and wear and tear on your drive by preventing the head from bouncing around like a yoyo. Both the INQUIRE DISK and WRITE command string can be sent via either a BASIC PRINT# statement or a machine language CHROUT routine after an appropriate OPEN statement.
Writng Data The next step is to change the fast serial port direction from the default "input" mode (data flow from the 1571 to the C-128) to "output" mode (data flow from the C-128 to the 1571) and set up the initial clock state. This is done with a short machine language routine using the new C-128 KERNAL SPIN/SPOUT routine (Serial Port INput/Serial Port OUTput). To set the mode to output (SPOUT), the routine is called with the carry flag set: SEI ; (DISABLE INTERUPTS) SEC JSR $FF47 ; (KERNAL SPIN/SPOUT ROUTINE) LDA #$40 STA CLOCK The last two instructions start the test for the system clock state on a high value. The label "CLOCK" refers to any usable RAM location (such as zero page $FA to $FF) which is used in subsequent steps as a temporary storage location for testing the state of the system clock. Once the system has been initialized, the data can be sent. Similar to the read protocol, data are sent to the 1571 based on a simple toggle handshake using the Acknowledge and Ready for Data (ARFD) line. The procedure is as follows: LDY #$00 ; (RESET BUFFER DATA INDEX) WAIT1 LDA $DD00 ; (READ ARFD CLOCK STATE) CMP $DD00 ; (DEBOUNCE) BNE WAIT1 EOR CLOCK AND #$40 ; (CHECK STATE OF ARFD CLOCK) BEQ WAIT1 LDA ($FA),Y ; (GET DATA BYTE FROM RAM BUFFER) STA $DC0C ; (SEND DATA) LDA CLOCK EOR #$40 ; (TOGGLE STATE OF "CLOCK" REGISTER)
STA CLOCK WAIT2 LDA #$08 BIT $DC0D ; (WAIT UNTIL BYTE SENT) BEQ WAIT2 INY BNE WAIT1 ; (START OVER FOR NEXT BYTE) The above routine assumes a 256 byte sector of data is to be transferred (remember MFM sectors can be 128, 256, 512 or 1024 bytes while GCR sectors written with this command are always 256 bytes). Indexing routines for other sector sizes are given in Table 11-1. The first instruction resets the data buffer index. It is assumed that the data buffer address is stored in zero page locations $FA and $FB in standard low byte, high byte format. The next six instructions form a wait loop until the serial port clock pulse is in the correct phase. The next two instructions retreive the data byte from memory and send it to the 1571 via the CIA data registers. Because the size of the data buffer in BANK 15 (the default BANK for I/O operations) is limited, the "LDA ($FA),Y" instruction can be replaced with: LDX #$3F ; GO TO BANK 0 STX $FF00 LDA ($FA),Y ; GET DATA LDX #$00 ; BACK TO BANK 15 STX $FF00 This allows you to use most of BANK 0 free RAM as a data buffer. The next group of three instructions toggles the state of the clock comparison register. The three instructions beginning with the "WAIT2" label form a loop until the interupt control register (ICR) of CIA#1 signals that the transmission of the data byte is complete. The final two instructions increment the buffer pointer and repeat the process for the next byte until a complete sector has been sent.
Write Status The fourth step in the write process is used to check that the data have been successfully written to the 1571. The 1571 returns a status byte after each sector has been written. To read this byte, the fast serial port must first be set to the read (SPIN) direction followed by a sending ready signal to the 1571. This is done with: CLC ; (CLEAR CARRY FLAG) JSR $FF47 ; (SET SPIN/SPOUT TO SPIN) BIT $DC0D ; (RESET CIA ICR) LDA $DD00 ORA #$10 ; (SET ARFD CLOCK LOW) STA $DD00 The status byte can then be read with a standard burst mode read:
LDA #$08 WAIT3 BIT $DCOD ; (WAIT FOR BYTE INTERUPT) BEQ WAIT3 LDA $DC0C ; (READ STATUS) STA $FA ; (STORE IT SOMEWHERE IF YOU WANT) LDA $DD00 AND #$EF ; (SET ARFD CLOCK TO HI) STA $DD00 If more sectors are to be written, the whole process starts over again from step 2 (set serial port to SPOUT) until the specified number of sectors have been written.
Closing Up
Once all sectors have been written, the final step is to restore default input/output (I/O) channels: CLI JSR $FFCC ; (KERNAL CLRCHN ROUTINE) If no longer required, the command channel can also be CLOSEd at this point. That, my friends, is all there is to writing in burst mode.
TABLE 11-1: SUMMARY OF ASSEMBLY LANGUAGE BURST MODE WRITE ROUTINES
General write-a-burst-byte routine (used by all subroutines below) WRITE LDA $DD00 CMP $DD00 ;DEBOUNCE ARFD CLOCK BNE WRITE EOR $0D00 ; TEMP STORAGE FOR CLOCK STATE AND #$40 BEQ WRITE LDX #$3F ;SWITCH TO BANK 0 STX $FF00 LDA ($FA),Y ;GET DATA LDX #$00 ;BACK TO BANK 15 STX $FF00 STA $DC0C ;SEND DATA LDA $0D00 ; TOGGLE TEMP CLOCK EOR #$40 STA $0D00 LDA #$08 WAIT BIT $DC0D ; WAIT TILL BYTE SENT BEQ WAIT RTS ; READ STATUS BYTE ROUTINE READST CLC JSR $FF47 ;SET SPOUT BIT $DC0D ;RESET ICR LDA $DD00 ORA #$10 ;SET ARFD INPUT STA $DD00 LDA #$08 WAITR BIT $DC0D BEQ WAITR ;WAIT FOR BYTE LDA $DC0C ;GET STATUS STA $0D01 ;STASH IT LDA $DD00 AND #$EF ;RESET AFRD STA $DD00 RTS NOTE: before using any of the following routines, you must load zero page locations $FA and $FB with the low and high bytes of the start of your data buffer and call the appropriate burst mode command. Location $0D00 in the RS-232 buffer is used as a temporary storage register for testing the clock phase. $0D01 is used to store the status byte if desired. ; WRITE N 128 BYTE SECTORS: LDX #NUMBER_OF_SECTORS_TO_WRITE STX $FC LDX #$00 STX $FD ;# SECTORS WRITTEN LDA #$40 STA $0D00; TEMP STORAGE SEI NEXTS LDY #$00 ; RESET INDEX SEC JSR $FF47; SET SPOUT NEXTB JSR WRITE INY CPY #$80; END OF SECTOR BNE NEXTB JSR READST; READ STATUS LDX $FD INX CPX $FC ; LAST SECTOR? BEQ END STX $FD TYA CLC ADC $FA ; INCR PNTR 128 BYTES STA $FA BCC NEXTS ; READ NEXT SECTOR INC $FB JMP NEXTS END CLI JSR $FFCC ; CLRCHN RTS ; WRITE N 256 BYTE SECTORS: LDX #NUMBER_OF_SECTORS_TO_WRITE STX $FC LDX #$00 STX $FD ;# SECTORS WRITTEN LDA #$40 STA $0D00; TEMP STORAGE SEI NEXTS LDY #$00 SEC JSR $FF47; SET SPOUT NEXTB JSR WRITE INY CPY #$00; END OF SECTOR BNE NEXTB JSR READST; READ STATUS LDX $FD INX CPX $FC ; LAST SECTOR? BEQ END STX $FD INC $FB JMP NEXTS ;READ NEXT SECTOR END CLI JSR $FFCC RTS ; WRITE N 512 OR 1024 BYTE SECTORS: LDX #NUMBER_OF_SECTORS_TO_WRITE STX $FC LDX #$00 STX $FD ;# SECTORS WRITTEN LDA #$40 STA $0D00; TEMP STORAGE LDX #SECTOR_SIZE/256 STX $FE ; SAVE IT TWICE STX $FF SEI NEXTS LDY #$00 SEC JSR $FF47; SET SPOUT NEXTB JSR WRITE INY CPY #$00; END OF PAGE? BNE NEXTB LDX $FE DEX STX $FE INC $FB CPX #$00 ;END OF SECTOR? BNE NEXTB JSR READST; READ STATUS LDX $FF STX $FE LDX $FD INX STX $FD CPX $FC ; LAST SECTOR? BNE NEXTS END CLI JSR $FFCC RTS
Burst Mode and BASIC
This document deals with accessing MFM disks and interfacing burst mode with BASIC and/or standard machine language KERNAL input/output routines. This is done by reading or writing directly from or to the memory of the 1571 drive. A number of important 1571 memory locations are summarized in Table 12-1. Of course, this method will not be as fast as full burst mode. For example, to read the burst mode status byte, the following statement can be used in a short BASIC program (remember INPUT# and GET# can not be used in direct mode): OPEN 15,8,15,"M-R"+CHR$(94)+CHR$(0)+CHR$(1):GET#15,A$ The ASCII value of A$ represents the burst mode status byte.
Bit 7 of the first byte of the burst mode command string (after the "u0") controls the transfer of data between the computer and the 1571. If this bit is set, then data transfer occurs via direct memory reads and writes to the 1571 RAM buffers. One sector worth of data for the burst mode READ/WRITE SECTOR commands can be transferred using conventional BASIC or machine language through the disk drive command channel. When in burst mode, buffers 0 to 3 (address $300 to $6FF) in the 1571 are used for data storage. Four buffers are allocated for transfer because MFM sectors can be up to 1024 bytes long. The buffer is filled starting at address $300. To read an MFM sector in BASIC, the following command sequence can be used: 1) OPEN the command channel: OPEN 15,8,15 2) Read sector into 1571 buffer: PRINT#15,"U0"+CHR$(A)+CHR$(track#) +CHR$(sector#)+CHR$(1) where "A" has a value of 192 to read a sector on side 0 or 208 to read a sector on side 1 of an MFM disk. 3) Extract data from 1571 buffer with a memory-read: PRINT#15,"M-R"+CHR$(BL)+CHR$(BH)+CHR$(0) where BL is the low byte of the 1571 buffer address (0) and BH is the high byte (starts at 3) of the address. The data can then be read with a GET#15,A$ in an indexed loop and stored in C-128 memory with a command similar to POKE nnn,ASC(A$). Because only 256 bytes can be read this way at one time, the M-R must be repeated at 1571 buffer addresses of $400, $500, and $600, if the MFM sector size is 512 or 1024 bytes. 4) CLOSE the command channel: CLOSE 15 The reverse procedure is used to write data to an MFM disk: 1) OPEN the command channel: OPEN 15,8,15 2) Transfer data from the C-128 to the 1571 with a memory-write: PRINT#15,"M-W"+CHR$(BL)+CHR$(BH)+CHR$(32); Then PRINT#15 the data in an indexed loop. Because the M-W command is limited to a maximum of only 34 bytes at a time (restricted by the length of the 1571 command input buffer), the M-W must be repeated, incrementing the buffer pointers, BL and BH, each time (as shown above it is easiest to work in increments of 32 rather than 34 because 256=8 x 32) until one sector has been transferred. 3) Write sector from buffer to disk: PRINT#15,"U0"+CHR$(B)+CHR$(track#)+CHR$(sector#)+CHR$(1) where "B" has a value of 194 to write to side 0 or 210 to write to side 1 of an MFM disk. 4) CLOSE command channel: CLOSE 15 Although the above examples have been given in BASIC for clarity, the same procedure can be followed using equivalent machine language KERNAL calls. By using simple memory read and memory write commands to access the RAM of the 1571 disk drive, even a novice programmer can unlock the secrets of MFM disks with ease! The techniques outlined above can be used with any Commodore computer, a C-64, Plus/4, even a VIC-20, to access the burst mode commands on the 1571.
TABLE 12-1: Useful 1571 Memory Locations ............................................................... Address Function ............. Hex Dec ............................................................... 24-29 36-41 ID field of last MFM sector accessed 24 36 track# 25 37 0=side 0, 1=side 1 26 38 sector# 27 39 sector size: 0=128 bytes, 1=256, 2=256, 3=512, 4=1024 28-29 40-41 CRC (checksum) 37 55 Bus status byte: bit 0 1="file has only one sector" 3 inverse status of clock line 6 1=1571 bus mode, 0=1541 bus mode 7 1=1571 operating mode (2 MHz) 0=1541 operating mode (1 MHz) 3B 59 Last burst mode command number 3C 60 MFM disk sector interleave 5E 94 Burst status byte: bit 0-3 current error message number returned by job loop 7 1=MFM, 0=GCR 5F 95 Current job code 60 96 Smallest sector number on track 61 97 Largest sector number on track 67 103 Current track number 97 151 Number of MFM sectors per track 200- 512- Input buffer for command strings 229 553 from computer 274 628 Length of command string in input buffer 300- 768- RAM buffers: $300 is the start of 7FF 2047 buffer for MFM sector reads/writes 1800- 6144- 6522 VIA# 1: Controls 1541 bus and 180F 6159 electronics 1C00- 7168- 6522 VIA# 2: Controls GCR recording 1C0F 7177 electronics, motor, etc. 2000- 8192- WD 1770 : Controls MFM recording 2003 8195 electronics 4000- 16384- 6526 CIA# 1: Controls 1571 fast bus mode 400F 16399 8000- 32768- ROM 32K disk operating system FFFF 65535
AUTO BOOTING on the C-128 with a 1541 or 1571 Drive
The C-128 has the ability to automatically load and run either a machine language or BASIC program from disk when it is first turned on or the reset button is pressed. In this process, known as AUTOBOOTING, the computer loads track 1, sector 0 into memory at address 2186 in bank 0 (part of the BASIC stack area) and checks it for a certain combination of bytes. If the byte pattern is matched, the machine language instructions in the remainder of the sector are executed. This feature works with both the 1571 and 1541 disk drives, as well as with most other compatible Commodore and non-Commodore drives.
For AutoBooting, the first three bytes of track 1, sector 0 must be the characters "CBM" followed by four CHR$(0)s. This can be followed by an optional message to be displayed during boot-up of up to 16 characters. The message is followed by two more CHR$(0)s. This sequence is followed by some machine language instructions and a BASIC command string consisting of either RUN"filename" or BOOT"filename" for BASIC or macine language programs, respectively. The final byte is another CHR$(0). The machine language instructions are:
LDX #address low LDY #address high JMP AEA5
where address low and address high are the low and high bytes of the beginning of the RUN or BOOT string that follows. This address may change depending on the length of the boot message string. The JMP instruction goes to the area of ROM for handling BASIC commands.
Listing one, AUTOBOOT MAKER is a short BASIC 7 program which allows you to write the required byte pattern to make a boot disk. One word of caution, however. Since the program uses the random disk block write method to write the boot sector, the boot disk should not be subjected to the DOS VALIDATE or BASIC 7 COLLECT command. Either of these commands may cuase the boot sector to be overwritten and in any case will cause it to become un-allocated. AUTOBOOT MAKER will warn you if it finds track 1, sector 0 already allocated. If you get such a message, you can either overwrite the existing sector or try another disk. If the disk already has a boot sector written, the old data will be replaced by the new. In any event, it is perhaps better to write a boot sector on a new disk immediately after formatting it. This ensures that the sector does not have any valid data on it.
Interestingly, the BASIC command string used in the boot sector is not limited to BOOT"filename" or RUN"filename", but can be any valid immediate mode BASIC command. By changing lines 70 and 80 of the program to contain the appropriate command string such as:
70 BO$=BO$+"DIRECTORY"+CHR$(0)
you could have an automatic display of the disk directory on boot up. Other useful applications include DLOADing a program (without running it); BLOADing a machine language utility, sprite data, function key definitions, custom title screen, etc.; or perhaps merely changing the screen colors with a couple of COLOR commands. The only apparent restriction on the command string is that its total length must be less than one BASIC line and must contain only one logical line (i.e. no CHR$(13)s). All statements must be immediate mode (i.e. INPUT, GET etc. are not allowed).
Of course, most of the above applications (except for the DLOAD) can be performed by a RUN"filename" type command string. However, while these programs are easily accessible to prying eyes from the disk directory, a person needs a disk sector editor to view or change the contents of a custom boot sector command string.
LISTING One:
10 BO$="CBM":C0$=CHR$(0):FORI=1TO4:BO$=BO$+C0$:NEXT:COLOR0,7:COLOR4,7:PRINT" " 20 PRINT"BOOT MESSAGE":INPUT" MAX 16 CHARS";ME$:ME$=LEFT$(ME$,16): SA=2831+LEN(ME$) 30 INPUT" BOOT FILE NAME";BF$:INPUT" {B}ASIC OR {M}ACH. LANG.";FT$:PRINT" " 40 PRINT" BOOT MESSAGE: "ME$:PRINT" BOOT FILE NAME: "BF$;: IFFT$="M"THENPRINT" MACH. LANG.":ELSEPRINT" BASIC" 50 INPUT" OK {Y/N}";O$:IFO$<>"Y"THEN10 60 BO$=BO$+ME$+C0$+C0$+CHR$(162)+CHR$(SA AND255)+CHR$(160)+CHR$(SA/256)+CHR$(76)+CHR$(165)+CHR$(175) 70 IFFT$="M"THENBO$=BO$+"BOOT":ELSEBO$=BO$+"RUN" 80 BO$=BO$+CHR$(34)+BF$+CHR$(0):OPEN15,8,15:OPEN8,8,8,"#":IFDSTHENDCLOSE:GOTO110 90 PRINT#15,"U1:";8;0;1;0:PRINT#15,"B-A:";0;1;0:IFDS=65THEN120 100 PRINT#15,"B-P:";8;0:PRINT#8,BO$;:PRINT#15,"U2:";8;0;1;0:DCLOSE:IFDSTHEN110:ELSEEND 110 PRINT" DISK ERROR >> ";DS$:GOTO40 120 PRINT" BOOT SECTOR ALREADY IN USE":INPUT" OVER-WRITE {Y/N}";OW$:IFOW$="Y"THEN100:ELSE40
Formatting MFM Disks on the 1571 Disk Drive
The 1571 disk drive is capable of reading and writing a number of standard CP/M disk formats when used with a C-128 in CP/M mode including: IBM CP/M-86 single sided IBM CP/M-86 double sided Kaypro II single sided Kaypro IV double sided Osborne single sided Epson QX-10 double sided Epson Euro single sided? and Slicer 9 among others. These are all referred to as MFM (maximum frequency modulation) formats, based on the technique used to record the data on the disk. Normal Commodore DOS (eg. 1541 format) uses a method called GCR (group coded recording) which is totally different from MFM. There are several other differences between MFM and GCR disks. MFM disks generally have the same number of sectors on each track while Commodore GCR disks usually have different numbers of sectors for different ranges of track numbers. The numbering system for MFM tracks start from 0 and go to a high value (usually 39) while the sectors on each track are numbered from 1 to a high number. This is the opposite of Commodore GCR disks where tracks are numbered from one and sectors start at zero.
Although Commodore CP/M can read and write in MFM formats with a 1571 drive, the current version of the FORMAT.COM program supplied on the CP/M system disk cannot create or format new disks in these extended disk formats. It is, however, quite simple to format disks in these or any other format you wish with a 1571 drive.
In addition to the standard Commodore DOS commands, the 1571 has a few new ones. These commands, collectively defined as "burst mode" in the 1571 manual, allow you to read, write and format disks in virtually any format you choose. Most of the commands, such as those for reading and writing data from/to the disk, need fairly sophisticated machine language programming to access because the required high speed data transfer is under totally manual control. The extended "FORMAT" command is the simplest to use. It can be quite easily invoked from BASIC because high speed data transmission is not required.
The description of the burst mode commands in the 1571 manual is at best cryptic and is often quite confusing. The syntax for the MFM FORMAT command can be simplified to: OPEN15,8,15,"U0"+CHR$(B1)+CHR$(129)+CHR$(0)+CHR$(B4)+CHR$(39)+ CHR$(B6):DCLOSE Where: B1 equals 70 for a single sided disk or 102 for a double sided disk; B4 equals 0 for 128 bytes per sector 1 for 256 bytes per sector 2 for 512 bytes per sector or 3 for 1024 bytes per sector; and B6 equals the number of sectors per track.
The actual bit patterns for the various CHR$ after the U0 (the bit patterns for the first two parameters listed in the manual, bytes 0 and 1, are equal to the characters U0 for all of the burst mode commands) are explained in detail in the 1571 manual. I have simplified them here to make the command easier to use. There are other optional parameters which allow you to partially format a disk, format different areas of the disk in a different manner, change the sector slew rate, change the fill byte etc. These can be the basis of, for example, a simple yet very effective copy protection scheme you can use on your own disks and programs. Try re-formatting track 35 on a standard 1541 DOS disk with the command: OPEN15,8,15,"U0"+CHR$(198)+CHR$(129)+CHR$(0)+CHR$(0)+CHR$(35) +CHR$(10)+CHR$(34)+CHR$(34):DCLOSE. Then see what happens when you try to copy the disk, even with a whole disk nibble type copier for protected disks. If the program you wish to protect tries a random block read or write to this track, an error will result. If the drive (either a 1541 or 1571) does not return an appropriate read error code, then disk has been formatted in GCR and is obviously not an original. The re-formatting of track 35 should be done before you copy your program to the disk. This is to ensure that no valuable program data are lost by wiping out the track. In addition, you should use the DOS Block-Allocate command to allocate all of the sectors on that track to prevent DOS from accidentally trying to write there during a legitimate SAVE or other type of operation.
Listing one, MFMFORMAT, is a short BASIC 7 program for use in C-128 native mode which allows you to effortlessly format disks in a number of different MFM formats on the 1571 drive. The data statements at the end of the program contain the required parameters (number of sectors per track, number of bytes per sector and number of sides) for each type of disk. These parameters can all be changed to create custom disk formats if you wish. However, most of the custom formats will not be supported by CP/M on the C-128. In addition, the line numbers for the DATA statments are very important due to the use of a calculated line number reference in line 30 for reading the selected data.
LISTING 1:
10 PRINT" MFM DISK FORMATTER":PRINT" BY M.GARAMSZEGHY " 20 PRINT" OPTIONS: ":FORI=1TO5:READA,A,A,A$:PRINTI": ";A$:NEXT 30 INPUT" SELECT A FORMAT";F:RESTORE(F*10+100):READ B6,BS,SD,F$ 40 PRINT" FORMAT >> ";F$:PRINT" PRESS <RETURN> TO CONTINUE" 50 PRINT" OR PRESS ANY OTHER KEY TO ABORT":GETKEYA$:IFA$<>CHR$(13)THENRUN 60 B5=39:S(0)=128:S(1)=256:S(2)=512:S(3)=1024:SI=0:IFSD=2THENSI=32 70 B1=70ORSI:FORI=0TO3:IFBS=S(I)THENB4=I:ELSENEXT 80 OPEN15,8,15,"U0"+CHR$(B1)+CHR$(129)+CHR$(0)+CHR$(B4)+CHR$(39)+CHR$(B6) 90 INPUT#15,A:DCLOSE:INPUT" FORMAT ANOTHER <Y/N>";FA$:IFFA$="Y"THEN40 100 REM DATA #SECTORS PER TRACK,#BYTES PER SECTOR,#SIDES 110 DATA 8,512,1,"IBM CP/M-86 SINGLE SIDED" 120 DATA 8,512,2,"IBM CP/M-86 DOUBLE SIDED" 130 DATA 10,512,2,"KAYPRO IV DOUBLE SIDED" 140 DATA 10,512,1,"KAYPRO II SINGLE SIDED" 150 DATA 5,1024,1,"OSBORNE SINGLE SIDED"
|
|