Discover the capabilities of A " disk drives A Data Becker Book ST Disk Drives: Inside and Out Uwe Braun • Stefan Dittrich • Axel Schramm A Data Becker Book Published by Abacus mu Second Printing, January 1988 Printed in U.S.A. Copyright © 1986 Data Becker GmbH MerowingerstraBe 30 4000 Diisseldorf, West Germany Copyright © 1987 Abacus, Inc. 5370 52nd Street SE Grand Rapids MI 49508 This book is copyrighted. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, electronic, mechanical, photocopying, recording or otherwise without the prior written permission of Abacus, Inc. or Data Becker, GmbH. Every effort has been made to insure complete and accurate information concerning the material presented in this book. However Abacus, Inc. can neither guarantee nor be legally held responsible for any mistakes in printing or faulty instructions contained in this book. The authors will always appreciate receiving notice of subsequent mistakes. Atari, 520ST, 1040ST, TOS, SH204, SF354, SF314 and ST BASIC are trademarks or registered trademarks of Atari corporation. GEM and GEMDOS are registered trademarks of Digital Research, Inc. GFA BASIC is a trademark of Gfa-Systemtechnik. MS-DOS is a registered trademark of Microsoft Corp. ST PASCAL Plus is a trademark of CCD. Lattice C is a trademark of Metacomco. Pro FORTRAN-77 is a trademark of Prospero Software Ltd. ISBN 0 - 916439 - 84-4 Table of Contents 1 Introduction 1 2 Files and programs 5 2.1 File structures and access by high-level languages 11 2.1.1 An overview of GEMDOS functions 11 2.2 File access in BASIC 14 2.2.1 BASIC command overview 14 2.2.2 The sequential file in BASIC 15 2.2.3 The random-access file in BASIC 16 2.3 File handling in Pascal 19 2.3.1 The sequential file in Pascal 19 2.3.2 Random-access files in Pascal 22 2.4 File access in C 24 2.4.1 The sequential file in C 27 2.4.2 The random-access file in C 29 2.5 File handling in FORTRAN 32 2.5.1 The sequential file in FORTRAN 32 2.5.2 The random-access file in FORTRAN 33 2.6 A s’mple database 35 3 Data structures 43 3.1 Diskette format 45 3.2 The boot sector 47 3.2.1 Formatting program 50 3.2.2 The BIOS parameter block 58 3.3 The directory 65 3.4 The FAT 68 3.5 Program construction 69 3.5.1 The program header 70 3.5.2 The relocation table 72 3.6 Hard disk format 73 4 The disk drives 75 4.1 Floppy diskette functions 77 4.2.1 The DMA chip 79 4.2.2 The disk controller 80 4.2.2.1 Pinout 83 4.2.2.2 Organization 88 4.2.2.3 Command description 96 m 4.2.2.4 Status interpretation 126 4.2.3 The floppy interface 132 4.3 Connecting the disk drives 133 5 The SH204 hard disk 137 5.1 Function and design 138 5.1.1 The hard disk controller 139 5.1.1.1 Command structure 141 5.1.1.2 List of commands 147 5.1.1.3 HDC tools 153 5.1.1.4 Partition analyzer 158 5.2 Connecting the hard disk 167 5.3 Print the complete directory 168 6 The RAM disk 177 6.1 An easy-to-use RAM disk program 181 6.2 Disk to RAM disk copy 193 7 Programming a disk monitor 199 7.1 The TOS functions for disk access 202 7.2 Listing and operation of the disk editor 210 7.2.1 The main menu 303 7.2.2 The TRACK menu 304 7.2.3 The TRACK with SYNC menu 305 7.2.4 The SECTOR menu 305 7.2.5 The CLUSTER menu 306 7.2.6 The FORMAT menu 307 7.2.7 The GAP menu 307 7.2.8 The OPTIONS menu 308 7.3 Sample use of the disk editor 309 7.3.1 File Allocation Table 313 7.3.2 Subdirectories and folders on diskette 315 7.3.3 Formatting in non-Atari format 316 7.4 Assembling with different assemblers 318 8 Machine language utilities for BASIC 319 8.1 Calling and passing parameters 321 8.2 Some example programs 323 8.2.1 B ASIC/TOS interface 323 8.2.2 Directory reader 325 8.2.3 Read/write sectors 328 8.2.4 Any disk format 330 8.2.5 Searching for data 334 8.2.7 Reading the date and time 338 8.3 Programming the FDC in BASIC 341 8.3.1 The BASIC/FDC interface program 342 8.3.2 Demo 1—All FDC commands 361 8.3.3 Demo 2—Copying disks 370 8.3.4 Demo 3—Creating standard and foreign formats 374 8.4 Creating BASIC loaders 380 Appendix 385 ASCII character set 387 Index 389 v Chapter One Abacus Atari ST Disk Drives Inside and Out Introduction The Atari ST computers are ideal for professional applications with their fast 16/32-bit processors and their large memory capacities. But equally as important as internal memory are the methods of external data storage. The floppy disks and hard disks used for storage are very interesting, complex storage media which can do much more than you would guess from reading the manuals. If you want to make optimal use of your ST, it's important to know the capabilities of the individual ST components. That is the purpose of this book. It first gives you an overview of mass storage methods and describes the procedures for writing application programs. Later chapters detail the secrets of the Atari floppy disk drives, hard disk drives and even RAM disks. All of this software and hardware knowledge lets you make the best use of these storage media. You can increase the capacity of the disks, develop a method of copy protection for your programs, and create a RAM disk to meet your own needs. With the help of the example and utility programs listed in this book, you'll be able to access your floppy or hard disk much faster and much more efficiently. In addition, this book and its optional program diskette contain some very useful programs. They include a program that prints out a complete directory, including the contents of all folders, and one that allows you to analyze diskettes or the hard disk. A special feature of this book is a complete disk monitor—a program that gives you direct access to disks, thereby allowing you to apply all of your new knowledge. You can use this disk monitor to recover deleted files, to read "foreign" disk formats and much more. You will find information in this book that doesn't appear in any ST manual or user's guide. These commands or relationships were discovered after much work with the ST disk systems. You'll soon find out that the ST disk drives can do more than you might have thought. We hope that this book helps you answer any questions you may have about mass storage on the ST, and that you find this information useful. Uwe Braun, Stefan Dittrich, Axel Schramm October, 1986 3 Chapter Two Abacus Atari ST Disk Drives Inside and Out Files and programs The two terms file and program really mean the same thing: Computer data stored on some form of external storage medium. It's true that internal memory capacity in computers is growing; for example, the Atari 1040 ST has 1 megabyte of RAM. However, the computer still must store data that is not immediately needed—whether it's a word processing program, the population of the city of Chicago, or the price of tea in China—on some external medium. Otherwise this data would be lost when the computer's power was turned off. Magnetic tape, diskettes, hard disks and CD ROMs are used as external storage media. With all of the devices that handle these media, the data is first encoded on the storage medium, and later read back into the computer's memory using electronic circuitry. A group of data stored under one name is called a file, regardless of the type of mass storage. It is imporant for the user to have at least fundamental knowledge of how the computer stores a file, whether it's an address list, a letter, or executable program code. For example, a file of stored program code may not have any separators between the individual data items. This is different from a file of stored text, where separators are often used between individual sentences such as carriage returns (i.e., the key) and punctuation marks. The type of file is indicated by its extension. An extension is three additional characters following the name, separated from the name by a period. The ST operating system distinguishes between programs and files by means of this extension. If you were to change the extension of a program from . PRG to . DAT, clicking on this name would only result in this dialog box: You can only print or display this document. Please click on appropriate button to do so. I Show I I Print I [Cancel | 7 Abacus Atari ST Disk Drives Inside and Out The extensions which the Atari can directly distinguish are: . P RG Designates an executable machine language program that can run with GEM support. . TOS Designates an executable machine language program, but GEM will be disabled while it is running. • TTP Abbreviation for TOS Takes Parameters; same as .TOS, except that before the program is executed a dialog box appears, into which you can enter parameters (such as a filename for editors). . ACC Special machine language programs known as accessories are loaded after the computer is turned on. These programs remain in memory and can be called as accessories from the Desk menu of the Desktop. . INF Used by the Desktop for DESKTOP . INF. This file contains information about the positions and sizes of the windows, the values set for the Control Panel, etc. This file is created by selecting Save Desktop from the Options menu. Other files such as BASIC programs are equipped with the extension . BAS, but this is not vital to the ST operating system. You can load a . TXT file into the BASIC interpreter, for instance, if it contains the text of a BASIC program. The other extensions are therefore not important, but they can be useful for keeping your files in order. The actual differences between file types are found in the internal construction of the files themselves. Most high-level languages distinguish between various file forms, such as those with or without separators between strings and numbers, special text modes, etc. We will look first at data files which contain only strings and numbers—that is, ASCII data. We can use various methods for finding and processing certain data in the file. The speed of access to given data on the diskette or hard disk depends largely on the "intelligence" of the file management system. This can best be shown through a concrete example. Let's say we have a file containing the addresses of all of the female inhabitants of Escanaba, Michigan. 8 Abacus Atari ST Disk Drives Inside and Out The block of information that contains complete data on an individual, like the first and last names, street address, city, state, and zip code, is called a record. A single piece of information, like the first name, is a field in the record. Breen Candace 15 Main Street Escanaba Ml 49829 555-1213 Olafsson Marian 13 Mine Street Escanaba Ml 49829 555-1212 Psmith Laureen 1 Mime Street Escanaba Ml 49829 555-1234 Taber Rosalyn 1562 120 Mi. Rd Escanaba Ml 49829 555-5555 A few of the women of Escanaba The simplest form of file is a sequential file, in which the data is stored in linear sequence, one field after the other. The program which reads this data from the file must be able to recognize the end of a record, because a separator is used only between the individual fields. Generally, every record has a different length. If you want to access the 10th record, you must read through the file from the 1st record to the 10th record. This procedure is acceptable for small files, but what if you had to find the address of Willem Zygonze from Wawatosa in a sequential file containing every inhabitant of Wisconsin? If large quantities of data must be managed, you would generally use records of a set length and random-access files. In random-access files, each field of a record has a set, predetermined size, such as 12 characters for the last name, 10 for the first name, 20 for the street address, 15 for the city, 2 for the state, and 5 for the zip code—a total of 64 characters per record. Now if you want to access the 10th record, you can calculate the start of the 10th record relative to the start of the file through simple multiplication. You then need only start to read at the 10*64=640th byte of the file. At this byte you can immediately read your data. This calculation applies only if the numbering of the data records starts with 0 and you want record number 10. 9 Abacus Atari ST Disk Drives Inside and Out This trick works only if an arbitrary location in the storage media can be directly accessed, which is not possible with audio tape, for example. This kind of access is possible with diskettes or a hard disk, because the disks themselves are divided into individual, numbered sections called tracks. Let's return to our address file example. If we know in which sector the first record (record number 0) begins, we can also calculate where the 640th byte of the file is located. Let's say that our file starts in sector number 10. On the Atari ST, each sector contains 512 bytes. Accordingly, our 10th record, or the 640th byte, is found in the 11th sector at byte 640-512, or byte 128. You don't need to bother with all this arithmetic if you're writing in a high-level language. A high-level language is any programming language except machine or assembly language. Assembly language programmers can also perform these sector calculations using the ST's operating system, because the operating system offers such a function (but more about this in Chapter 7). Building on this simple principle of direct access, there are several forms of file organization. For instance, you can sort the entire file according to one important field, such as the last name, and then write the sorted names into a separate file together with the numbers of the corresponding records. This type of file is called an index file. The result is an index-sequential fde (index file with sequential access) for which there are some very advanced search procedures. An index-sequential file can be used to find and access a given record very quickly. 10 Abacus Atari ST Disk Drives Inside and Out 2.1 File structures and access by high-level languages The operating system of a computer manages the basic operations for file handling. The various high-level languages build their file forms around this operating system management. As we already mentioned, the Atari ST disk operating system GEMDOS supports random-access files. These GEMDOS file functions will now be covered briefly, and then discussed in more detail as they are used with each high-level language. The programs which follow in BASIC, Pascal, C and FORTRAN all have the same effect: They create and read a sequential file and a random-access file. 2.1.1 An overview of GEMDOS functions Every file must be given a filename by the user. The maximum length of a filename is 11 characters. The first eight characters represent the actual filename. The last three characters after the period (which serves as a separator) represent the file extension. Extensions are necessary for the use of high-level language compilers, i.e., programs which convert the language's source text into an executable program in machine code. As the complier converts from source text to finished program, up to four files are created that have the same name, but different extensions. For example, you would write a C sourcecode with an editor and call it test 1. c. When you compile and link the program, files with the names testl. o (compiled object code) and testl . prg (the final linked running program) are created. To create a new file, GEMDOS offers the CREATE function (function number $3C). The programmer passes the desired filename to the function, as well as a special mode word that contains information about the type of file. If the file is successfully created (the disk is not write-protected, etc.), GEMDOS returns a file number which will be used for all subsequent file access. This number is called a handle. The CREATE function is called only before the very first access to a file. Later access to an existing file can be prepared for by a call to the function OPEN ($3D). When calling CREATE, an empty file with the given name is created on the current drive, and this file can then be accessed for writing. 11 Abacus Atari ST Disk Drives Inside and Out Many high-level languages incorporate the CREATE function into their OPEN commands, so that if a file is opened and it does not already exist, it will be created. To write to a file, a programmer uses the GEMDOS function WRITE ($40), passing it the filename or handle returned by CREATE or OPEN, the number of characters to be written, and the characters themselves. Once all of the data has been written to the file, it must be closed before that data can be accessed. The CLOSE function ($3E) accomplishes this. If the CLOSE function is not called, data will probably be lost, or the file's distribution on the disk will not be properly marked on the diskette. After the file has been created with CREATE, filled with WRITE, and then closed again with CLOSE, it can be opened again for reading with the OPEN function ($3D). Like CREATE, OPEN is given the filename as well as a mode word between 0 and 2. A 0 passed as the mode word opens the file for reading only. This means that data may only be read from the file. Any attempts to write to the file will result in error messages. A mode of 1 opens the file for writing only, and a 2 allows both reading and writing. The function READ ($3F) is used to read data from a file. Like WRITE, this function is given the handle and the number of characters to be read. File access with READ and WRITE is completely sequential. This means that when you open the file with CREATE, the operating system creates a pointer to the file, which is always set to zero each time the file is opened. This pointer always points to the current position in the file. For example, if you write 14 characters in this file, the operating system moves this internal pointer 14 positions farther. When the next write access occurs, the new characters will be appended to the 14 existing characters. You must therefore either specify a given number of characters per field, or else a given character must be inserted between fields, so that the end of a field can be recognized when the file is read. For our address file, which represents a pure text file, we really don't need all of the 256 characters which can be represented by 8 bits. All we need are the uppercase and lowercase letters, numbers and some punctuation. The American Standard Code for Information Interchange (ASCII), the code the ST uses to represent characters, has several control characters which mark the end of the file or the end of a field, for example. 12 Abacus Atari ST Disk Drives Inside and Out The internal pointer advances by the number of characters read from a file, just like when we write to a file. Every character can be read this way, but to read the last character in a file, all of the previous characters must be read first. The GEMDOS function LSEEK ($42) makes it possible to position the internal data pointer to an arbitrary character relative to the start of the file, the end of the file, or the current pointer. Again, the parameters must include the file handle, the mode word and the desired change to the pointer position. If the LSEEK mode word has a value of 0, the pointer's position is calculated relative to the start of the file. A value of 1 calculates the new position of the pointer relative to the current pointer, meaning that negative values are also allowed. A value of 2 as the mode word calculates the pointer's position relative to the end of the file, and only negative values are allowed. With the LSEEK function it is possible to program a random-access file using fixed field lengths, such as 12 for the name and 64 characters for an entire record. This way you can compute the number of characters by which the internal data pointer must be moved to get to the desired record. There are three more GEMDOS functions important for file handling which we haven’t yet discussed. SETDTA ($1A) sets up a buffer for the two functions SFIRST ($4E) and SNEXT ($4F). These latter two functions make it possible to read all the files on a diskette from the directory and to determine the lengths of these files. In the following sections, we'll turn to the individual high-level languages and take a closer look at the file handling features for each language. These examples are not introductions to the languages themselves, nor do they illustrate a complete file management system. They are only intended to show concrete examples of how simple it is to create and access a disk file in these languages. After this semi-theoretical treatment of access techniques, you will find a simple but complete database program written in BASIC in Section 2.6. It illustrates the practical application of what you will have learned by then. 13 Abacus Atari ST Disk Drives Inside and Out 2.2 File access in BASIC The ST BASIC language included with the Atari ST provides both sequential and random file access. The programs below will run in the GFA BASIC® interpreter without alterations. However, the line numbers must first be removed with the ST-KILL program included with GFA BASIC®. 2.2.1 BASIC command overview Use the command OPEN to create a disk file. The disk file offers three different file options. Here is the command syntax: OPEN "mode",#file number,"filename",record length The following options, which must be in capital letters, exist for mode: "I" = open file for sequential reading (input) "O" = open file for sequential writing (output) "R" = open file for random access #file number is any number between 1 and 15. filename can contain a maximum of eight letters followed by a period and three more letters (the extension), record length has an effect only when opening a random- access file (mode = "R"); it specifies the size of each record in bytes. In contrast to the operating system function, you must specify when the file is created whether it will use sequential or random access. The use of sequential files is severely limited in ST BASIC, because there is no way to append data to an existing file. This can only be done with a rather roundabout trick. For example, if you have a sequential address file with 100 addresses stored, and you want to add an address to the list, you would have to read all 100 addresses into memory, add the new address, and write the 101 addresses back to disk. OPEN " O " erases an existing file with the same name and creates a completely new, empty file on the disk. Because of the limited file handling capabilities, and the fact that the maximum size of a sequential file is dependent on the size of the random access memory (RAM) in the ST itself, we will not spend a lot of time on sequential files under ST BASIC. 14 Abacus Atari ST Disk Drives Inside and Out 2.2.2 The sequential file in BASIC ASCII strings and numbers can be written to a sequential file. Writing special characters can cause problems because it is possible that the end of a field may not be found, so we'll keep to the ASCII standard. For example, this type of file can be opened for writing by the following command: OPEN "0",#1,"TEST1.DAT" This newly created file is given the filename TEST1. DAT. The WRITE #1 and PRINT#1 commands handle writing to the file. WRITE outputs a comma between the data to be written, while PRINT uses the same formatting characters as are used in screen output, such as spaces following a comma. print# 1 and WRITE#1 have the same syntax: PRINT#file number,data[,data, ...] WRITE#file number,data[,data, .. .] The following command sequence opens the file TEST1. DAT for writing and writes data to it: 10 open "O",#1,"A:TEST1.DAT" 20 a$ = "Harry" 30 b$ = "Hirsch" 40 for i = 1 to 10 50 write#l,a$ 60 write#l,b$ 70 next i 80 close #1 This program creates the file TESTl. DAT on the diskette in drive A and writes Harry Hirsch to the file ten times. The WRITE# function encloses a string in quotation marks and places the characters $0D (CR = Carriage Return) and $0A (LF = Line Feed) at the end of the output. The character $1A is used by BASIC as the end-of-file (EOF) character, and gives the programmer a way of recognizing the end of the file. 15 Abacus Atari ST Disk Drives Inside and Out There are two commands in ST BASIC for reading from a sequential file. These commands differ only in the way they handle control characters in the text to be read: The INPUT# function skips preceding spaces, CR's, LF's, and special characters. The function starts at the first ASCII character and reads until it finds a space, a comma, the end-of-line character (EOL, consisting of $0A and $0D [LF and CR]), the EOF character, or a maximum of 255 characters. The LINE input# function reads all characters from the first to the EOL character, or up to 254 characters. Both commands must be passed a variable in which to place the characters read, as well as the file number. INPUT# 1, a$ reads a string from the file numbered 1 into the variable a$. The following program fragment opens the file TEST1. DAT created on the last page, and reads all strings up to the EOF character. The function EOF (f ilenumber ) is used to recognize this. It returns a logical value: TRUE if the end of the file was reached, or FALSE if this was not the case. 10 open "I",#1,"A:TEST1.DAT" 20 if eof(l) goto 100 30 input #l,a$ 40 print a$ 50 goto 20 100 close #1 2.2.3 The random-access file in BASIC Random-access file manipulation is implemented much better in ST BASIC than sequential access. However, you must learn several commands first, because the creation and handling of a random-access file is proportionately more complex. Opening and creating a random-access file is not much different from opening a sequential file. OPEN "R", #1, "TEST2 . DAT", 64 opens the file TEST2 . DAT as a random-access file, and declares a record length of 64 characters for the file. When you later access the file with GET# and PUT#, these accesses will always take place in 64-character "segments." 16 Abacus Atari ST Disk Drives Inside and Out The only characters allowed in this type of file are ASCII characters. For this reason, all numbers to be written to a random-access file must be converted to ASCII codes first. When the file is being read, these codes must then be converted back into numbers. There are several BASIC functions available for this purpose. Generally a random-access file record will contain several fields, e.g., for the last name, the first name, etc. This division of available space (in this case the 64 characters) is accomplished with the command FIELD #. FIELD #1, 10 AS a$, 12 AS b$, 20 AS c$, 15 AS d$, 2 AS e$, 5 AS f$ The preceding instruction reserves 10 characters for a$ (first name), 12 for b$ (last name), 20 characters for c$ (street address), 15 for d$ (city), 2 characters for e$ (state), and 5 characters for f$ (zip code). These string variables are not accessed directly, but only by way of the functions LSET and RSET. LSET a$ = "Harry" transfers the string Harry to the string variable a$ and left-justifies it in a$, which can contain 10 characters. The remaining five characters not used by the word are filled in with spaces ($20). The command RSET a$ = "Harry" fills the buffer variable right- justified; that is, the word Harry will be formatted to the right margin of the variable, and the spaces will be placed to the left of the word. To write numbers into a random-access file, they must first be converted to byte strings. The functions MKD$, MKI$, and MKS$ take care of this: MKI$ (number) returns a 2-byte string for integers MKS$ (number) returns a 4-byte string for real numbers MKD$ (number) returns an 8-byte string for double-precision numbers Numbers are converted to ASCII strings by one of these functions before they are written to the desired buffer variable and later converted to "normal" numbers by another set of functions (CVI, CVS, CVD). After the desired buffer variables of the record have been set up with FIELD, strings have been placed in the buffer variables with LSET, and numbers have been converted by one of the above converters then put in place with LSET, the entire record can be written to the file with the PUT command. PUT #5, 1 writes the data contained in the buffer variables of file number 5 as record number 1. 17 Abacus Atari ST Disk Drives Inside and Out The following BASIC program creates a random-access file with the name TEST3.DAT on the disk in drive A, specifies 6 fields for the buffer variable, fills the buffer variable with values, and then writes these values to the file as records 1 and 2. 10 open "R",#1,"A:FILE3.DAT,64 20 field #1/10 as a$,12 as b$,20 as c$,15 as d$,2 as e$,5 as f$ 30 lset a$= "Harry" 40 lset b$= "Hirsch" 60 lset c$= "2222 Oak Dr." 70 lset d$= "Portland" 80 lset e$= "OR" 90 b = 94750 100 lset f$=mks$(b) 110 put #1, 1 120 put #1, 2 130 close #1 In line 100, the number 94750 is converted to a 4-byte string by mks$ before it is assigned to the buffer variable f $. Reading the data in from a random-access file is similar to writing. You open the file, define buffer variables, and read a complete record with the command GET #1. The individual fields can be accessed directly through the corresponding buffer variables. However, numbers must be converted back to the normal format, because they are stored in a random-access file as strings. The following BASIC program opens the file created above and reads all of the records from it, printing the data on the screen. 10 open "R",#1,"A:FILE3.DAT",64 20 field #1,10 as a$,12 as b$,20 as c$,15 as d$,2 as e$,5 as f$ 30 get #1,1 40 print a$,b$ 50 print c$,d$,e$ 60 print cvs(f$) 70 close 1 The sizes of the fields may not differ between writing and reading. That means that if 13 characters are reserved in the buffer variables for a$ before writing, then 13 characters must also be defined for the buffer variable in the same position as a$ when the file is read. But the names of the buffer variables do not have to be the same when reading as when writing. 18 Abacus Atari ST Disk Drives Inside and Out 2.3 File handling in Pascal This description of file functions in Pascal is based on the ST PASCAL Plus® compiler by CCD. This compiler is a veiy good implementation of Pascal on the Atari ST—it goes far beyond the Pascal standard. ST PASCAL Plus supports both sequential and random-access files. The data type file of or the predefined type text (which can be used only for sequential files and which corresponds to the type packed array of char) can be used. For example: var dat: file of integer This instruction declares a file which will hold integer numbers and the corresponding pointer as the variable dat, which points to the element currently being accessed in the file. 2.3.1 The sequential file in Pascal After declaring a file variable of type file of , a new file will be created by the function rewrite (internal filename, 'external name ' ) , which is similar to the BASIC command OPEN "O". This command will create a file with the given filename and assign it an external name. The filename must be declared as a variable of type file of in the declaration section. The file can be accessed via the filename or the buffer variable defined by rewrite (same name with an appended A ). internal filename represents the file within the Pascal program and the external name in single quotes represents the same file on the mass storage medium (disk file). For example, if you declare the file dat with: var dat: file of integer; and open it with rewrite (dat, 'a : sfile .dat ' ) for sequential writing, the buffer variable dat A will be defined at the same time which can accept an integer, and which points to the first element in the file. In addition, the file sfile . dat will be created on the disk in drive A and opened for writing. All subsequent input and output refers to this disk file. 19 Abacus Atari ST Disk Drives Inside and Out To read an existing file, it must be opened with reset (internal filename, 'external name '). This command opens an existing file for reading and transfers the first record into the buffer variable. If an attempt is made to open a nonexistent file, eof ( ) will be TRUE. The function eof (internal filename) returns a value of type boolean (TRUE or FALSE). TRUE is returned if the file pointer points to the end of the file, eol (f ile variable) is also a function of type boolean, but it can only be used on files of type packed array of char or text and returns TRUE when the end of the line is reached. Access to the data in the file is made via put (internal filename) for write access and get (internal filename) for read access. put (dat ) writes the value of the buffer variable dat A in the file. The buffer variable represents a pointer to the file, which is set to zero by rewrite or reset and is incremented by one with get or put upon each access. This sets the pointer to the next element in the file. After opening the file for reading with reset (dat, 'name '), the first file element is transferred to the buffer variable dat A . A subsequent get (dat ) increments the file pointer by one and transfers the value to which the pointer points to the buffer variable dat A . The function eof (file variable) is used to recognize the end of the file. This function returns a value of type boolean. In the example, we must test for the end of the file before the access with get, because get increments the file pointer and tries to read the next file element into the buffer variable. With files of type text it is also possible to recognize the end of the line with the function eol (file variable) , which returns a value of type boolean. All available data types in Pascal, including records, can serve as possible file elements. After a file has been opened with rewrite, the buffer variable can be assigned a value, which can then be written to the file with put. For pure text files, those of type packed file of char (text), the command sequence necessary for writing a file element, assigning a value to the buffer variable through dat A : = value ; and writing this value to the file with put (dat) ; can be abbreviated to the command write (dat, value) ;. Similar to this, the command read (dat, value) reads from a text file and replaces the commands value : = dat A and get (dat ) . 20 Abacus Atari ST Disk Drives Inside and Out The following Pascal program creates a file on the diskette in drive A and writes 20 strings to it. In CCD Pascal, string [20] defines a variable of type packed array of char which can hold 21 characters. The Pascal compiler stores the length of each string at the start of each string, inserting it in the null character. (* Writing a sequential file in Pascal. D.B. 9.86 *) program sfile ; var datl tl,t2 i file of string[20] ; string[20] ; integer ; begin rewrite (datl, 'a:seqfile.dat' ); tl := 'Harry'; t2 := 'Hirsch'; for i:= 1 to 10 do begin datl A := tl; put (datl); datl A := t2; put (datl); end; (* for loop *) end. (* program *) If you look at the created file seqfile.dat with the disk monitor presented in Chapter 7, you can clearly see the organization of a sequential Pascal file with string variables (21 characters per string, string length at the start of the string). The following program reads the file created by the program above: (* Reading a sequential file in Pascal. U.B. 9.86 *) program readfile ; var dat 1 tl, t2 i file of string [20] ; string[20] ; integer ; begin writeln (' Read file '); reset (datl,'a:seqfile.dat'); 21 Abacus Atari ST Disk Drives Inside and Out while not eof(datl) do begin tl := datl*; get (datl); writeln (tl); end; (* while loop *) writeln; writeln (' Press the Return key '); readln (t2); end. (* program *) After opening the file with reset (datl, ' a : seqf ile . dat ' ), the first file element will be assigned to the buffer variable dat 1 A , so that the buffer variable can process a variable immediately after opening the file. This variable must naturally be the same type as the buffer variable defined along with the declaration of the file variable, or errors can occur. Moreover, no attempt may be made to read data beyond the end of the file. The function eof (datl) checks to see if the end of the file has been reached. The read loop will be exited in this case. As in BASIC, there is no way to append data to an existing sequential file in Pascal. If you want to expand an existing file, you will have to read in the entire file, add the new file elements and write it all out to a new file. Creation of and access to files of other data types (file of integer, file of real) is done in the same way as the examples given here. 2.3.2 Random-access files in Pascal Creating random-access files and opening them for reading uses the same commands that are used for sequential files (rewrite, reset). Even the access to individual pieces of data is similar. There is only one additional parameter for get and put: the number of the record which is to be read or written. The numbering of records starts with 0, whereby all records between 0 and the largest number must first be created. For example, if the last record has the number 8, then record number 10 cannot be created until record number 9 has been written. The short example program below demonstrates the flexibility of this file type. The program creates a small address file to which the same address is written 10 times. 22 Abacus Atari ST Disk Drives Inside and Out (* Random-access file writing in Pascal. U.B. 9.86 *) program ranfile ; type addr = record fname : string[10]; lname : string[12]; street : string[20]; city : string[15]; state : string [2]; zip : string[5]; end; (* record *) var datl tl,t2 i file of addr; addr; integer; begin rewrite(datl,'a:randoml.dat'); tl. fname := 'Harry'; tl.lname := 'Hirsch'; tl. street := '2222 Oak Dr.'; tl.city ;= 'Portland'; tl.state := 'OR'; tl.zip := '94750'; for i:= 0 to 9 do begin datl"' := tl; put (dat1,i); end; (* for loop *) end. (* program *) In CCD Pascal, the command dat 1 A : = 11; passes the entire address record (with first name, last name, etc.) to the buffer variable, which is then written to the file as record number 1 with put (dat 1, i). As you can see, the number of characters in a string is stored before the first character of the string, simple integers are stored as 2-byte hexadecimal numbers. Pascal uses the number $F5 as the end-of-file character. 23 Abacus Atari ST Disk Drives Inside and Out 2.4 File access in C The C language can be considered the native language of the Atari ST. Large parts of the TOS are written in this language. Therefore it's not surprising to find the GEMDOS functions described in the introduction to this chapter in the language description of C, although in a modified form. From the user's point of view, C is an incomplete language. That's because many functions, including the functions for file management, are omitted from the C language, and the user has to design them himself. However, all C compilers come with the standard I/O library—the #include file stdio . h as described by C authors Brian W. Kemighan and Dennis M. Ritchie. To use the file functions, this file must be integrated into the C program at its beginning with the command #include "stdio. h". One of the problems for someone learning C on the Atari ST, other than the chaotic appearance of the operators and abbreviations (&, !=, I | , etc.), is the initial version of the Digital Research C compiler for the ST. An inexperienced C programmer can never be sure whether a given problem or error lies was caused by his program or by the Digital Research compiler itself. For this reason, all of the C programs presented here have been compiled with the Lattice C® compiler from Metacomco. It shouldn't be difficult to adapt the program to other C compilers, because only the standard functions from the stdio. h library are used. Communication with files in C is accomplished by a data structure of type FILE, which is defined in the stdio . h library along with the functions for accessing this data structure. Here is an overview of the individual access functions with the data types of their parameters: pointer = fopen(name, mode) FILE *fopen() FILE *pointer char *name char *mode 24 Abacus Atari ST Disk Drives Inside and Out Here are the possible mode words: "w" : create a file and open for writing "a" : open an existing file for appending data "r" : open an existing file for reading data In addition to these, there are other mode words which have different functions depending on the compiler used, but they are not of importance to us in this case. The above function opens a file with subsequent access dependent upon the mode word. If an error occurs and the file cannot be opened, the pointer will equal NULL, or else it will contain the pointer to the file. code = fclose(pointer) int code FILE *pointer This closes the file to which pointer points. fprintf(pointer, format, arguments) FILE *pointer char *format char *arguments This function writes multiple arguments, separated by commas, to the file with the format described by format. The format parameters correspond to the those of the normal print f function. code = fscanf(pointer, format, chpointer) FILE *pointer char *format char *chpointer int code This function reads strings from the file specified by pointer in the format specified by format into the variable chpointer. The format options are identical to those of the scanf function. 25 Abacus Atari ST Disk Drives Inside and Out code = fputs(buffer, pointer) FILE *pointer char *buffer int code This function writes a string to which buffer points, to the file to which pointer points. If an error occurs, code will equal EOF. The zero byte which terminates a C string is not written, but the string is terminated with a NEWLINE character. code = fgets(buffer, number, pointer) FILE *pointer char *chpoint char ^buffer int number int code This code reads number characters from the file to which pointer points into the buffer to which buffer points. It will stop reading when the end-of-line character (EOL) is encountered. A zero-byte will be appended to the string and the pointer to the buffer will be returned in chpoint. After an error-free access, chpoint points to buffer, otherwise chpoint will contain a 0, which is expressed as null in C. code = fputc(chr, pointer) FILE *pointer char chr int code A single character, contained in chr, is written into the file to which pointer points. After an error, code = EOF, otherwise code contains the character written. code = fgetc(pointer) FILE *pointer int code 26 Abacus Atari ST Disk Drives Inside and Out The above function reads a single character from the file to which pointer points. The code of the character read will be returned in code, EOF if the end of the file was reached. code = fseek(pointer, position, mode) FILE *pointer long position int mode int code Sets the file pointer of the file to which pointer points to a new value. The mode parameter specifies the new position of the pointer and can have the following values: 0 : set new position relative to the start of the file 1 : set new position relative to the current position 2 : set new position relative to the end of the file 2.4.1 The sequential file in C The following C program opens the file SEQFILE . DAT for writing and writes Harry Hirsch into this file 10 times. /* Writing to a sequential file in C. U.B. 9.86 */ #include #include main() { int i, k; FILE *datl, *fopen(); char *tl = "Harry"; char *t2 = "Hirsch"; datl = fopen("a:seqfile.dat","w"); for (k=l; kcll; k++) { fprintf(datl,"%13s",tl); 27 Abacus Atari ST Disk Drives Inside and Out fprintf(datl,"%13s",t2) ; } /* end of the for loop */ i = fclose(datl); printf("Press a key\n"); getchar(); } /* End main */ The following program reads the file just written and displays the contents of the entire file on the screen: /* Reading a sequential file in C. U.B. 9.86 */ ♦include main () { int i, k; FILE *datl, *fopen(); char space[14]; char *p; datl = fopen("a:seqfile.dat","r"); while (p = fgets(space,14,datl) != NULL) { printf("%s\n",space); ) /* End of while loop */ i = fclose(datl); printf("\n\n"); printf("Press key"); getchar(); ) /* End main */ 28 Abacus Atari ST Disk Drives Inside and Out 2.4.2 The random-access file in C The function f seek (), which allows positioning of the file pointer to a specific character within the file, is required to make random-access files possible in C. Each field receives a set length as a result of the formatted output to the file with fprintf (). As a result, each complete record (such as an address) also has a precise, set length (which, in our case, is 64 characters). To read the 10th record, you need only multiply the length of a record with the number of the desired record, set the file pointer to the computed value, and the desired record can be processed. In C the numbering of the records starts with zero. /* Writing a random-access file in C. U.B. 9.86 */ tinclude #include char *fname = "Harry"; char *lname = "Hirsch"; char *street = "2222 Oak Dr."; char *city = "Portland"; char *state = "OR"; int zip = 94750; main () { int i, k; FILE *datl, *fopen(); datl = fopen("A:random2.dat","w"); for (k=l; k ♦include ♦define LENGTH 64L main () { int k, il, i; FILE *datl, *fopen(); long pos; char space[80], *p; datl = fopen("a:random2.dat","r"); k = 0; pos = k*LENGTH; while ((i = fgetc(datl)) != EOF) { i = fseek(datl,pos,0); printf(" Record number = %8d\n",k); printf (" Byte pos. in file = %8d\n",pos); printf ("\n"); p = fgets(space,11,datl); printf (" First name = %s\n",space); p = fgets(space,13,datl); printf(" Last name = %s\n",space); 30 Abacus Atari ST Disk Drives Inside and Out p = fgets(space,21,datl); printfC Street = %s\n",space); p = fgets(space,16,datl); printfC City = %s\n", space) ; p = fgets(space,3,datl); printfC State = %s\n", space) ; p = fgets(space,5,datl); il = atoi(space); printfC Zip code = %8d\n",il); k+=l; pos=k*LENGTH; printf ("****************************\ n \ n ii) . } /* End WHILE loop */ i = fclose(datl); printf("\n\n"); printf("Press a key\n"); getchar(); ) /* End main */ 31 Abacus Atari ST Disk Drives Inside and Out 2.5 File handling in FORTRAN All of the examples using FORTRAN here refer to the Pro FORTRAN-77® compiler from Prospero. Like CCD Pascal, this FORTRAN allows both sequential and random-access files. The Atari implementation is quite good, and all language definitions meet FORTRAN-77 standards. In comparing the speed of compiled code, at least in terms of mathematical computations, this compiler is substantially faster than the C and Pascal compilers. 2.5.1 The sequential file in FORTRAN The OPEN function is used to create a sequential file as well as open a file. OPEN (5, FILE = ' a: fdatl. dat ') opens a file on access unit 5 with the name "f dat 1 . dat" on drive A. This file will be created if it does not already exist. The normal I/O command WRITE, with optional parameters, can be used to write to this file. WRITE (5) "Harry" writes to file unit 5. The WRITE command also supports the standard FORTRAN formatting options, although we do not have the space to discuss them here. Here is the FORTRAN version of our example program which creates a sequential file and writes the name Harry Hirsch into the file 10 times: PROGRAM SEQ1 CHARACTER*13 LNAME, FNAME FNAME = "Harry" LNAME = "Hirsch" OPEN (2, FILE='A:FSEQ1.DAT', FORM='UNFORMATTED') DO 100 N = 1,10 WRITE (2) FNAME WRITE (2) LNAME 100 CONTINUE CLOSE (2) END 32 Abacus Atari ST Disk Drives Inside and Out The following program reads the data from the sequential file: PROGRAM SEQ2 CHARACTER*2 T1 CHARACTER*13 TEXT OPEN (2, FILE='A:FSEQ1.DAT 1 , FORM='UNFORMATTED 1 ,STATUS='OLD') 100 CONTINUE READ (2,END=200) TEXT WRITE (*,*) TEXT GOTO 100 200 CONTINUE CLOSE (2) END 2.5.2 The random-access file in FORTRAN Back to our standard random-access file program, this time in FORTRAN: C Write a random-access file in FORTRAN. U.B. 9.86 PROGRAM RAND1 INTEGER*4 ZIP CHARACTER*10 FNAME CHARACTER*12 LNAME CHARACTER*20 STREET CHARACTER*15 CITY CHARACTER*2 STATE FNAME = 'Harry' LNAME = 'Hirsch' STREET = '2222 Oak Dr.' CITY = 'Portland' ZIP = 94750 OPEN (2, FILE = 'A:\FRAND1.DAT', REC1 = 64, ACCESS = 'DIRECT') DO 100 N = 1,10 WRITE (2,REC = N) FNAME, LNAME, STREET, CITY, ZIP 100 CONTINUE CLOSE (2) END 33 Abacus Atari ST Disk Drives Inside and Out The next program reads the data from the file: C Read a random-access in FORTRAN. U.B. 9.86 PROGRAM RAND1 INTEGER*4 ZIP, STAT CHARACTER*10 FNAME CHARACTER*12 LNAME CHARACTER*20 STREET CHARACTER*15 CITY CHARACTER*2 STATE OPEN (2, FILE = 'A:\FRAND1.DAT', REC1 = 64, ACCESS = 'DIRECT', - STATUS = ’OLD) N=1 10 CONTINUE READ (2, REC = N, IOSTAT = STAT) FNAME, LNAME, STREE, CITY, ZIP IF (STAT .EQ. 0) THEN WRITE (*, *) ' Record number: ' , n WRITE (*, *) WRITE (*, *) ' First name — 1 r FNAME WRITE <*, *) ' Last name — l r LNAME WRITE <*, *) ' Street — 1 r STREET WRITE (*, *) City — i CITY WRITE (*, • (a. i 6) ') ’ Zip code — 1 r WRITE (*, *) WRITE <*, *) N = N+l GOTO 10 ELSE WRITE (*,*) WRITE (*,*) WRITE (*,*) ' Press a key' CLOSE (2) END IF END 34 Abacus Atari ST Disk Drives Inside and Out 2.6 A simple database After all of this theory, we want to demonstrate some practical data/file management techniques with a simple database program. This program probably isn't the best thing to use for warehouse inventory, but it will work well for listing telephone numbers or managing your record collection. The program is written in ST BASIC, which is included with the Atari ST. When creating such a program, you should consider what a database program should be able to do. This program has some of the most important functions: • Create a new database • Input new data or correct old entries • Load an existing database into memory • Output data on the screen or printer • Search for given keywords • Sort data according to a field • End the program These functions are accessible from a simple menu displayed on the screen. To select a function, simply enter the function number and press . Before we take a closer look at the individual functions, it would be a good idea to enter the program first: 10 i*** Mini-Database S.D. *** 20 dim d$(5),i$(5),1(5),p$(500) ,r(500) 30 for i=l to 500: r(i)=i: next i 40 for i=l to 5: d$(i)=space$(100) 50 i$(i)="" : next i 60 start: 70 fullw 2: clearw 2: gotoxy 0,0 80 ? "**** Mini-database from ST Drive Book **** 90 ? d;" Data sets available in file ";f$ 100 for i=l to 5 110 gotoxy 28,1+i: ?i;") ";i$(i) 120 next i 130 if so then gotoxy 21,1+so: ?">" 140 gotoxy 0,6 150 ?: ? "1) Create a database" 35 Abacus Atari ST Disk Drives Inside and Out 160 7 " 2 ) Input the data" 170 7 "3) Load the data" 180 7 "4) Sort the data" 190 7 "5) Search" 200 7 " 6 ) Output the data" 210 7 "7) End" 220 ? input "Your choice ";w 230 on w gosub create,enter,lading,sort,search,output,ende 240 goto start 250 260 '** create the database ** 270 create: 280 ? " ** Database create : 500 items with 5 fields free **" 290 sum=0 300 ?: for i=l to 5 310 ? i;". Field name,Length 320 input i$(i),l(i) 330 sum=sum+l(i) 340 next i 350 ?: input "OK ";o$ 360 if o$="n" or o$="N" then create 370 gosub getfn 380 open "0",#l,fi$ 390 for i=l to 5 400 printtl,i$(i) 410 printtl,1(i) 420 d$(i)=space$(1(i)) 430 next i 440 close #1 450 open "R",#1,fd$,sum 460 field #1, 1(1) as d$(l), 1(2) as d$(2), 1(3) as d$<3), 1(4) 480 return 490 ' 500 '** Enter the data ** 510 enter: 520 clearw 2: gotoxy 0,0: ? " *** Data entry *** 530 ? d;" Data sets available" 540 gotoxy 0,3:? "Number ";d+l 550 gotoxy 0,4: input "Number ";d$ 560 if len(d$)>0 then dl=val(d$) else dl=d+l 570 if dl=0 then return 580 if dl>d+l then enter 590 if dl0 then Iset d$(i)=d$ 650 next i 660 ?: input "OK (y/n) ";o$ 670 if o$="n" or o$="N" then enter 680 if dl=d+l then d=d+l 690 put #l,r(dl) 700 goto enter 710 720 '** Database load ** 730 lading: 740 gosub getfn 750 close #1 760 sum=0 770 open "I",#l,fi$ 780 for i=l to 5 790 inputtl,i$(i) 800 inputtl,1(i) 810 sum=sum+l(i) 820 d$(i)=space$(1(i)) 830 next i 840 close #1 850 open "R",#1,fd$,sum 860 field #1, 1(1) as d$(l), 1(2) as d$(2), 1(3) as d$(3), 1 d$ (4) , 1(5) as d$ (5) 870 d=0 880 while not eof(l) 890 d=d+l 900 get #l,d 910 wend 920 return 930 ' 940 1 ** Data output ** 950 output: 960 if d=0 then ? "No data available !": goto waitkey 970 ? " ** Data output **" 980 input "S)creen or P)rinter ";o$ 990 for dl=l to d 1000 gosub outputl 1010 if o$="p" or o$="P" then lprint else ? 1020 next dl 1030 waitkey: 1040 gotoxy 30,16: input "-Press 'Return'-",w$ 1050 return 1060 outputl: 1070 get #l,r(dl) 1080 for j=l to 5 1090 if o$="p" or o$="P" then lprint i$(j),d$(j) else ? i$(j) (4) as , d $ ( j) 37 Abacus Atari ST Disk Drives Inside and Out 1100 next j 1110 return 1120 ' 1130 '** Search ** 1140 search: 1150 if d=0 then ? "No data available!": goto waitkey 1160 ?: input "Field number,Text ";f,t$ 1170 for dl=l to d 1180 get #l,dl 1190 if instr(d$(f),t$) then gosub outputl: ? 1200 next dl 1210 goto waitkey 1220 ' 1230 '** Sort ** 1240 sort: 1250 if d=0 then ? "No data available!": goto waitkey 1260 ?: input " Which field to sort on ";so 1270 if so=0 or so>5 then return 1280 for i=l to d 1290 get #l,i 1300 p$(i)=d$(so) 1310 next i 1320 for i=l to d 1330 for j=i to d 1340 if p$(r(i))>p$(r(j)) then swap r(i),r(j) 1350 next j 1360 next i 1370 return 1380 ' 1390 '** End ** 1400 ende: 1410 close #1 1420 ?: ? "**** End Program ! ****" 1430 end 1440 ' 1450 '** subroutines ** 1460 getfn: 1470 ?: input "Filename ";f$ 1480 fi$=f$+".idx" 1490 fd$=f$+".dat" 1500 return 38 Abacus Atari ST Disk Drives Inside and Out Now we'll discuss the individual functions: 1) Creating a database After calling this function, you will be asked five times to enter two parameters: field name and field length. Here you enter the name of the field, followed by a comma and the maximum length of this entry in characters. For an address database, this might look like this: First name,10 Last name,15 Street address,25 City,16 Telephone,13 Once you have entered these, you will be asked if the information is correct (OK?). If it is, enter Y here (the program accepts upper or lower case lettering). You will then be asked for the filename under which the database will be stored on the disk. The drive may be included along with the name, as in A: TEST. You may not enter an extension (like . DAT) because the program creates two files with the same name but different extensions. After the program is run you'll find one file with the extension . IDX. This file contains the names and lengths of the data fields, as well as one with the extension . DAT, which contains the records themselves. Once the data items are entered and stored on the disk, the main menu will be displayed again. 2) Enter data After selecting this function, you will be told how many records currently exist, and you will be asked to enter the name of the record to enter or modify. The number of the next available record is supplied behind the question mark, so you just have to press to enter a new record. If you want to change a record, enter its number and you will be shown the old contents of the record as well as a question mark requesting that you enter new data. If you want to keep the old contents of a data field, just press . 39 Abacus Atari ST Disk Drives Inside and Out Additional data is entered in the same way. If you want to stop entering data, enter 0 for the record number. 3) Load a database Here you are asked for the name of the database. Again, you can enter only the drive and the filename without an extension. The main menu will be displayed again once the database is loaded, and the menu will list the name of the file, the number of entries in it and the field names. 4) Sorting the data If you want to sort the records based on a specific field, choose this function. You will be asked for the number of the field by which the records are to be sorted. For example, you can use this to sort your address list by name, print it out, and then sort by zip code and print it out again. The sort function does not contain any output function. A > character will be placed in front of the field name with which you last sorted the file. 5) Search This function asks you to enter a field number and a search string. For example, if you want to output all the addresses in Wawatosa, you would enter "4, Wawatosa" in the previous example. All records whose city field (field number 4) contains the string Wawatosa will be displayed. You can also enter just part of search string. 6) Output data This function allows you to output all records to the screen or printer. Answering the question regarding the destination of the output with p sends it to the printer, while all other input sends it to the screen. The records are output in the order they were entered, unless you first call the sort function. 7) End The opened data channel is closed (CLOSE # 1 ) and the program ends. 40 Abacus Atari ST Disk Drives Inside and Out The program uses both sequential and random-access files. The field names and lengths of the fields are stored sequentially (name . IDX), and the records themselves are placed in a random-access file (name . DAT). For small databases and with the large memory capacity of the Atari ST, you could also store all data sequentially in an appropriate string array and manage it directly in memory. However, this takes more time to load, and works only if everything is saved again after it is accessed and edited. 41 Chapter Three Abacus Atari ST Disk Drives Inside and Out Data structures Writing to disk is basically a matter of taking a large set of data and placing it on diskette. It sounds simple enough, but when we look at the procedures more closely, certain areas present some difficulties. First of all, the diskette must be organized in such a way that the data can be found again. Some preparations are necessary for this. You don't have to bother much with the details, but the operating system and the computer and disk drives must execute many complex steps. A diskette must be formatted before it can be used. During formatting, the surface of the diskette is divided into individual sectors whose positions are determined by the format used. The computer must be able to recognize this format, because it can work with different formats. The number of sides of the diskette used is as important as the number of sectors and their length. This information is contained in the boot sector, which we'll examine in detail. The sectors used for every file or program stored on the diskette must be assigned and marked. This information is stored in the File Allocation Table (FAT) of the disk directory. This will be discussed in the next chapter. 3.1 Diskette format As we explained before, when a diskette is formatted it is divided into individual sections. The diskette is first divided into tracks. These tracks are concentric rings on the diskette and are numbered from the outside in. There are 80 such tracks on a normally formatted diskette, numbered from 0 to 79. It is possible to format up to 82 tracks, but the data security decreases toward the center because of the reduced available space. For this reason tracks 80 to 82 are not used. They can be used if formatted appropriately. The individual tracks are in turn divided into sectors. The sectors represent segments of the track rings. These sectors are combined into clusters, usually two sectors per cluster. Clusters are not very significant, so we will ignore them and discuss only sectors. 45 Abacus Atari ST Disk Drives Inside and Out In the normal diskette format there are 9 sectors on every track, and each sector comprises 512 bytes. This results in a storage capacity of 80*9*512=368640 bytes on a single-sided disk. However, 368640 is not the actual number of bytes stored on the diskette. Additional information is placed on each track and each sector during formatting. This data is required by the disk controller , the chip that controls the disk drive in the Atari ST. The disk controller uses the information to find the proper sector in the track. Let's look at the complete construction of a normal track. Number Bytes 60 $4E00 per sector: 12 $00 3 $F5 1 $FE 1 track# 1 side # 1 sector # 1 $02 1 $F7 22 $4E 12 $00 3 $F5 1 $FB 512 Data 1 $F7 40 $4E end of track: 1401 $4E Comments Start of track will be written as $A1 ID address mark track number 0 to 79 side number 0 or 1 sector number 1 to 9 *$100=512 bytes per sector CRC checksum (will be 2 bytes) filler bytes tt become $A1 marker (data address mark) the actual sector data write CRC checksum filler bytes filler bytes If you add all of these bytes together, you get 6969 bytes per track, which corresponds to an unformatted diskette capacity of 557520 bytes. Unfortunately, this capacity cannot all be used for data, or else the controller wouldn't be able to find the data again (how would it recognize the start and end of a sector?). 46 Abacus Atari ST Disk Drives Inside and Out However, it is possible to use the last 1401 bytes of each track for an additional sector. This would increase the usable diskette capacity to 409600 bytes. If we also use the three additional tracks (80 to 82), the total storage space increases to 424960 bytes. But as we said, the security of the data decreases. We'll need a short program to create this custom disk format. Before we take a look at such a program, we must take a closer look at the individual steps that comprise the formatting process. It isn't enough just to format the tracks. The parameters used, like the number of tracks and sectors, must be written on the diskette or the ST will not be able to determine how the diskette is formatted. This is where the boot sector comes in. 3.2 The boot sector The boot sector always lies at the very beginning of a diskette or hard disk: track 0, side 0, sector 1 of a diskette, or sector 0 of a hard disk. Like all the other sectors, the boot sector is 512 bytes long and is checked by the operating system every time the diskette is changed. In addition, the boot sector plays a decisive role in booting the diskette. Booting refers to loading the operating system from diskette after the computer is turned on. First the boot sector of the diskette in drive A is loaded and checked to see if the diskette contains an operating system. The boot sector also contains additional information. The boot sector contains the serial number of the diskette, a parameter block for the BIOS of the computer, and possibly a boot program with boot parameters. If this program is present, the sum of all the bytes in the sector (checksum) must yield the "magic number" $1234. If the checksum equals $1234, the program at the start of the sector, which usually contains a BRA (branch always) command, is executed. The program must be written so that it can run at any memory location. Normally, a boot sector does not contain such a boot program. More important are the various parameters which are found in the sector. These parameters are loaded by a GETBPB operating system call into the BPB (BIOS parameter block). If these parameters are not valid, the GETBPB function returns a 0 instead of the address of the BPB. 47 Abacus Atari ST Disk Drives Inside and Out The additional information in the boot sector is the serial number of the diskette. This is a 24-bit number that's determined and written to the diskette during formatting. This number is used to verify when the diskette has been changed. Here is the complete construction of the boot sector: Significance Branch command to boot program (if present) Reserved fill bytes or loader Serial number Bytes per sector (512) Sectors per cluster (2) Reserved sectors (1) Number of FATs (File Allocation Tables) (2) Number of possible directory entries (112) Number of sectors on the diskette (720/1440) Medium description (unused) Sectors per FAT (5) Sectors per track (9) Number of sides of the diskette (1 12) Number of hidden sectors (0) Flag for COMMAND . PRG Flag for file or sector boot First sector to be loaded Number of sectors to be loaded Load address FAT address Filename (usually TOS. IMG) Reserved Boot program Comparison word for the checksum The entries marked with an asterisk (*) correspond to the BPB of the diskette. These entries are identical to those of MS-DOS, the operating system of the IBM PC. We should note that a 16-bit word is stored here, in the byte order low byte-high byte (for example, BPS = $00 $02 means $200 bytes per sector). This makes it possible for the Atari ST to read IBM PC diskettes. However, the ST cannot do any more than read these files, because the data distribution on the diskette is organized differently on the PC. Bvte# Marne $00 BRA $02 filler $08 serial # * $0B BPS * $0D SPC * $0E RES * $10 NFATS * $11 NDIRS * $13 NSECTS * $15 MEDIA * $16 SPF * $18 SPT * $1A NSIDES * $1C NHID $1E EXECFLG $20 LDMODE $22 SSECT $24 SECTCNT $26 LDADDR $2A FATBUF $2E FNAME $39 RES $3A BOOTIT $1FD $1FE 48 Abacus Atari ST Disk Drives Inside and Out A couple of comments about the entries in the boot sector: • The numbers in parentheses found behind some of the entries indicate the normal contents of these entries on a single-sided diskette. • NHID, the number of hidden sectors, is not used by the ST BIOS for diskettes. The data at $1E are of interest only if the diskette is bootable. Such a diskette normally contains the operating system in the form of data files called image files (. IMG). An executable boot sector can also be recognized by the text LOADER at the 3rd byte. The boot program, which is stored in two ROMs in older Atari STs, also recognizes such a boot sector by the checksum—it must be $1234 for an executable boot sector. If this is the case, the additional data in the boot sector has the following meaning: EXECFLG will be copied in the system variable cmdload. This flag determines whether or not the program command . PRG will be loaded after loading the operating system. LDMODE determines the loading mode. If this flag is zero, the file specified by FNAME will be loaded. This file is usually TOS . IMG. If LDMODE is not zero, sectors will be directly loaded, depending on SECTCNT and SSECT. SSECT is the logical sector at which booting starts. This variable is valid only if LDMODE is not zero. SECTCNT specifies the number of sectors to be booted. This is also valid only if LDMODE is not zero. LDADDR is the address at which the file or sectors will be loaded. FATBUF specifies the address at which the FAT and the directory sectors will be loaded. FNAME is the filename of the image file to be loaded (LDMODE = 0). It is constructed just like a normal filename, with eight characters for the name and a three-character extension. 49 Abacus Atari ST Disk Drives Inside and Out BOOTIT is a boot program that will be executed after the boot sector has been loaded. That is the basic construction of the boot sector. Together with what we have learned about the diskette format, we can start putting some of our knowledge into practice by writing a program for formatting diskettes. We can already use the Format option in the File menu to format disks. As we mentioned earlier, the format used by the Atari operating system TOS is set to 80 tracks and 9 sectors per track. However, we can physically fit more tracks and sectors on a diskette. 3.2.1 Formatting program The program below offers some options for increasing the capacity of a normal diskette. It displays a menu which shows the parameters for formatting: *** Formatting program S.S. *** [Fl] Sides (s) : 2 [F2 ] Tracks .: 80 [F3] Sectors/track 9 [F4] Drive .: A [F8] Format ... [F10] Quit ! Pressing a function key changes a setting or performs a function. The following settings are available: : This key toggles between one and two sides. If you are using a single-sided disk drive, only one side can be formatted. : Here you can select 80 (normal setting) or 82 tracks. It is also possible to use 83 tracks, but we have not included this option because of data loss problems. You can add this capability by making a minor change to the program. : This function key toggles between 9 and 10 sectors per track. 50 Abacus Atari ST Disk Drives Inside and Out : This key allows you to select either drive A or drive B. Always check this parameter before you start the formatting, to prevent accidentally erasing important data on the diskette in the other drive... : Formatting begins immediately after this key is pressed, indicated by the following message: Formatting. Please wait... If an error occurs, the following message appears: ** An error occurred !! ** You should check the diskette to make sure that it is not write- protected. The error message remains onscreen until you press a key. : When are finished formatting disks, you can exit the program by pressing this key. Disks of varying storage capacities can be created by the selections possible with this program. Here are some values for single-sided formats: Tracks 80 82 80 82 Sectors per track 9 9 10 10 Capacity in bvtes 357376 366592 398336 408576 As you can see from the table above, it is possible to increase the capacity of a single-sided diskette by up to 51200 bytes. For double-sided disks, it is possible to gain more than 100K. Here is the program. It was created with the AssemPro assembler, which has few differences from the DRI assembler. If you want to assemble the program with the DRI assembler, you must start each comment line with an asterisk (*), and change the ALIGN. w instruction to EVEN. 51 Abacus Atari ST Disk Drives Inside and Out ;** Formatting-Program S.D. ** run: move .1 #menue,dO bsr print ;Menu output bsr getkey cmp.b #$3b,dO bit run /false key cmp.b #$44,dO bgt run /false key cmp.b #$3b,dO /FI ? bne notf 1 eor #3,sds /1/2 Side eor # 1 ,sdsf bra run notf 1 : cmp.b #$3c,dO / F2 ? bne notf 2 eor # 2 ,trs ,•80/82 Tracks eor # 2 ,trsf bra run notf 2 : cmp. b #$3d,dO / F3 ? bne notf3 eor #3,sptf eor #$1109,spt /9/10 Sectors 5 >er Track bra run notf3: cmp.b #$3e,d0 bne notf4 eor #3,lw eor #l,lwf bra run notf4 : cmp.b #$42,dO bne notf 8 bsr format bra run notf 8 : cmp.b #$44,d0 bne run ;F4 ? ;Drive A/B ;F 8 ? /=> Formatting ; F10 ? 52 Abacus Atari ST Disk Drives Inside and Out clr -(sp) trap #1 format: move.l #wait,dO bsr print move trsf,trsfl subq #l,trsfl floop: move sdsf,side floopl: bsr fmttr bne error subq #l,side bpl floopl subq #l,trsfl bpl floop setboot: clr ~(sp) moveq #2,d0 or sdsf,dO move dO,-(sp) move.l #$1000000,-(sp) pea buffer move #$12,-(sp) trap #14 add.l #14,sp lea buffer,aO clr.l dO cmp #9,sptf beq sok move.b #10,24(aO,dO) move trsf,dl tst sdsf beq sdll lsl #1,dl sdll: bsr addsec sok: cmp #80,trsf beq trok move #18,dl tst sdsf ;Quit, return to Desktop ;* Formatting * ;"Formatting drive.." ; Side /format one Track /Get other side /format /next Track /Boot-Sector create /Execute-Flag: not set /Disk type and number of sides /Serial number /Buffer address /Boot-Sector create /number of Boot-Sector-buffer /9 Sectors per Track ? /yes /set 10 SPT value /number of Tracks in Dl /I Side ? /yes /else set two sided /SEC + number of Tracks (Dl) /80 Tracks ? /yes /1 Side ? 53 Abacus Atari ST Disk Drives Inside and Out beq sdl2 ; yes lsl #1, dl ;else double sided sdl2 : bsr addsec ;SEC + 2*9 or 4*9 trok: move #1, -(sp) ;1 Sector clr. 1 - (sp) ;Side 0, Track 0 move #1, -(sp) /Sector 1 move lwf, -(sp) /Disk drive clr. 1 - (sp) pea buffer /Buffer move #9, -(sp) trap #14 /flopwr, Boot-Sector write add. 1 #20,sp tst dO /Error test? bne error /yes: error routine bra run /New start addsec: /SEC = SEC + Dl move.b 20 (a0,d0),d2 ;HI lsl #8,d2 move. b 19(aO,dO),d2 /LO add dl, d2 move. b d2, 19(aO,dO) /set LO lsr #8,d2 move .b d2,20(aO,dO) /set HI rts error: move. 1 #errtxt,dO bsr print /Error message output bsr getkey /wait for key press bra run /and new start fmttr: /one track formatting clr - (sp) /Virgin data move.1 #$87654321,-(sp) /Magic-number move #1, -(sp) / interleave move side, -(sp) /Side move trsf1,-(sp) /Track move spt f, — ( Sp) / Sectors/Track move lwf,-(sp) / drive clr .1 - (sp) pea buffer /Track-Buffer move #10,-(sp) trap #14 /flopfmt. Track format 54 Abacus Atari ST Disk Drives Inside and Out add.1 #26,sp tst dO rts print: move.l dO,-(sp) move #9,-(sp) trap #1 addq.1 #6,sp rts getkey: move.w #l,-(sp) trap #1 addq.l #2,sp swap dO rts ;Test for Error /Text output from (DO) /wait for key press. /key code in DO.b / Text and Variables: menue: dc.b $lb,"E***** Formatting—Program S.D dc. b 10,13,10,13 dc. b " [FI] Side (s) .: " sds : dc. b " 2",10,13 dc. b " [F2] Tracks .: " trs: dc.b "80",10,13 dc.b " [F3] Sectors/track ..: " spt: dc. b " 9",10,13 dc.b " [ F4 ] Drive .: " lw: dc. b " A",10,13 dc.b " [F 8 ] Format ...",10,13 dc. b "[F10] Quit !", 10,13,10,13,0 wait: dc. b "Formatting. Please wait...", 10,13,0 errtxt dc.b "** An error occurred !! **",10,13,0 align w sdsf: dc. w 1 trsf: dc .w 80 trsf 1 : dc. w 80 sptf: dc. w 9 lwf: dc. w 0 side: dc. w 0 BSS buffer DS.B 8000 END 55 Abacus Atari ST Disk Drives Inside and Out The program is divided into the following segments: 1) Menu control: The screen is cleared and the menu is printed. After a key is pressed, the key code passed in DO is evaluated. If one of the CMP . B #$xx, DO comparisons match, the selected function will be executed. For the switch function (-), the switch is accomplished with the EOR command in the menu text and the corresponding parameter line. After the switch, the program branches back to start (run), except for the key, which ends the program via the GEMDOS TERM function. 2) Formatting: After outputting the message Formatting . . ., the diskette will be formatted from the set maximum track-1 to track 0. If double-sided formatting is enabled, the tracks on side 1 (back) are formatted first, followed by the tracks on side 0. 3) Creation of the boot sector: First a normal boot sector is created by the XBIOS PROTOBT function. Only the number of sides is taken into account. 4) Correction of the boot sector: If nonstandard settings are used (10 sectors per track, 82 tracks), the boot sector will be corrected accordingly. First the number of sectors per track is tested. If it is 10, this will be placed in the SPT cell of the boot sector and then the number of tracks will be added to the number of sectors on the diskette. The selected number of tracks will then be tested and the sector number increased if required. 5) Saving the boot sector: The new boot sector will be written to side 0, track 0, sector 1 with the help of the FLOPWR XBIOS function. If an error occurs, it will be displayed. 6) Data area: This is where the strings for the menu, messages and variables are stored. The length of the buffer is set, but the buffer is not written on the diskette because it is in the . bs s area. Here is a BASIC program which generates the formatting program on the diskette, storing it under the name bigfmt. prg: 1000 open"R",1,"a:bigfmt.prg",16 1010 field#l,16 as bin$ 1020 a$="":for i=l TO 16: read d$:if d$="*"then 1050 1030 a=val("&H"+d$):s=s+a:a$=a$+chr$(a):next 56 Abacus Atari ST Disk Drives Inside and Out 1040 lset bin$=a$:rec=rec+l: 1050 data 60,1A,00,00,03,00, 1060 data 00 ,00,00,00,00,00, 1070 data 01,FC,61,00,01,DC, 1080 data BO,3C,00,44,6E,E6, 1090 data 00, 03,00, 00,02,3E, 1100 data BO,3C,00,3C,66,00, 1110 data 0A,79,00,02,00,00, 1120 data 00,14,OA,79,00,03, 1130 data 02, 76, 60, 98,BO, 3C, 1140 data 00, 00,02, 92,OA,79, 1150 data BO,3C,00, 42, 66, 00, 1160 data BO,3C,00,44,66,00, 1170 data 02,B9,61,00,01,3C, 1180 data 53,79,00,00,02,F8, 1190 data 61, 00,00, E2, 66, 00, 1200 data 53, 79,00, 00, 02,F8, 1210 data 02,F4,3F,00,2F,3C, 1220 data 3F, 3C, 00,12,4E, 4E, 1230 data 03, 00, 42,80,OC, 79, 1240 data 11,BC,00,0A,00,18, 1250 data 02,F4,67,00,00,04, 1260 data 00,00,02,F6,67,00, 1270 data 02,F4,67,00, 00, 04, 1280 data 42,A7,3F,3C,00,01, 1290 data 00,00,03,00,3F,3C, 1300 data 4A,40,66,00,00,IE, 1310 data 14,30,00,13,D4,41, 1320 data 4E,75,20,3C,00,00, 1330 data 60,00,FE,5A,42,67, 1340 data 3F,39,00,00,02, FE, 1350 data 02,FA,3F,39, 00, 00, 1360 data 3F,3C,00,OA,4E, 4E, 1370 data 2F,00,3F, 3C, 00, 09, 1380 data 4E,41,54,8F, 48, 40, 1390 data 46,6F,72,6D,61,74, 1400 data 72, 61, 6D, 20,53, 2E, 1410 data 0A,0D,20,5B,46,31, 1420 data 2E,2E,2E,2E,2E,2E, 1430 data 46,32,5D,20,54,72, 1440 data 2E,2E,2E, 2E, 3A, 20, 1450 data 53, 65, 63, 74,6F, 72, 1460 data 3A,20,20,39,OA, OD, 1470 data 65,20,2E,2E,2E,2E, 1480 data OA,OD,20,5B, 46, 38, 1490 data 2E,2E,OA,OD, 5B, 46, 1500 data OA,OD,OA, 0D,00,46, put l,rec:goto 1020 00, 00, 00, 00, 00, 00, IF, 40,00, 00 00, 00, 00, 00, 00,00,20,3C,00,00 61,00,01,E4,B0,3C,00,3B,6D,EC BO, 3C, 00, 3B, 66, 00,00, 14,0A,7 9 OA,79,00,01,00,00,02,F4,60,CC 00,14,OA,79,00,02,00,00,02,5A 02,F6,60,B2,B0,3C,00,3D,66,00 00,00,02,FA,OA,79,11,09,00,00 00,3E,66,00,00,16,OA,79,00,03 00,01,00,00,02,FC,60,00,FF,7E 00, OA, 61, 00,00, 12, 60, 00, FF, 6E FF,66,42,67,4E,41,20,3C,00,00 33,F9,00,00,02,F6,00,00,02,F8 33, F9, 00, 00, 02,F4,00, 00,02, FE 00, CC, 53, 79, 00,00, 02,FE,6A,F0 6A,DE,42,67,70,02,80,79,00,00 01,00,00,00,48,79,00,00,03,00 DF,FC,00,00,00,OE,41,F9,00,00 00, 09, 00, 00, 02,FA, 67, 00, 00,IE 32,39,00,00,02,F6,4A,79,00,00 E3,49,61,00,00,50,OC,79,00,50 00,16,32,3C,00,12,4A,79,00,00 E3,49,61,00,00,30,3F,3C,00,01 3F,39,00,00,02,FC,42,A7,48,79 00,09,4E,4E,DF,FC,00,00,00,14 60,00,FE,84,14,30,00,14,El,4A 11, 82,00, 13, EO, 4A, 11, 82,00, 14 02,D6,61,00,00,46,61,00,00,4E 2F, 3C, 87, 65, 43, 21,3F, 3C, 00, 01 3F,39,00,00,02,F8,3F,39,00,00 02,FC,42,A7,48,79,00,00,03,00 DF,FC,00,00,00,1A,4A,40,4E,75 4E,41,5C,8F,4E,75,3F,3C,00,01 4E,75,IB,45,2A,2A,2A,2A,2A,20 74,69,6E,67,2D,2D,50,72,6F,67 44,2E,20,2A,2A,2A,2A,2A,OA,OD 5D,20,53,69,64,65,28,73,29,20 2E,2E,3A,20,20,32,OA,OD,20,5B 61,63,6B,73,20,2E,2E,2E,2E,2E 38,30,OA,OD,20,5B,46,33,5D,20 73,2F,74,72,61,63,6B,20,2E,2E 20,5B,46,34,5D,20,44,72,69,76 2E,2E,2E,2E,2E,2E,3A,20,20,41 5D,20,46,6F,72,6D,61,74,20,2E 31,30,5D,20,51,75,69,74,20,21 6 F, 72, 6D, 61, 74,74, 69, 6E, 67,2E 57 Abacus Atari ST Disk Drives Inside and Out 1510 data 20,50,6C,65,61,73,65,20,77,61,69,74,2E,2E,2E,0A 1520 data 0D,00,2A,2A,20,41,6E,20,65,72,72,6F,72,20,6F,63 1530 data 63,75,72,72,65,64,20,21,21,20,2A,2A,OA,OD,00,00 1540 data 00,01,00,50,00,50,00,09,00,00,00,00,00,00,00,02 1550 data 24,08,12,08,12,08,12,08,26,OA,04,06,06,04,OE,08 1560 data OC,OE,12,OA,10,06,12,OE,1A,08,34,IE,06,06,06,08 1570 data 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 1580 data * 1590 close l:if so 48541 then print"ERROR IN DATA!":end 1610 print "Ok." Some comments about the program: • The only way to copy a normal diskette to an extended-capacity diskette is file by file. The operating system will not copy the disks directly because of the different disk formats. • It is not directly possible to use an extended-capacity diskette as a TOS system disk, because there is no loader in the boot sector. To make such a diskette bootable, the boot sector of another diskette must be copied and modified with a disk monitor to take into account the parameters of the extended-capacity diskette. • Do not use extended-capacity diskettes for storing very important and unique data. If the diskettes are not very high quality, one or more sectors can go bad in the inner tracks. 3.2.2 The BIOS parameter block Back to theory. As we mentioned before, the BIOS parameter block (BPB) is made up of a variety of information. Let's take a closer look at this BPB. Some entries in this parameter block will look familiar, because they are also present in the boot sector. The BPB is created by calling the BIOS command GETBPB (number 7), provided the diskette was changed in the meantime. Unlike the boot sector, the data in the BPB is in the normal 16-bit format. It is in the following order: recsize - Sector size in bytes (512) clsiz - Cluster size in sectors (2) clsizb - Cluster size in bytes (1024) rdlen - Number of directory sectors (7) 58 Abacus Atari ST Disk Drives Inside and Out fsiz - FAT size in sectors (5) fatrec - Start sector of the second FAT (6) datrec - First data sector (rdlen+fsiz+fatrec= 18) numcl - Number of data clusters (711) bflags - FAT entry size in bit 0: 0=12 bits, 1=16 bits (0) The numbers in parentheses are the typical contents of the entries for a double-sided diskette. Now we’ll take a look at a program that reads the BIOS parameter block and analyzes it. The construction of the program is fairly simple. First a prompt that contains the title is displayed. This prompt asks you to enter a letter from the keyboard. This letter is either a drive specifier (a, b, c, or d) or the letter q. Pressing ends the program and returns you to the Desktop. After this input, the program tests to see if a valid letter was entered. If not, the program is restarted. (If a is entered, the program will end). The valid letter entered will then be converted to the value required for the GETBPB call (0-3) by subtracting a. The GETBPB will then be called. The address of the BPB will be returned in register DO. The entries in the BPB can now be read, printed in hexadecimal, and given appropriate labels. All of the information about the diskette can then be seen at a glance. Here is the program, written with the AssemPro assembler: ;** BPB-Analyzer S.D. ** run: move.1 #prompt,dO bsr pmsg /Prompt output bsr getkey /input the drive A-D cmp =#= a o /Quit ? beq quit /yes => Desktop move dO, d6 /save charavter bsr pcrlf /CR output sub #'a',d6 /value to small bmi run /false input cmp #3,d6 bgt run /false input 59 Abacus Atari ST Disk Drives Inside and Out move d6,-(sp) ;Device-Nr. move #7, -(sp) trap #13 ;GETBPB-Function addq.1 #4, sp tst. 1 dO beq run /Error ! move.1 dO, a5 /store BPB-Address bsr pnext move.1 #bps,dO bsr pline /"Bytes per Sector" bsr pnext move.1 #spc,dO bsr pline /"Sectors per Cluster" bsr pnext move.1 #bpc,dO bsr pline /"Bytes per Cluster" bsr pnext move.1 #dirsec,dO bsr pline /"Directory-Sectors" bsr pnext move.1 #fatsec,dO bsr pline /"FAT-Sectors" bsr pnext move.1 #fat2s,dO bsr pline /"Start-Sector of 2. FAT bsr pnext move. 1 #datsec,dO bsr pline /"Start-Sector of Data" bsr pnext move. 1 #datc,dO bsr pline /"Data cluster" move #'$',d0 bsr pchar /"$" output move #12,dO /12 Bit btst #0,(a5) /correct ? beq bitsl2 /yes move #16,dO /else 16 Bit 60 Abacus Atari ST Disk Drives Inside and Out bitsl2: bsr phexbyt move .1 #fatbit,dO bsr pline /"Bits per FAT-entry" bra run /ready => New start quit: / Exit to Desktop clr - (sp) trap #1 getkey: /Get Key -> DO move # 1 , -(sp) trap #1 and. 1 #$ff,dO addq .1 rts # 2 , sp pline: /Print Line/CR bsr pmsg pcrlf: /Print CR, LF move #10,dO bsr pchar move #13,dO pchar: /Print Character DO move dO,-(sp) move # 2 , -(sp) trap #1 addq .1 rts #4, sp pmsg: /Print Line (DO) move .1 dO, -(sp) move #9, -(sp) trap #1 addq rts # 6 , sp pnext: /get next word and output move #'$',d 0 bsr pchar /"$" output move (a5) +, dO phexword: /Print Hex-Word DO moveq #3, dl bra phexl 61 Abacus Atari ST Disk Drives Inside and Out phexbyt: ;Print Hex-Byte moveq #l,dl rol.l #8,d0 phexl: rol.l move.1 move.1 bsr move.1 move.1 dbra rts phexnib: and.1 #$7f,d6 swap dO and.l #$0f,d0 add.b #$30,dO cmp.b #$3a,d0 bcs phexn add.b #7,d0 phexn: bra pchar /Nibble output prompt: dc .b ■■*** BPB-Analyzer S.D. ***",10,13 dc.b "Input disk drive (a-d) or",10,13 dc .b "'q' for Quit : ",0 bps: dc.b " Bytes per Sector",0 spc: dc. b " Sectors per Cluster",0 bpc: dc. b " Bytes per Cluster",0 dirsec: dc.b " Directory-Sectors",0 fatsec: dc. b " FAT-Sector",0 fat2s: dc.b ": Start-Sector 2.FAT",0 datsec: dc.b Start-Sector of data",0 date: dc.b " Data-Cluster",0 fatbit: dc.b " Bits per FAT-entry",10,13, 0 end Here is the BASIC loader. It creates the BPB analysis program as bpbana . TOS on the diskette: 1000 open"R",1,"a:bpbana.tos",16 1010 field#l,16 as bin$ 1020 a$="":for i=l TO 16:read d$:if d$="*"then 1050 #4, dO dO,-(sp) dl,-(sp) phexnib ;one Nibble (0-F) output (sp)+,dl (sp)+,dO dl,phexl 62 Abacus Atari ST Disk Drives Inside and Out 1030 1040 1050 1060 1070 1080 1090 1100 1110 1120 1130 1140 1150 1160 1170 1180 1190 1200 1210 1220 1230 1240 1250 1260 1270 1280 1290 1300 1310 1320 1330 1340 1350 1360 1370 1380 1390 1400 1410 1420 1430 1440 1450 1470 a-val("&H"+d$):s=s+a:a$=a$+chr$(a):next lset bin$=a$:rec= II a> 0 + put • 1 , , rec:goto 1020 data 60, 1A, 00 , 00 , , 02 , ,44, 00 , 00 , , 00 , 00 , 00 , 00 , 00 , 00 , , 00 , , 00 data 00 , 00 , 00 , 00 , , 00 , , 00 , O o 00 , o o 00 , o o 00 , 20 , 3C, , 00 , , 00 data 01 , 54, 61, 00 , . 00 , , FA, 61, 00 , , 00 , CA, B0, 7C, 00 , 71, , 67, , 00 data 00 , BE, 3C, 00 , ,61, . 00 , 00 , DO, , 9C, 7C, 00 , 61, 6 B, DE, , BC, . 7C data 00 , 03, 6 E, D 8 , , 3F, , 06, 3F, 3C, , 00 , 07, 4E, 4D, 58, 8 F, , 4A, , 80 data 67, CA, 2 A, 40, , 61, , 00 , 00 , D4, , 20 , 3C, 00 , 00 , 01 , 9A, , 61, ,00 data 00 , A2, 61, 00 , . 00 , ,C 6 , 20 , 3C, , 00 , 00 , 01 , AC, 61, 00 , , 00 , , 94 data 61, 00 , 00 , B 8 , . 20 , . 3C, 00 , 00 , , 01 , Cl, 61, 00 , 00 , 86 , . 61, , 00 data 00 , AA, 20 , 3C, . 00 , . 00 , 01 , D4, , 61, 00 , 00 , 78, 61, 00 , , 00 , , 9C data 20 , 3C, 00 , 00 , , 01 , ,E7, 61, 00 , , 00 , 6 A, 61, 00 , 00 , 8 E, , 20 , , 3C data 00 , 00 , 01 , F4, . 61, . 00 , 00 , 5C, , 61, 00 , 00 , 80, 20 , 3C, . 00 , , 00 data 02 , 09, 61, 00 , . 00, ■4E, 61, 00 , . 00, 72, 20 , 3C, 00 , 00 , . 02 , ,20 data 61, 00 , 00 , 40, . 30, , 3C, 00 , 24, . 61, 00 , 00 , 48, 30, 3C, . 00, , OC data 08, 15, 00 , 00 , , 67, , 00, 00 , 06, , 30, 3C, 00 , 10 , 61, 00 , , 00, , 5A data 20 , 3C, 00 , 00 , 02 , . 2E, 61, 00 , , 00, 1A, 60, 00 , FF, 30, 42, , 67 data 4E, 41, 3F, 3C, . 00, .01, 4E, 41, ,C0, BC, 00 , 00 , 00 , FF, ,54, . 8 F data 4E, 75, 61, 00 , , 00, . 1A, 30, 3C, . 00, 0A, 61, 00 , 00 , 06, , 30, , 3C data 00 , 0D, 3F, 00 , . 3F, , 3C, 00 , 02 , , 4E, 41, 58, 8 F, 4E, 75, , 2F, , 00 data 3F, 3C, 00 , 09, . 4E, ■41, 5C, 4F, ■4E, 75, 30, 3C, 00 , 24, , 61, ,E2 data 30, ID, 72, 03, . 60, . 00, 00 , 06, 72, 01 , El, 98, E9, 98, ■ 2F, 00 data 2 F, 01 , 61, 00 , 00 , . OC, 22 , IF, 20 , IF, 51, C9, FF, F0, 4E, . 75 data CC, BC, 00 , 00 , 00 , 7F, 48, 40, CO, BC, 00 , 00 , 00 , OF, DO, 3C data 00 , 30, B0, 3C, 00 , 3A, 65, 00 , 00 , 06, DO, 3C, 00 , 07, 60, A2 data 2 A, 2A, 2 A, 20 , 42, 50, 42, 2D, 41, 6 E, 61, 6 C, 79, 7A, 65, 72 data 20 , 53, 2E, 44, 2E, 20 , 2 A, 2 A, 2A, 0A, 0D, 49, 6 E, 70, 75, 74 data 20 , 64, 69, 73, 6 B, 20 , 64, 72, 69, 76, 65, 20 , 28, 61, 2D, 64 data 29, 20 , 6 F, 72, 0A, 0D, 27, 71, 27, 20 , 66 , 6 F, 72, 20 , 51, 75 data 69, 74, 20 , 3A, 20 , 00 , 20 , 42, 79, 74, 65, 73, 20 , 70, 65, 72 data 20 , 53, 65, 63, 74, 6 F, 72, 00 , 20 , 53, 65, 63, 74, 6 F, 72, 73 data 20 , 70, 65, 72, 20 , 43, 6 C, 75, 73, 74, 65, 72, 00 , 20 , 42, 79 data 74, 65, 73, 20 , 70, 65, 72, 20 , 43, 6 C, 75, 73, 74, 65, 72, 00 data 20 , 44, 69, 72, 65, 63, 74, 6 F, 72, 79, 2D, 53, 65, 63, 74, 6 F data 72, 73, 00 , 20 , 46, 41, 54, 2D, 53, 65, 63, 63, 74, 6 F, 72, 00 data 3A, 20 , 53, 74, 61, 72, 74, 2D, 53, 65, 63, 74, 6 F, 72, 20 , 32 data 2E, 46, 41, 54, 00 , 3A, 20 , 53, 74, 61, 72, 74, 2D, 53, 65, 63 data 74, 6 F, 72, 20 , 6 F, 66 , 20 , 64, 61, 74, 61, 00 , 20 , 44 , 61, 74 data 61, 2D, 43, 6 C, 75, 73, 74, 65, 72, 00 , 20 , 42, 69, 74, 73, 20 data 70, 65, 72, 20 , 46, 41, 54, 2D, 65, 6 E, 74, 72, 79, 0A, 0D, 00 data 00 , 00 , 00 , 02 , 3C, 0E, 0E, 0E, 0E, 0E, 0E, 0E, 26, 00 , 00 , 00 data ★ close 1:if s<> 40341 then print"ERROR IN DATA!":end print "Ok." 63 Abacus Atari ST Disk Drives Inside and Out When the computer is turned on, the BPB data are not available. The operating system doesn't create the BPB until after booting, when the number of connected drives and their designations are known. If you have an early model of the ST (without TOS in ROM), the System Disk must first be booted. Booting also occurs if the computer contains an operating system but the disk contains a bootable operating system (TOS. IMG) and the boot sector is executable. Booting takes place in four steps: 1) The boot sector is loaded and the boot program contained in it is executed. 2) The FAT and the directory are loaded from the current diskette. The loader searches for the given filename (usually TOS . IMG). If it is not found, it will return an error message. 3) TOS . IMG is loaded at memory address $40000. 4) The loaded program is started. The file TOS . IMG, for its part, consists of three parts: • A relocator, which is a program that moves the operating system to the address at which it was intended to run ($6100). This program clears the screen, moves the TOS image block to its original address and then starts it there. • The operating system data (BIOS, XBIOS). • The GEM data and the Desktop program. As you can see, the construction of the operating system in the file TOS . IMG is pretty complicated. The built-in TOS, which is contained in 6 PROM chips (Programmable Read Only Memory), is naturally somewhat shorter because it contains only the operating system with GEM and no relocator. Let's continue looking at data structures on the disk with a discussion of the construction and management of the directory. 64 Abacus Atari ST Disk Drives Inside and Out 3.3 The directory On single-sided disks the directory starts at track 1, sector 3 and occupies 7 sectors. In each entry it stores a whole set of data in addition to the filename and extension, data which is more or less important for the management of the diskette. Each entry in the directory consists of 32 bytes, which contain all of the information about the file. These 32 bytes are divided into eight data fields, which are constructed as follows: 1) Filename 8 bytes 2) File type (extension) 3 bytes 3) Attribute 1 byte 4) Reserved 10 bytes 5) Time 2 bytes 6) Date 2 bytes 7) First cluster 2 bytes 8) File size 4 bytes The first field contains the filename. This name consists of ASCII characters, letters and numbers only. Furthermore, only uppercase letters are used. The name is limited to eight characters. If the name has fewer than eight characters, the remaining characters will be blank spaces. If the first byte of a name is zero, it means that the entry has never been used. If the file was already used and then erased, this byte will contain a 229 ($E5). If the first character of the name is a period (.), the entry is for a special subdirectory: a folder. The following field contains the file type, also called the extension. This extension is limited to three characters (such as prg, TOS, bas, etc.) and is also padded with spaces. Again, only uppercase letters are used. After this comes the file attribute byte. It contains a bit code for the status of this entry or file. The meanings of these bits are as follows: Bit Meanin2 when set Ill Bit Meaning when set (1) 0 read only 1 hidden file 2 system file 3 entry is the disk name 4 entiy is a file 5 file was changed 65 Abacus Atari ST Disk Drives Inside and Out After this byte are 10 bytes which are not used. They are reserved and may be used by later versions of TOS. Following these are two bytes which contain the time of the last modification of the file. The time is specially coded to save space. The 16 bits of the time entry are divided into three sections: hours, minutes, and seconds. This division looks like this: Example: 19:21:34 Hour Minute Second/2 10011 010101 10001 The clock in the ST reports the time only in two-second increments, which is why the lowest five bits of the time contain a 17. The next field in the directory contains the date of the last modification of the file. The division into year, month, and day is done like the time. Only seven bits are reserved for the year, which is why the number 1980 must be added to the value returned. Example: 5/12/1986 Year Month Day 0000110 0101 01100 The seventh field of the directory contains the number of the first cluster on the disk which can be used by a file. Files are stored on the disk starting with this cluster, which usually consists of two sectors. More information about what happens after this can be found in the next section. The last field contains the length of the file in bytes. It should be noted that fewer bytes may be read than are indicated here, which also depends on the FAT. The file length should only be seen as the maximum file length. With this information about the construction of the directory on the diskette, you can now analyze the division of the diskette using a disk monitor. Many results can be obtained by changing the values, but most of these manipulations lead to unpleasant results. For this reason, it is a good idea to work with a copy of the diskette rather than risk destroying the original. If we wanted to write a program which read the directory of a disk, we would have to prepare a buffer for the expected data before the 66 Abacus Atari ST Disk Drives Inside and Out corresponding operating system function was called. The address of this buffer is designated the Disk Transfer Address (DTA). This buffer is 44 bytes long and must be specified to the operating system by calling a special function. Once this is done, the search for directory entries can begin. The function SFIRST (Search FIRST) looks for the first matching entry in the directory, and SNEXT (Search NEXT) looks for additional entries. Entries which are found by either function are loaded into the buffer at the DTA. After calling one of these functions, the buffer contains all of the information that also appears in the directory window on the Desktop. The division of the data is as follows: Bvte(s) .Contents 0...20 Reserved 21 File attribute 22,23 Time of modification 24,25 Date of modification 26...29 File size in bytes (LO, HI) 30...43 Filename and extension The machine language routine below sets the DTA and then searches the directory for the specified filename. If the name given is simply * . *, the first entry that corresponds to the given attribute will be returned. If no matching entry is present, the function will return error number - 33 (File not found) in register DO. Otherwise this register will contain zero. MOVE.L #BUFFER,-(SP) ★ Pass DTA MOVE #$1A,—(SP) ★ SETDTA function number TRAP #1 ★ Call operating system ADDQ.L #6, SP ★ Repair stack MOVE #%11001,-(SP) ★ File type: all files MOVE.L #NAME,-(SP) ★ Address of the filename MOVE #$4E,-(SP) ★ SFIRST function number TRAP #1 ★ Call operating system ADDQ.L #8, SP ★ Repair stack TST DO ★ Found BNE NOTHING ★ no etc. BUFFER: .ds.b 44 * Space for the data NAME: .ds.b "*.*",0 * All names allowed 67 Abacus Atari ST Disk Drives Inside and Out To search for the next entry, all we need is: MOVE #$4F,-(SP) TRAP #1 ADDQ.L #2,SP TST DO BNE NOTHING * SNEXT function number * Call operating system * Repair stack * Found * no We can easily write a program to output the directory of a diskette to the printer, for example. Such a program, which prints the entire directory including the contents of all folders, is found in section 5.3. If you look at the directory in the Desktop, you will see the name, extension, date, time, and length of the file. When you then click a program, the operating system needs to know not only where the file begins on die disk, but also where the rest of the file is located. This information is contained in the FAT, which we'll look at next. 3.4 The FAT The FAT (File Allocation Table) normally occupies five sectors on a single-sided diskette. It usually starts at track 0, sector 2 of side 0. The size of this table varies depending on the format used. The table is used to store the distribution of each file on the disk. The reason for this lies in the fact that a file does not necessarily consist of consecutive sectors. Sectors which used to belong to a deleted file are released for storing new data. A new file being written to the disk would be assigned to such free sectors. Occupied sectors are simply skipped. Each sector must therefore have an entry in the FAT to be recognized as either free or allocated. In order to keep the size of the FAT down, every two sectors are grouped together and designated as a cluster. Clusters are numbered from 2 to the end of the disk, and the FAT then contains only one entry for every two sectors. Each entry in the FAT is normally 12 bits long. Some formats use 16-bit entries, but we will not go into that here. Twelve-bit FAT entries mean that two entries occupy three bytes. 68 Abacus Atari ST Disk Drives Inside and Out The first two entries of the FAT contain format information, which is why the numbering starts at 2. Every other entry represents a cluster. A zero in an entry means that the corresponding cluster is free. Naturally, this does not mean that the sectors don't contain any data, since a deleted file isn't actually removed from the disk. When a file is deleted, all that happens is that the first letter of the name in the directory is changed to $E5 and the cluster belonging to the file are released with zeroes in the FAT. The data itself is still present, but hard to find. If a FAT entry contains $FF7, the cluster is unusable. Such clusters are recognized and marked during formatting. If a disk is physically damaged in some way (e.g., scratched), you will notice that the capacity announced after formatting is less usual. However, if such an error occurs in track 0 or 1, the entire diskette is unusable, because these tracks are supposed to contain the boot sector, the FAT, and the directory. When a file is to be loaded, the operating system takes the number of the first cluster of the file from the directory entry. The FAT entry of this cluster then contains the number of the next cluster in the file. The FAT entry of this cluster in turn contains the next number, and so on, until an entry contains $FF. This means that this cluster is the last in the file. A disk monitor can also be used to make changes to the FAT. However, the probability of data loss from doing this is extremely high—be sure to make a copy of the disk before you change anything in the FAT. 3.5 Program construction The Atari ST has a large amount of memory which can hold more than one program at a time. In fact, it is possible to place several programs in memory at the same time and execute them. Simple examples are the desk accessories, which run in the "background" while an application is running. This open memory division causes a problem. When we used to program 8-bit computers, we were used to writing machine language programs that would be stored at a specific location in memory and would run there. This is because the machine language program would address the memory directly or branch via a specific address. 69 Abacus Atari ST Disk Drives Inside and Out But this is impossible on the Atari ST. How can a programmer know in advance where his program will be loaded, and whether or not another program is already resident? Another problem is that the operating system must know the size of the program and how much memory it needs to run. If the program needs additional memory for storing information, this memory may not be overwritten by other programs. As you can see, it won't work if a program file on the disk contains nothing more than the program data themselves. The construction of such a file is the subject of this section. An executable program on the disk (. PRG, . TOS, and . TTP files) is divided into four segments. These segments are the file header, the program with data field, the symbol table (if one exists) and relocation data (if present). Let's look at the first part: the header. 3.5.1 The program header The header is 14 bytes long and contains the lengths of the individual segments. The construction of the header is as follows: $ 00,$01 $02-$05 $06409 $0A-$0D $0E-$11 $1241B Contents $601 A, the machine language command BRA *+$ 1 A Length of the program segment (text) Length of the data segment (data) Length of the additional storage segment (bss) Length of the symbol table 00, reserved The first entry is a machine language command which branches the program execution to the start of the program segment. Following this is the length of the program segment. This segment, generally called the "text" segment, contains the program itself. All addresses which the program uses are set up so that the start of the program is taken as address 0. Data contained in this segment are not changed. 70 Abacus Atari ST Disk Drives Inside and Out The next entry contains the length of the data segment. This segment must follow the program immediately. In a machine language program, the separation between the text and data segments is made with a data instruction. The initialized data, such as strings or tables, are stored here. Uninitialized data, like buffers for disk operations or temporary storage, are contained in the next segment. The fourth entry of the header contains the length of this additional storage. This memory area is called bss. After the program is loaded this memory area will be made available to the program. At the same time, other applications will be prevented from using it. Its contents are not defined—it must be filled by the program. The advantage of the bss segment over the data segment is that this area does not have to be stored in the disk file. Entry number five contains the length of the symbol table. Such a table is seldom present, because it plays no role in the function of the program. A symbol table is appended to the program by a compiler or assembler if the you desire. The symbols correspond to the labels used in the source program for routines or variables. The advantage of such a table is that a symbolic debugger like SID can include labels in a disassembly of the program. Once the test and development phase of a program is completed, the symbol table should be left off to save space. Each entry in the symbol table is seven words long, and contains the name, type, and value of the symbol: lixik Contents $0-$7 Symbol name, ends with zero $8-$9 Symbol type: relocatable, global, or external $A-$C Value, such as address, register number, direct value, etc. The entire symbol table of a program can be read and printed with the program NM68. To do this, enter the foil wing line from the command prompt: NM68 filename By adding >prn : to the command line, the output of NM68 can be directed to the printer. Otherwise the output is displayed on the screen. Back to the construction of the program header: The remaining bytes from $12 to $ IB are reserved for later use, and must be zero. 71 Abacus Atari ST Disk Drives Inside and Out Immediately following the header is the program. As we said, the program can really only work at address $0000. In order to make it run at the address at which it was loaded, all absolute addresses which occur in the program must be changed by adding the actual starting address of the program to the addresses contained in the program. But how does the operating system know that it has to make these changes, or where the absolute address are located in the program? The answer is called a relocation table. 3.5.2 The relocation table Following the symbol table in the program file is the relocation table. This table contains the distances between the longwords which must be relocated. The first longword in this table specifies the offset of the first long word to be changed from the start of the program. After this, bytes are used whose values give the distances between the current longword and the next longword to be changed. If the distance between two such longwords is greater than 254, bytes of value 1 will be inserted until the distance to the next longword is less than 255. The first byte which contains a zero indicates the end of the relocation table. This is also the end of the entire program file on the disk. When a program is loaded, the operating system places the program at a free location in memory and then relocates it. The distribution of the program in memory is somewhat different than it was on the disk. Before the actual program (which the data and bss segments follow) lies what is called the base page. This 256-byte-long base page is another header, which contains information about the actual distribution of the program in memory. The base page is laid out as follows: Bvte Length Lfinlents 00 4 Start address of the working memory 04 4 HI address of the working memory + 1 08 4 Start address of the program OC 4 Length of the program segment in bytes 10 4 Start address of the data segment 14 4 Length of the data segment in bytes 18 4 Start address of the bss segment 1C 4 Length of the bss segment in bytes 2C 4 Pointer to the "environment string" 80 80 Command line text (for . TTP programs) 72 Abacus Atari ST Disk Drives Inside and Out All other entries in the base page are reserved. The computer isn't the only one that needs the information in the base page. A program can also make good use of it. The best example of this is the command line. If the program is of type . TTP, the operating system displays a dialog box when the program is called in which die user can enter the command line. This line can then be evaluated by the program. To get the address of the command line, a command sequence like the following must be at the start of the program: run: MOVE.L 4(SP),A0 ;Address of the base page LEA $80(AO),AO AO now contains the address of the command line, and the line can be processed. 3.6 Hard disk format Now let's turn to the hard disk. Because of its enormous storage capacity, the hard disk's organization is not quite so simple as that of the diskette. A hard disk is divided into four individual sections, each of which contains a boot sector. These individual sections are called partitions. The first sector on the hard disk (logical sector 0) contains the information about the partitioning of the hard disk. This information is stored as follows: Bvte Name Meaning $1C2 hd siz Total size of the hard disk in logical sectors $1C6 P0 fig Partition 0 exists if pO f lg>0 If bit 7, booting starts here $1C7 pO id Partition ID (GEM) $1CA pO St Logical sector # of the first sector in the partition $1CE p°_ siz Size of the partition in sectors $1D2 Pi fig $1D3 Pi id see above, partition 1 $1D6 Pi St 73 Abacus Atari ST Disk Drives Inside and Out Bxt.e. Name Meaning $1DA pl_siz $1DE p2 fig $1DF p2 id see above, partition 2 $1E2 p2 st $1E6 p2 siz $1EA p3 fig $1EB p3 id see above, partition 3 $1EE p3 st $1F2 p3 siz $1F6 bsl st Starting sector of the bad sector list $1FA bsl cnt Number of defective sectors The bad sector list is created when the diskette is formatted. It contains a list of the defective sectors which could not be formatted. The table is usually stored at the end of the hard disk. The operating system uses the variable p*_f lg to determine whether the given partition exists or not ( p*_f lg not equal to zero). The first sector of each partition contains a boot sector which contains the BPB. The operating system boots from the first boot sector whose p*_flg has bit 7 set. Note: A program for analyzing and displaying the partition parameters is found in Section 5.1.1.4. 74 Chapter Four Abacus Atari ST Disk Drives Inside and Out The disk drives Probably the most common media for data storage are floppy diskettes. These disks, measuring 3 1/2 and 5 1/4 inches in diameter have certain advantages. The first is the price. If a 3 1/2" diskette costs about two dollars and stores 360K of data, this comes out to a little over a half a cent per kilobyte. The price per kilobyte is even less with 5 1/4" diskettes. Since no technical problems prevent the use of 5 1/4" diskettes on the Atari, this cost advantage might play a role in which diskette format you'll select. Some ST owners have connected both 3 1/2" and 5 1/4" drives to their computers. Another advantage that diskettes have over hard disks is that you can easily switch between different diskettes. This means that, at least in theory, a disk drive can manage an unlimited amount of data. Also, diskettes are well- suited for copying, exchanging and backing up programs and data. But we should also mention the disadvantages. Except for storage on audio cassette tapes, diskettes are the slowest form of data storage. The Atari ST drives compare favorably with the competition because they allow relatively fast data transfer, using various technical tricks in the ST. Let's look at floppy diskettes in detail. 4.1 Floppy diskette functions When the ST computer needs data from a floppy diskette in one of its drives, various functions are initiated within the disk drive itself. First of all the drive motor is switched on. If two drives are connected, both of them will run, because the signal line responsible for the motor control from the ST is connected to both drives. The advantage of this is that copying from drive to drive is faster—time isn't wasted waiting for the motors to reach the correct speed. The next step is to select a single drive's address. This is done via the drive select line. If a disk drive detects that it is being accessed, the BUSY light goes on and shows that the device is operating. Now comes the decision whether data should be read from or written to the diskette. First the ST must specify the exact track on which the data lies. 77 Abacus Atari ST Disk Drives Inside and Out These tracks are rings that are organized concentrically on the diskette. The read/write head is then moved to a location on the diskette by a small arm, gliding above the rotating diskette to this track's location. The stored data is distributed on these tracks with a system that records and reads the data items as tiny magnetic pulses on the surface of the diskette. To make the distribution of data on a track a little easier to manage, the tracks are divided into sectors. Each track has nine such sectors. In turn, each sector contains 512 bytes of actual data. A sector really contains more data than stated, but this additional data is not immediately accessible. (We'll discuss these special bytes in a later section). The read/write head moving over the rotating magnetic diskette contains a small coil. This coil serves as a magnetic receiver, and recognizes the magnetic pulses that represent the data bits. This method is similar to that used by an audio tape recorder—but much greater precision is required for a diskette. The read/write head can exactly locate every one of almost three million bits on a disk of about 30 square centimeters—the surface area of a 3 1/2" diskette. A single byte, which consists of 8 bits, is stored in a surface of only 0.008 square millimeters! If the computer needs data from the diskette, it requests an individual sector from the disk. Through a complicated process, the disk controller built into the computer decides which of the stream of bits arriving at the read/write head belong to the sector. These data bits are then selected, and its resulting 512 bytes are sent to the computer. All of these procedures present many technical problems for the disk drive manufacturers. The mechanism that positions the head must place it exactly on the desired track (approximately 0.2 mm wide). Then the magnetic pulses which come from the rotating disk must be recognized as zeros or ones. At 300 rotations per minute, there are only about 0.5 microseconds available to read each bit. It's the job of the drive's electronics to sort through the desired information from a huge pool of bits. This is achieved through what are called synchronization bytes. The synchronization bytes are stored at the start of each sector on the disk. As we have seen, a disk drive is an extremely complicated device. We'll only look at the rudimentary construction of the system that processes the information on the diskettes. 78 Abacus Atari ST Disk Drives Inside and Out 4.2.1 The DMA chip Let's start with the ST computer itself. The disk drive sends the requested data through the cable, which arrive at the computer as a flood of bytes. This data must be placed somewhere in memory to be able to use it again. Most computers use their Central Processing Unit (CPU) to receive the data and place it in memory. This means that the speed at which data can be received is limited by the speed of the microprocessor. The Atari ST does things differently, however. Data is received and distributed in memory by a special component which has direct access to the memory just like the CPU. This component is the DMA (Direct Memory Access) chip. The DMA chip is under the control of the CPU, but it performs its task completely independent of the processor. As a result, the CPU can perform other tasks while data is being transferred. Moreover, the DMA chip can move data much faster than the CPU could. The result of this advanced feature is that a very high data transfer rate for diskette operations can be achieved—especially for hard disk operations. The DMA chip occupies the following memory locations in the Atari ST: $FF8604 FDC access/sector count. This is where the registers of the DMA chip or the FDC chip are accessed, the selection of which is determined by $FF8606 $FF8606 DMA mode/status. Bits 0-2 reflect the status of the DMA and FDC chips when reading. Writing to this 16-bit register sets the mode of the DMA chip. $FF8609 DMA memory vector HI byte $FF860B DMA memory vector MID byte $FF860D DMA memory vector LO byte These three bytes make up the 24-bit address at which or from which data is to be transferred by the DMA chip. These bytes must be stored in the order LO, MID, HI. 79 Abacus Atari ST Disk Drives Inside and Out The DMA chip used in the ST is connected directly to the hard disk interface. The connection to the floppy disk drives is not tied directly to the DMA chip. Between this connection and the DMA chip is a component that prepares die serial data arriving from the disk drive or sends the data to the drive serially. This component is the floppy diskette controller, which also controls the functions of the drive. The next section explains the programming of these two devices. 4.2.2 The disk controller This lengthy section deals with the WD1772 floppy diskette controller (hereafter referred to as the FDC, or simply the controller) used in the Atari ST. For the description of this component, we gathered all of the available information and data sheets on the chip that we could find. Naturally, this alone wasn't good enough for a comprehensive treatment, because theory and practice often differ from each other. It was necessary to experiment with the WD1772 ourselves to verify the information we had—and to discover deviations from this information. As a result, this section contains more than enough information for those who just want an overview of the controller. This section also contains information for programmers who want to know how the FDC works, and want to control it directly from their own programs. It's not necessary to program the FDC yourself for normal data exchange between the disk drive and the ST. Appropriate calls to the BIOS or XBIOS will handle this. However, the operating system does not support all of the capabilities of the FDC. For programmers who want to develop a fast copy program or devise some form of copy protection, for example, these missing functions are quite important. If you want to create special disk formats, you cannot use the existing operating system routine for track formatting—you will have to write your own. To use these functions in an application program, the controller must be programmed directly. This is only possible if you have a good knowledge of the FDC commands and the way they work. This knowledge can save you hours of programming and debugging, only to find out that your idea would never work. An example of such an idea: 80 Abacus Atari ST Disk Drives Inside and Out "If I read all of the tracks of a diskette into the computer with the READ TRACK command and then write all of the tracks onto another diskette with the WRITE TRACK command, I'd have the fastest copy program imaginable. And I could use this to make 'backups' of copy¬ protected disks. The READ TRACK command reads all of the information on the track (including the copy protection), and the WRITE TRACK command will write all of it back out again!" If you actually wrote a program that worked this way, however, you would find that the copies that it created were unusable—and it wouldn't even copy an unprotected diskette. After you have worked through this section and know how the FDC commands work and what they do, you'll see why the copy program above won't work. We are reasonably sure that the description of the controller is comprehensive enough to inhibit "ideas" of this sort. Now to the description of the WD1772 (finally!). Developed by Western Digital, this chip incorporates all of the functions necessary for controlling a 5 1/4" drive. As the Atari ST demonstrates, the WD1772 can also control 3 1/2" drives. This capability of the WD1772 comes thanks to Sony, the developer of the 3 1/2" drive. Sony decided that a faster market introduction would be possible if the 3 1/2" drives were equipped with an interface compatible with 5 1/4" drives. From an ST owner's point of view, this means that you can also connect 5 1/4" drives to your computer. But be careful with older disk drives—especially rebuilt or used ones—which can be had for very low prices. If you want to connect this type of device as a foreign drive, you may run into problems for the following reason: The standard version of the WD177x series (the WD1770) is software compatible with the older FDC series WD179x and WD279x. But the WD1772 uses shorter stepping rates than these other models. The stepping rate is the time the controller waits between tracks when moving the read/write head across the disk. The four programmable times of the WD1772 are 2, 3, 5 and 6 ms, while on the WD1770 they are 6, 12, 20, and 30 ms. This means that the drive must be capable of changing tracks in a maximum of 6 ms. You should find the stepping rate information in the documentation for the drive under "TRACK TO TRACK." 81 Abacus Atari ST Disk Drives Inside and Out Let's look at the FDC now and start with a brief summary of the features of this chip: • 28-pin Dual-Inline-Package • single 5 V supply • integral digital data separator • integral write precompensation • single and double write density • integral motor control • sector lengths of 128, 256, 512, or 1024 bytes • fast stepping rates (2, 3, 5, and 6 ms) We want to explain two of these points right now and we'll get to the rest in the following sections, in connection with the individual functions of the FDC. 1) The fact that the WD1772 is contained in a 28-pin package is important only for the development of a system in which an FDC is required. Since a 28-pin chip means less layout work than a 40-pin device, system developers are more likely to choose devices with fewer pins (assuming the features are equivalent). 2) The ability to operate the WD1772 in single or double density will not be treated in the course of the controller discussion. The reason for this is simple: In the Atari ST, the FDC is used in double-density mode. To use the controller in the single-density mode, the computer must be opened and wiring of the FDC changed. The result of this undertaking would be that only 50% as much information could be stored on a disk as before. Although it doesn't seem like there would be any reason to do this (after all, why would anyone throw away half the storage capacity?), it might be useful to do this in practice to create a disk format compatible with some other computer. Special cases like this are not of general interest, however. Since the FDC is already complicated enough, we won't burden you with capabilities which will probably never be used in the ST. 82 Abacus Atari ST Disk Drives Inside and Out 83 Abacus Atari ST Disk Drives Inside and Out PIN 1 CS (CHIP SELECT) A low signal on this input selects the chip and allows access to the registers. The CHIP SELECT connection is found on all peripheral components including the memory chips. Since they are all connected to the data bus of the processor, there has to be some way of keeping them from all trying to use the bus at once. The CHIP SELECT lines enable just one device for data transfer. In the ST, the FDC is selected by the DMA controller. PIN 2 RAV (READAVRITE) The signal at this input controls the data direction. If it is high, the contents of the selected register are output on DAL0-DAL7, while if it is low, the data on DAL0-DAL7 is placed in the selected register. PINS 3,4 A0,A1 (ADDRESS 0,1) These two inputs select the FDC register. The WD1772 has 5 registers which are addressable by the computer system. But since two address lines can select only four registers, one address has two registers (A0=0 and A 1=0). The signal on the RAV pin is used to decide between these two registers. CS A1 AO R/W = 1 R/W = 0 0 0 0 0 0 1 0 10 Oil Status reg. Track reg. Sector reg. Data- reg. Command reg. Track reg. Sector reg. Data reg. The result is that the command register cannot be read and the status register cannot be written. These pins are connected to the DMA controller and not the processor address bus. The FDC registers are selected via a control register in the DMA controller. PINS 5-12 DAL0-DAL7 (DATA ACCESS LINE 0-7) These eight lines make up the bidirectional data bus. The data between the computer system and the FDC registers are transferred over this bus. These lines, like the address lines, are connected to the DMA controller. The FDC registers are accessed indirectly via the data registers of the DMA controller, which is selected via the control register of the DMA controller. 84 Abacus Atari ST Disk Drives Inside and Out PIN 13 MR (MASTER RESET) After power is supplied to the FDC, the contents of its registers are purely random and the registers must be placed in a defined initial state. This is achieved by a low pulse (of at least 50|is) at this input. This is usually done after the ST is turned on. Naturally, it is always possible to reset the FIX! with the 68000's RESET command. But remember, the other devices connected to this common line will also be reset if you execute this command. PIN 14 GND (GROUND) Ground connection. PIN 15 Vcc (POWER SUPPLY) The +5V supply connection. PIN 16 STEP (STEP) This output sends a pulse to the drive for each step the read/write head is to be moved. PIN 17 DIRC (DIRECTION) The FDC uses the signal on this line to tell the drive the direction in which to the move when it encounters a STEP pulse. If this pin is high, a STEP pulse moves the head toward the center of the disk, while if it is low, a STEP pulse moves the head one track out. PIN 18 CLK (CLOCK) Like a microprocessor, a command issued to the FDC executes microprograms within it. This is why the FDC is supplied with a clock, just like a microprocessor, which controls the execution. This clock is also required for the timing of the serial data stream. The clock is not created by the FDC itself but is taken from the outside via the CLK input. The clock frequency is 8 MHz. PIN 19 RD (READ DATA) The signal which the read/write head of the drive sends is connected to this input of the FDC. The data separator in the FDC separates the clock and data pulses, which are both contained in the signal. 85 Abacus Atari ST Disk Drives Inside and Out PIN 20 PIN 21 PIN 22 PIN 23 PIN 24 PIN 25 MO (MOTOR ON) This output is used for motor control. The drive motors are started by the FDC for read, write, and seek operations. WG (WRITE GATE) The data pulses which the FDC sends to the drive are first sent to a write amplifier instead of directly to the read/write head. If no data items are sent by the FDC, the input of this amplifier is open. Amplifiers with open inputs have the unpleasant property of being sensitive to stray voltages that might enter the connection cable. To keep such voltages from reaching the read/write head and thereby destroying data on the disk, the drives have a circuit which controls the write amplifier. For write operations, WRITE GATE is set to a high signal by the FDC. This enables the write amplifier, and the data pulses that arrive over the WRITE DATA line can be processed. WD (WRITE DATA) The information to be written to the disk, consisting of data and clock pulses, is sent to the drive via this line. TR00 (TRACK 00) The disk drives have a light barrier which detects when the read/write head is over track zero. When the head is over track zero, this input of the FDC is set to low. IP (INDEX PULSE) The drive sends a pulse to this pin on each revolution of the disk, which the controller then uses for its operations. For example, it uses this signal to detect the start of track when reading or writing. The speed of the drive motor can also be measured by counting the index pulses in a given length of time. The index pulse on the 3 1/2" drives is created independent of the diskette. 3 1/2" diskettes, unlike 5 1/4" diskettes, do not have an index hole. WPRT (WRITE PROTECT) This input is tested before the FDC attempts a write operation. If this input from the drive is placed low (write-protected diskette), the controller will terminate the write operation. 86 Abacus Atari ST Disk Drives Inside and Out PIN 26 DDEN (DOUBLE DENSITY ENABLE) The signal at this input determines the recording format with which the FDC works. A low signal at this pin puts the controller in the double-density mode, while a high places it in the single-density mode. In the Atari ST the WD1772 is always operated in the double-density mode because DDEN is connected to ground. PIN 27 DRQ (DATA REQUEST) This output, the condition of which is indicated by a bit in the status register, has the following meaning when it is placed high by the FDC: a) For a read operation, a byte is in the data register which must now be read (data register full). b) For a write operation the data register is empty and must be filled with the next byte to be written. Reading or writing the data register resets the DRQ output and the DRQ status bit. The DMA capability of the WD1772 rests on the existence of this output. In the Atari ST, this pin is connected to the DMA controller. During read and write operations, the DRQ status bit must be read to recognize when a data transfer takes place. The DMA controller handles this through the DRQ output. PIN 28 INTRQ (INTERRUPT REQUEST) After each command, this FDC output is set high. It is reset when the status is read. This connection is connected to the I/O port (bit 5) of the MFP 68901. To recognize when the FDC has finished a command, this port bit is read in a loop. It should be noted that this bit is inverted. The command is ended when the port bit is cleared. It is possible to program the MFP so that a high on the INTRQ output generates an interrupt. This saves having to poll the port bit continually. The interrupt control is not used by the operating system. 87 Abacus Atari ST Disk Drives Inside and Out 4.2.2.2 Organization To better understand the programming of the FDC, which is explained in detail later, it's a good idea to take a look at the function diagram of the WD1772 and then talk about the individual function blocks. DAL 0-7 WD1772 organization 88 Abacus Atari ST Disk Drives Inside and Out Data shift register (DSR) During a read operation, the serial data which arrive over the READ DATA input (RD) are collected in this 8-bit shift register. For write operations the contents of this register are sent out in serial over the WRITE DATA output (WD). For some operations the data shift register is also used for temporary storage. Data register (DR) For a read/write operation, this register is used as temporary storage. When the DSR has received eight bits after a read operation, this information is transferred to the data register. For write operations, the next byte is transferred to the DSR after the DSR has sent a byte. For a seek operation, the data register contains the number of desired track. Track register (TR) This register normally contains the track over which the read/write head is located—but there are exceptions to this. To keep the track register up to date, it is incremented by one for each step in and decremented by one for each step out. The commands RESTORE and SEEK increment or decrement the register every time. However, this is only true for the commands STEP, STEP IN and STEP OUT if the update flag (U bit) is set. For write, read, and verify operations, the contents of the track register are compared with the track number recorded in the ID field. The track register can be read and written, but it should not be loaded during an operation. Sector register (SR) For read and write operations, this register contains the number of the desired sector, which is compared with the sector number recorded in the ID field. After a READ ADDRESS command, the sector number from the ID field is in the register. The sector register can be read and written, but it should not be loaded during an operation. 89 Abacus Atari ST Disk Drives Inside and Out Command register (CR) This register contains the command currently being executed. It can only be written because reading automatically selects the status register. The command register should not be loaded during an operation, except when the command is FORCE INTERRUPT. Status register (STR) The information found in this register indicates the status of the FDC/drive. The individual bits are partially dependent on the command being processed. The status register can only be read, whereby the byte read has the following meaning(s): Bit 7 MOTOR ON This bit reflects the status of the MOTOR ON output. It is set for about one to two seconds after a command, if the busy bit is already cleared. Bit 6 WRITE PROTECT After write operations, this bit indicates whether or not the diskette in the drive is write protected. If it is set, it also means that the desired write operation was not executed. The WPRT bit is set (in the case of a write-protected diskette) after a type 1 command. This bit is reset when an operation with a non-write-protected diskette takes place. Bit5 SPIN UP/RECORD TYPE SPIN UP: For type 1 commands this bit is set at the conclusion of the spin up sequence. This shows that the drive motor has (probably) reached its nominal rotation rate. RECORD TYPE: After a READ SECTOR command, this bit indicates whether the data field starts with a "normal" or an "erased" data mark. Bit 5 = 0, "normal" data mark ($FB) Bit 5 = 1, "erased" data mark ($F8) Bit 4 RECORD NOT FOUND (RNF) If no correct ID field was found, this bit is set. This could be the case after a READ SECTOR, WRITE SECTOR, or READ ADDRESS command. However, after a READ 90 Abacus Atari ST Disk Drives Inside and Out SECTOR command, the RNF bit can be set despite a correct ID field. This is the case if no data mark was found within the 43 bytes which follow the last CRC byte in the ID field. Bit 3 CRC ERROR This bit is set if the contents of the CRC field in the data field or the ID field do not match the contents of the CRC register. Bit 2 LOST DATA/TRACK 00 LOST DATA: If no action is taken after a data request (indicated by the DRQ output or DRQ status bit) within the required time for a type 2 or type 3 command, this bit is set. TRACK 00: For type 1 commands this bit is set when the read/write head is on track zero. Bit 1 DATA REQUEST / INDEX DATA REQUEST: This bit is set when data is ready or required for type 2 and type 3 commands. It is reset by reading or writing the data register. INDEX: For type 1 commands this bit is set during an index pulse. BitO BUSY This command is set during the execution of a command. CRC Logic To avoid read errors, a process must be used that supports high data security. The process used here generates 16-bit checksum from the written data according to a specific algorithm and this checksum is written after the data on the diskette. When these data items are read again the checksum is generated again according to the same algorithm. If this checksum matches the recorded checksum, you can be almost 100 percent certain that the data was read correctly. The complicated algorithm makes it highly improbable that the checksums would match if there were read errors. These 16-bit checksums are called Cyclic Redundancy Checks (CRC). The CRC logic is responsible for the creation and control of the checksums. The 91 Abacus Atari ST Disk Drives Inside and Out calculation is performed in hardware to speed the operation up. The hardware calculates the sum using the following polynomial: CRC(x) = x 16 + x 12 + x 5 + 1. Arithmetic Logic Unit (ALU) The ALU is used for register modification (increment, decrement). The ALU is also used for comparisons between registers and the information contained on the disk in the ID fields. Address Mark Detector This detector is probably the most important part of the FDC. As its name indicates, this part is capable of recognizing an address mark. This mark designates the start of an ID field (index address mark) or the start of a data field (data address mark). But why do we need a special detector? As an example, let's take the value $FE. The controller interprets this value as an index address mark. Even without a special detector it isn't difficult to find this $FE. However, the problem becomes clear when we consider that this value can occur in a data field, where it must not be evaluated as an index address mark. How is it possible to distinguish one $FE from the other $FE? The address mark detector takes care of this for us. As we mentioned before, the recorded information consists of more than just data pulses—clock pulses are also found in it. For read operations these are filtered out of the signal by the data separator and sent to the address mark detector. For a WRITE TRACK command, values which are larger than $F4 require special handling. The common feature of these values is that they are written without clock pulses. Values written like this consist only of data pulses. When this information is read, no clock pulses will arrive from the data separator because none are present in the signal. The address mark detector is activated by the absence of clock pulses. It can only recognize an address mark when it is written without clock pulses. There is another problem, however. This one you probably didn't know anything about, because we have always talked about "complete" data bytes. These bytes are recorded in serial, but the start of a byte is not marked in any way. When eight bits are collected in the DSR by a read 92 Abacus Atari ST Disk Drives Inside and Out operation, we can't just assume that they belong to a single data byte. They could just as well be four bits from each of two different data bytes. The address mark detector will recognize an address mark only if the collection of data bytes happens to be byte-synchronized. It's easy to see that we can't leave this up to chance—there has to be a way of recognizing the start of a byte. This is done through the synchronization byte. These are written (three of them) before each address mark when formatting a track. The SYNC bytes, like the address marks, don't contain any clock pulses, and therefore they activate the address mark detector. The controller, informed of the status of the address mark detector, reads the serial data bits until the contents of the DSR correspond to a SYNC byte. After this, the next bit must be the first bit of the following byte. One thing should be noted, however. The bytes read can be corrupted while the detector is enabled (see the READ TRACK command). When and why are the bytes corrupted? If the detector is activated by missing clock pulses, the FDC assumes that SYNC bytes and an address mark follow. In the synchronization phase (following the reconstruction of a SYNC byte), some of the data bits are discarded. If the detector "accidentally" trespasses on a data field looking for an address mark, the data up to the time the "false alarm" is recognized and changed. Since the address mark detector is so sensitive, it overreacts to missing clock pulses, and is switched off when ID or data fields are being read. Data Separator The data separator was actually described along with the address mark detector. We will mention here again that the data separator's job is to remove the clock pulses from the signal read and to send them to the address mark detector. The interface to the computer system The processor interface consists of eight bi-directional data lines (DAL0-DAL7), the two address lines (AO, Al), the data request (DRQ), the interrupt request (INTRQ), the chip select (CS), the read/write line (R/W), the clock input (CLK), and the master reset input (MR). These connections carry the exchange of data and control signals between the processor and the FDC. 93 Abacus Atari ST Disk Drives Inside and Out The interface to the drive The controller receives the following information: 1) whether a write-protected diskette is inserted (WPRT=1) 2) whether the read)write head is over track 0 (TR00=0) 3) whether the diskette completed a 360° revolution (IP=0) 4) the serial data that was read The signals which the controller sends to the drive are: 1) turn the drive motor on (MOTOR ON = 1) 2) step the read/write head (STEP= 1) 3) the direction of the step (DIRC=0 or 1) 4) turn on the write logic (WG=1) 5) the serial data to be written (WD) Process control When a command is performed by the FDC, the individual functions must be turned on and off in a certain order. Moreover, data items are transferred between the FDC registers, calculations are made, input lines are read, and output line status is changed. The entire process, depending on the command, is controlled or supervised by the process control. Read operations Read operations generally take place only as a result of the READ SECTOR command. The other commands, which can also be used to read data from the diskette, are used only for diagnostic purposes and have no meaning for normal operation. The sectors can be 128, 256, 512, or 1024 bytes long. The sector length is determined at formatting by the length field (the fourth byte in the ID field). When a sector is to be read, the controller uses the length field to figure out how many bytes must be after the data address mark. An precondition for error-free reading of the data byte is that the address mark detector is turned off during this time. Otherwise it may create read errors. 94 Abacus Atari ST Disk Drives Inside and Out Write operations Before data can be written to the diskette with a write command, the WRITE GATE output of the FIX! must be activated. As protection against accidental writing, this is not done until the data register is loaded as a reaction to the DRQ output set by the FDC. If this is not done, the command execution is completed, INTRQ is set, the LOST DATA status bit set, and the BUSY status bit is cleared. If a reaction is made to the first data request, the write command will be executed. If the FDC does not transfer another data in response to a subsequent data request, the command will not be terminated and a "zero-byte" is written instead. The LOST DATA bit is also set after the command is completed. If only 112 bytes were transferred by a WRITE SECTOR command, the controller would fill the remaining 400 bytes with zeroes (assuming a sector size of 512 bytes). This should not be used to erase part of a sector. Since the LOST DATA bit is set in any event, there is no way to tell if an error occurred when writing actual data or not. Writing is usually suppressed when the WRITE PROTECT input is marked low. In this case, any write command is terminated immediately, INTRQ is placed high, the WRITE PROTECT status bit set, and the BUSY status bit cleared. To increase the data security when the write density increases (on the inner tracks), it is possible to enable what is called write precompensation. If the write pre com pensation bit is cleared in the write command, the data stream over WRITE DATA is sent 125 nanoseconds earlier or later, depending on the bit pattern to be written. The following table shows the cases: X 1 1 0 earlier X 0 1 1 later 0 0 0 1 earlier 1 0 0 0 later I I I I I I I I_next bit to be transferred I I I_bit currently being sent I_I_ previously transferred bit 95 Abacus Atari ST Disk Drives Inside and Out The write precompensation is normally enabled on the inner tracks of 5 1/4" diskettes, where the write density is the highest. The precompensation is usually constantly enabled for 3 1/2" diskettes—the data density on their outer tracks already reaches the density of the middle tracks on a 5 1/4" diskette. 4.2.2.3 Command description Now that we have seen the internal construction of the FDC and gone into some of the basic processes involved in read and write operations, we will talk about the commands. The WD1772 recognizes eleven commands, and these are divided into four groups or types. The following table gives an overview of the commands: Type Command Bit 7 6 5 4 3 2 1 0 I Restore 0 0 0 0 h V rl rO I Seek 0 0 0 1 h V rl rO I Step 0 0 1 u h V rl rO I Step in 0 1 0 u h V rl rO I Step out 0 1 1 u h V rl rO II Read sector 1 0 0 m h E 0 0 II Write sector 1 0 1 m h E P aO III Read address 1 1 0 0 h E 0 0 III Read track 1 1 1 0 h E P aO III Write track 1 1 1 1 h E P 0 IV Force interrupt 1 1 0 1 13 12 12 10 These commands have several flag bits which have the following meanings: h = Motor On Flag h = 0, enable motor-on test h = 1, disable motor-on test When the drive motor is switched on, the operating system should delay until the motor has reached its operating speed. The WD1772 accomplishes this delay by waiting 6 index pulses after the motor is turned on. With an operating speed of 300 RPM, this delay time is at least one second. This 96 Abacus Atari ST Disk Drives Inside and Out process, called the spin-up sequence, ensures that the motors have reached their required speed by the time the read/write operations take place. After ending a command, the drive motors will not be turned off until ten more disk rotations are counted (about 2 seconds). If another command occurs during this time, it would be just a waste of time to go through another spin-up sequence. This is why a motor-on test was implemented in the controller. If this test is enabled (h=0), the MOTOR ON output will first be tested. If it is low, the FDC goes through the spin-up sequence. If MOTOR ON is high, however, the controller assumes that the motors are at their operating speed and continues processing the command. If the FDC receives a command (with the h-bit set), it first turns on the drive motors by placing MOTOR ON high. This is regardless of whether the MOTOR ON output is high already or not. It then begins executing the command immediately and does not wait until six index pulses have been received. V = verify flag V = 0, disable verify V = 1, enable verify This flag bit exists only in type-I commands. If it is set, the controller performs a track verification after a step command or after the last step in a restore or seek command. It does this by searching for a correct ID field after the step, whose track number matches the contents of the track register. Whether or not a verify is executed should be determined by the subsequent command, because a verify is not necessarily required and not always sensible. If a READ or WRITE SECTOR command follows and the read/write head is not on the desired track, an incorrect sector will never be written or read because these commands executed a verify. A verify doesn't make sense in certain situations, such as when a new diskette is being formatted. The verify after each STEP command would be negative. Since the controller searches for a correct ID field for the time of five revolutions, about one minute would be wasted while formatting the diskette. If you format a single track on a diskette that already contains data, use a verify to be safe. The WRITE TRACK command, which is used for 97 Abacus Atari ST Disk Drives Inside and Out formatting, does not perform any tests on the track before it is executed. The track over which the read/write head is positioned is formatted. rl, rO = stepping rate 0 0 2ms 0 1 3ms 1 0 5ms 1 1 6ms The step rate of the drives can be programmed with these two bits. This is the delay time between the individual step pulses for a SEEK or RESTORE command. This feature is used to adapt the FDC to the physical limitations of the drive. As an example let's take a drive whose head mechanism requires 6ms per step. If the step rate is set to 3ms and the head is moved with a SEEK command from track 0 to track 40 (which corresponds to 39 step pulses), the head would only reach track 20—every other pulse would be lost due to the mechanical limits. The drive will probably be correctly controlled by individual step pulses (STEP, STEP IN, STEP OUT) because the duration of the step pulse is not affected by the stepping rate. This pulse is determined by the internal timing of the FDC and is about 4|is long. u = update flag u = 0, do not update track register u = 1, update track register If the u-bit is set for a STEP, STEP IN, or STEP OUT command, the track register is incremented or decremented by one, as appropriate, after the operation. This does not mean that the contents of the track register match the actual track. Two conditions must be met for this to happen: 1) The actual track number must have matched the contents of the track register before the step. 2) No attempt can be made to access a track number greater than 82. If the read/write head is on track 82 (the last track the drive can reach), for example, the track register would contain the number 87 after five STEP IN commands, while the head would stay at track 82. 98 Abacus Atari ST Disk Drives Inside and Out m = multiple sector m = 0, read or write one sector m = 1, read or write multiple sectors This bit lets you read or write a maximum of all of the sectors on a track at once. It is assumed that the sector numbers are in a continuous sequence. The number of the first sector to be read or written is written to the sector register beforehand. After the FDC has read/written this sector, it increments the sector register and attempts to read/write the next sector. This continues until no more sectors are found or the command is terminated with a FORCE INTERRUPT command. aO = data address mark aO = 0, write normal data mark ($FB) aO = 1, write erased data mark ($F8) The data address mark designates the start of the data field. The capability to write different data address marks with the aO bit (depending on whether it is set or cleared) allows you to easily mark a sector. The type of data address mark is indicated in the RECORD TYPE status bit after a READ SECTOR command. E = 30ms settling delay E = 0, no head settling time E = 1, 30ms head settling time Some drives have read/write heads that are not constantly in contact with the magnetic disk surface. These drives raise or lower the head with a solenoid. This setup, called head load, is intended to reduce the wear and tear on the diskette by lowering the head only when read or write operations are actually taking place. Lowering the head causes vibrations in the head mechanism, which prevents optimum contact between the head and the storage medium for a given length of time. Setting the E-bit results in a delay that takes this settling time into account. P = write precompensation P = 0, enable write precompensation P = 1, disable write precompensation 10-13 = interrupt conditions 10 = 1, no meaning 11 = 1, no meaning 12 = 1, interrupt on next index pulse 13 = 1, immediate interrupt 10-13 = 0, terminate current command without interrupt 99 Abacus Atari ST Disk Drives Inside and Out The type-I commands The type-II and type-III commands, which are responsible for reading and writing data, always refer to the track that the read/write head is located over at the time. The group of commands consisting of RESTORE, SEEK, STEP, STEP IN, and STEP OUT is responsible for positioning the head. RESTORE (seek TRACK 0) Command word: 7 6543210 0 0 0 0 h V rl rO When the FDC is sent this command, it first tests the TROO input. If the TR00 input is low (because the read/write is already over track 0), the track register will simply be set to zero. If the read/write is not over track 0, STEP OUT pulses will be created until the TROO input goes low. If this is not the case after 255 step pulses, the command is terminated. The end of the command is indicated by the setting of the INTRQ output and the clearing of the BUSY status bit. SEEK Command word: 76543210 0 0 0 1 h V rl rO This command moves the read/write head directly over a given track. The number of the desired track is first written to the data register. For this command to work properly, the contents of the track register must be the current track number. If more than one drive is used, the track register may have to be loaded so that it points to the current track number. When the FDC receives a SEEK command, it compares the track register with the data register, which tells it whether a STEP IN or STEP OUT is necessary. Step pulses are then generated for the appropriate direction. The track register is UPDATEd after each step pulse. Once the contents of the track register and the data register are equal, the destination track has been reached and the command is ended. This is indicated by setting the INTRQ output and clearing the BUSY status bit. 100 Abacus Atari ST Disk Drives Inside and Out STEP Command word: 76543210 0 0 1 u h V rl rO This command causes the FDC to output a step pulse. The direction is the same as that used for a previous step pulse, because the state of the DIRECTION output is not changed. The INTRQ output is set high afterwards, and the BUSY status bit is cleared. STEP IN Command word: 76543210 0 1 0 u h V rl rO A STEP IN command sets the DIRECTION output to high, regardless of what it was before, and sends out a step pulse. This moves the read/write head one step toward the center of the disk. The INTRQ output is set high and the BUSY status bit cleared. STEP OUT Command word: 76543210 0 1 1 u h V rl rO A STEP OUT command sets the DIRECTION output to low, regardless of what it was before, and sends out a step pulse. This moves the read/write head one step toward the edge of the disk. The INTRQ output is set high and the BUSY status bit cleared. The verify sequence for type-I commands If the V-bit is set in a command, the following sequence is executed when the desired track is reached and after a 30 millisecond delay: The track number from the first ID field read is compared with the contents of the track register. If they match, the CRC byte of the ID field is tested. If this matches the byte generated by the CRC logic, the verify sequence is terminated without error. The INTRQ output is placed high and the BUSY status bit is cleared. 101 Abacus Atari ST Disk Drives Inside and Out If either the track number or the CRC checksum in the ID field is wrong, the next ID field is read and a new test made. If an ID field with the correct track number and valid checksum is not found within five revolutions, the RECORD NOT FOUND bit is set in the status register. The command terminates, as indicated by the set INTRQ ouput and the cleared BUSY status bit. Flow chart of the type-I commands To clarify the operation of the type-I commands, here are some flowcharts which demonstrate the processes: 102 Abacus Atari ST Disk Drives Inside and Out 103 Abacus Atari ST Disk Drives Inside and Out Flowchart for Type-1 commands / is the^\ VERIFY flag . set 30 ms delay (head resting time) set INTRO clear BUSY set RNF / have s 6 index impulses '^.occurred 1NO has an^v^ ID address marx \been founc^-' ^ns TRs'v, track number s ^in ID array clear CRC set INTRQ clear BUSY was the^ CRC array correct set CRC error 104 Abacus Atari ST Disk Drives Inside and Out The Type II commands This group of commands is responsible for reading and writing sectors, which are the logical data units. Data exchange is carried out exclusively with these commands. Like all commands which read and write information, these also work relative to the track over which the read/write head is currently located. READ SECTOR Command word:7 6543210 lOOmhEOO The FDC is given the number of the sector to be read in the sector register. When a READ SECTOR command is then started, the controller reads an ID field and tests to see if its track and sector numbers match the contents of the track and sector registers. If this is not the case, the next ID field is read. If they were the same, the sector length is saved and the CRC checksum of the ID field is compared with the one calculated from the data read. If these are the same, then the ID field belonging to the desired sector has been found. Otherwise another ID field is read. Before the data field can be read, a data address mark (DAM) must be found in the next 43 bytes. If a DAM is not found, another ID field is read. If no matching ID field is found after five revolutions, or a DAM did not follow in the 43 bytes after it, the RNF status bit is set and the command terminated. As you can see, certain conditions must be fulfilled before the FDC reads a data field containing a sector. If everything has gone smoothly up to now, then the process continues as follows: The type of data address mark (normal=0, erased=l) is indicated in status bit 5. The address mark detector is disabled and the appropriate number of data bytes (calculated from the specification in the sector-length field) is read. A DRQ is generated for each byte read. If no reaction is made to this DRQ, the FDC sets the LOST DATA status bit 105 Abacus Atari ST Disk Drives Inside and Out After all of the data bytes have been read, a CRC checksum is tested again. This is located in the two bytes following the sector and is compared with the checksum calculated from the sector data. If the two do not match, the CRC error status bit is set. The end of the operation is indicated by setting the DRQ output and clearing the BUSY status bit. READ SECTOR (with m-bit set) If the m-bit is set in the command word of the READ SECTOR command, the FDC tries to read multiple sectors (up to one track). The number of the first sector to be read is passed to the controller in the sector register. The operation of the command is first identical to the one described above. After the sector has been read, however, the controller automatically increments the sector register and starts another READ SECTOR. This continues until no more sectors are found. In other words, this command is always terminated with a RECORD NOT FOUND error. Since we can't tell the controller how many sectors to read, there has to be some other way to read multiple sectors. This is where the FORCE INTERRUPT command comes in. We don't wait until the FDC finishes the command itself, but instead we determine when we're done. Here is an example of how we might do this: Let's assume the following: The read/write head is positioned over an Atari-formatted track. The sectors of this track are numbered 1-9. We want to read the five sectors from 3-7. The sector number is passed to the sector register, and the read operation is started by passing the command word. If nothing else happens, seven sectors are read (3-9) and the command is terminated after five rotations with an RNF error (because sector 10 could not be found). But we want to read just sectors 3-7 (and we don't want an error message). This is how we do it: Since the data transfer is handled by the DMA controller, it is initialized with a DMA start address before the FDC command is started. This address is continually incremented as the FDC passes the data to the DMA controller. Therefore the current DMA address can be determined by reading the DMA 106 Abacus Atari ST Disk Drives Inside and Out address register. This register is continually read until its contents are incremented beyond $A00 (5*$200), the start address. When this is the case, the READ SECTOR command is terminated with a FORCE INTERRUPT command. WRITE SECTOR Command word: 76543210 lOlmhEPaO Here we’ll just explain the differences from the READ SECTOR command, since much of the operation is the same. At the beginning of the command, the FDC checks to see if the WRITE PROTECT input is low. If this is the case (write-protected diskette), the WPRT status bit is set and the command is terminated. If the disk is not write-protected, the ED field search starts. If the matching ID field is found, a delay corresponding to 23 bytes is made. After this, 12 zero bytes and a data address mark are written (type depending on aO). Following this is the actual sector information, followed by the CRC checksum. Finally, an $FF byte is written. Whether the sector was written correctly or not can only be determined by a READ SECTOR command. WRITE SECTOR (with m-bit set) See the discussion of the READ SECTOR command with m-bit set. 107 Abacus Atari ST Disk Drives Inside and Out Flowcharts of the Type II commands We also have flowcharts for the Type II commands. ( START ) I Set BUSY clear CRC.RNF.DRQ, INTRQ, RECORD TYPE and LOST DATA Flowchart for Type-ll commands Is the MOTOR ON YES Is YES s^option set? NO r | NO Set MOTOR ON Set MOTOR ON -wait for 6 index impulses ^_-_ v~ r 30 ms delay (head Ml resting time) NX clear BUSY set set WPRT INTRQ 108 Abacus Atari ST Disk Drives Inside and Out >9 Abacus Atari ST Disk Drives Inside and Out 110 Abacus Atari ST Disk Drives Inside and Out 111 Abacus Atari ST Disk Drives Inside and Out The Type III commands The WRITE TRACK command is used for formatting a track. The READ TRACK and READ ADDRESS commands are used to analyze track format. READ ADDRESS Command word:7 6543210 llOOhEOO Here we give you a few warnings about the DMA controller. Programming the READ ADDRESS command can easily drive a person crazy! After the READ ADDRESS command is started and executed, you might be surprised to discover that the ID field is not in RAM. A test of the status registers (DMA and FDC) show that no errors occurred. If you subtract the start DMA address from the end DMA address, the difference is zero. As a result, zero bytes were transferred—fewer than we had hoped. What happened to the six-byte ID field? That's easy—it's in the DMA controller! The DMA controller does not transfer bytes individually. It waits until it has received 16. Not until then does it request the bus from the 68000 processor to transfer these 16 bytes into RAM. Now, you might ask, how do we get the ID field out of the DMA controller? There's only one way to do it: read more data. This is done by reading several ID fields in succession. After three READ ADDRESS commands (18 bytes), we get 16 bytes in RAM and two in the DMA controller. To get all of the bytes transferred from the DMA controller to RAM, we have to read a number of byte which is evenly divisible by 16. But there's another problem. This results from clearing the DMA status register, which is done by toggling the read/write line. If we reset this register before each DMA transfer, "just to be safe," we get the second surprise. This erases not only the status register, but all of the bytes still in the controller. In this case we can read ID fields until the disk falls apart, and the DMA controller wouldn't transfer a single byte into memory. The moral of the story is that the DMA status register may be cleared only before the first READ ADDRESS command. 112 Abacus Atari ST Disk Drives Inside and Out Now on to the READ ADDRESS command itself. The READ ADDRESS command causes the next ID field which the read/write head encounters to be read. It can be used in connection with the READ TRACK command to analyze the format of a disk. Moreover, it is also possible to verify a track without leaving it. The READ ADDRESS command reads an ID field without testing to see if a matching data field exists. Six bytes are read. They have the following meanings: Bvte # M ea n in g 1 Track number 2 Side number 3 Sector number 4 Sector size 5 CRC byte 1 6 CRC byte 2 The track number (byte 1) is also written in the sector register. This can be used to do a track verify without using the data read. At first, this may seem unnecessary, because it doesn't matter if (for a track verify) we compare the contents of the sector register, or the first byte to the contents of the track register. In addition, it is simpler to use the transferred byte because we don't have to select the sector register first to use it. As we said before, no information is transferred to RAM until the DMA controller has accrued 16 bytes. But since the FDC also writes the first byte of the ID field in the sector register, it is still possible to execute a track verify with a single READ ADDRESS command. If no ID field is found within six index pulses (which corresponds to at least five rotations), the RNF status bit is set (RECORD NOT FOUND). Once the FDC has finished the command, it sets the INTRQ output and clears the BUSY status bit. READ TRACK Command word: 76543210 lllOhEOO 113 Abacus Atari ST Disk Drives Inside and Out The READ TRACK command is also used only for track diagnosis. It reads a complete track, including all GAP, SYNC, and data bytes. The reading begins on the rising edge of the next index pulse which the controller receives from the drive. Data is read until another index pulse reaches the controller. As usual, the end of the operation is indicated by setting the INTRQ output and clearing the BUSY status bit. A DRQ is created for each byte read. As with all commands, the LOST DATA status bit is set if there is no reaction to the DRQ. Regarding the LOST DATA bit, we found that it would sometimes be set for no apparent reason. The reason is that this bit doesn't really give information about whether data was lost or not after a READ TRACK command. Curiously, these cases never occurred for READ TRACK attempts on an unformatted disk. The "collection" of data bits is synchronized with each received address mark. The address mark detector, which is responsible for this, is not turned off (as it is for a READ SECTOR command, for instance) but remains active during the entire reading process. It is constantly on the lookout for an address mark, and therefore creates read errors. According to manufacturer specifications, all information can be read correctly (with the exception of the GAP bytes). Our attempts had different results. In practice, it seems that only the ID fields can be read properly. But read errors can even occur at the checksum of the ID field. You may wonder where the READ TRACK command can actually be used. The data themselves are of dubious value because of possible read errors; the READ ADDRESS command is much easier to use for reading ID fields, because the ED field search in the track information raises another problem. If we have a byte sequence of FE-01-00-01-02-BC-DB, for instance, we can’t tell whether it is actually an ID field or not. This byte sequence could also occur in a data field. A "real" ID field is simply an ID field that the controller recognizes as an ID field. Used by itself, the READ TRACK command is not very useful. However, when used in conjunction with the READ ADDRESS command, a track may be analyzed fairly precisely. Even if the data themselves don't say much, the number of data present is of great importance. The distances between given points can be measured, which is very important for track analysis. This makes up for a disadvantage of READ ADDRESS, which reads only ID fields and doesn't test whether a corresponding data field exists. 114 Abacus Atari ST Disk Drives Inside and Out As an example, we'll describe how a track analysis would work: 1) All ID fields on the track are read with READ ADDRESS commands. 2) All track information is read with a READ TRACK command. 3) All sectors on the track (track and sector numbers are obtained from the ID fields) are read with READ SECTOR commands. Our analysis is complete if there are no ID fields in the track, because the format is unreadable. We search for the first ID field in the track information. Here we have to orient ourselves by "landmark" points. The first of these points is the byte sequence $A1, $1% (or $C2, $FE), which is made up of the SYNC byte and the ID address mark. An ID field can follow only a byte sequence like this. Once it is found, the second landmark becomes interesting. This is located a maximum of 42 bytes behind the ID field. We must find a SYNC byte followed by a data address mark. If we don't find a SYNC byte, then there is no valid data field for the ID field. Another test checks the plausibility of the ID field. For example, if this indicates a sector size of 512 bytes, and the next ID field follows at a distance of 200 bytes, then something is wrong. We read the sectors for two reasons. First, the sector information cannot be taken from the data obtained from READ TRACK. Second, the only way to check the CRC checksum of a data field is with READ SECTOR. WRITE TRACK (FORMAT TRACK) Command word: 76543210 lOOmhEOO A diskette must be formatted before it can be used for data storage. But what actually happens when we format a disk? The sector is the logical data unit used for data transfer between the drive and controller is. Since a fresh blank diskette contains no information about the start of a sector, it must be assigned starting points before it can be used. 115 Abacus Atari ST Disk Drives Inside and Out Every sector has a field that contains information about the sector. A checksum for the data must be also written to a blank disk. Synchronization bytes are also missing. These are very important for finding the start of a byte which is hidden somewhere in the serial bit stream from a later read operation. The purpose of formatting is to write all of this information or marks on the diskette. This must be done according to certain rules, however, or sector transfer is impossible later. If one stays within these rules, a number of nonstandard but usable formats can be created. The FDC is passed a data byte, a value between $00 and $FF. But to write the marks, which differ from the "normal" data, on the disk, it must be possible to operate the controller so that a mark is written to the disk instead of a data byte. First we'll look at the control bytes, which are represented by values $F5 and $FF. In contrast to the READ SECTOR and WRITE SECTOR commands, which write these values as "normal" data bytes, the controller can be used to write special marks to the disk with a WRITE TRACK command. The common bond for all of these is that they are written without clock pulses and can thereby be distinguished from data bytes which the same values (see address mark detector). The following table shows what effect these control bytes have when they are encountered by later read operations: Byte given to Byte written the FDC from the FDC Meaning $F5 $A1 Sync-byte, CRC-reg. cleared $F6 $C2 Sync-byte $f7 $XX,$XX 2 CRC-bytes $F8 $F8 'cleared' data address mark $F9 $F9 Data mark $FA $FA Data mark $FB $FB 'normal' data address mark $FC $FC Data mark $FD $FD Data mark $FE $FE Index address mark $FF $FF These control bytes can be used to create different formats. We use some examples for creating the ST disk format to help explain how this is done. At the appropriate places we'll point out which elements can be changed. 116 Abacus Atari ST Disk Drives Inside and Out Let's start with a buffer that can hold all of the information which is written to the diskette by the WRITE TRACK command. This buffer must be at least 6250 bytes long. Now we have to fill the buffer so it will correspond to a format that can store nine sectors of 512 bytes each. If we divide our buffer into two components, we get the following setup, which is valid for all formats. Differences in the number of records are possible, of course. GAPl RECORD 1 RECORD 2 RECORD 9 GAP 5 A track starts and ends with a block called a GAP. As we'll see later, these GAPs are also present in the RECORDS. A GAP is a blank space that separates the individual components in the track. It contains no useful information, just fill bytes or, if the GAP comes before an ID field or a data field, SYNC bytes as well. The FDC is given a length of time corresponding to the length of the GAP to prepare itself for the requirements of the next component. Gap length Gap length Identifer Value ATARI format Foreign format GAPl (track leader) $4E 60 bytes min. 32 bytes GAP5 (track end) $4E ca.664 bytes min. 16 bytes The length of GAP5 is irrelevant at the moment. Simply put, GAP5 is just what is left over on the disk. For calculations of the buffer length, we have to leave at least 16 bytes for GAP5. If we subtract the number of bytes we reserve for GAPl from our buffer, we have 6190 bytes available to divided up among the records. But we can't do this yet—we don't know the length of a record. As you probably guessed by now, each record contains one of our nine sectors. So we know one thing for certain: the record length must be larger than the sector length. If we take a closer look at a record, we get the following picture: GAP 2 INDEX-FIELD GAP 3 DATA-FIELD GAP 4 117 Abacus Atari ST Disk Drives Inside and Out First we see the GAPs. In the order they are given above, these GAPs are called pre-record, inter-record, and post-record, gaps. Gap length Gap length Identifcr. Value ATARI format Foreign format GAP2 $00 12 bytes min. 8 bytes SYNC $F5 3 bytes 3 bytes GAP3 $4E 22bytes 22 bytes $00 12 bytes 12 bytes SYNC $F5 3 bytes 3 bytes GAP4 $4E 40 byte min. 24 bytes Total of GAP-bytes per RECORD 92 byte min. 72 bytes The synchronization bytes ($F5) in GAP2 and GAP3 ensure that the reading of the serial data bytes is synchronized with the start of the byte. Also, they alert the FDC to look for a following address mark and initialize the CRC logic. To write a SYNC, the FDC is passed the value $F5, which writes an $A1 byte without clock pulses. The data Held Now that we know about the GAPs found within a RECORD, let's take a closer look at the data field in which our sector is found. DAM Sector CRD $FB 512 data bytes $F7 The data field starts with the data address mark, which designates the start of the sector. The value $FB is interpreted as a "normal" data address mark by a later READ SECTOR command, while the value $F8, which can be used instead of $FB, is viewed as an "erased" DAM. The sector field is filled with "dummy bytes" when formatting. The values can be almost anything, but they should never be larger than $F4. There can be 128, 256, 512, or 1024 bytes. The FDC uses the index field to determine the number of bytes in the sector. Passing the value $F7 to the FDC causes it to write the contents of its 16-bit CRC register, which contains a checksum, to the disk. Although only one byte is passed, the controller still writes two bytes. 118 Abacus Atari ST Disk Drives Inside and Out The total length of the data field for a sector length of 512 bytes, in our example, is 515 bytes. The index field The index field, also called the ID field, contains information about the data field which follows it. ID-AM Track Side Sector Length CRC $FE 00-79 00-01 00-09 00-03 $F7 The index address mark (ID-AM) is the start mark of the ID field. If the controller encounters an ID-AM during a later read operation, it reads the next six bytes with its address mark detector turned off. The ID-AM is written without clock pulses. The three bytes which follow the ID-AM describe the RECORDS. The first specification is the track number on which the ID field is located. In our case this is a value between 0 and 79, depending on which track is being formatted. The side field specifies whether the record is on the front or back of the diskette. The byte is not used by the controller for any operations. The sector field contains the number of the sector (1-9). Since the FDC distinguishes between different sector sizes, it must be told how many data bytes are contained in the following sector. This is done by the length field. Table of sector lengths Length field Bytes per sector 00 128 01 256 02 512 03 1024 For a sector size of 512 bytes, this field contains "02". All that's missing is the checksum. As in the data field, this sum is written by passing the value $F7. Adding the lengths of all these values gives a total length of seven bytes for an ID field. Now that we have looked at all of the components in the track an then- order, we can calculate the length of a record. 119 Abacus Atari ST Disk Drives Inside and Out Data field 515 bytes ED field + 7 bytes GAP2-GAP4 + 92 bytes Record length =614 bytes This is the actual size of the record. In our buffer a record is only 612 bytes long because only one byte is passed to the FDC to write each of the two checksums. If one record requires 614 bytes, then we for a track we need 9 * 614 bytes = 5526 bytes. If we subtract this from the 6190 available bytes, we have 644 bytes left over for the track trailer (GAP5). This is more than enough since only 16 bytes are actually required here. Even a format which uses ten sectors at 512 bytes each would leave 50 bytes for GAP5. Let's look at the data with which we’ll prepare our buffer. The following explanations apply to the table which follows: The data for GAP2 through GAP4 inclusive (one complete record) repeat for each sector. For example, a format with 29 sectors would have the corresponding block repeated 29 times in the buffer. You have to determining the values specified with $XX yourself, although this isn’t very complicated. If you are formatting track 54, for example, the value for the track number would be 54. The value for the side number is generally 0 for the front side and 1 for the back. The sector numbers form a sequence, usually starting with 1. The order is variable and might be 3, 6, 9, 1, 4, 7, 2, 5, 8 for a format with 9 sectors. The only important thing is that the sequence is complete. If the sectors are designated with 1, 2, 3, 5, 6, 7, 8, 9, 10, a later READ SECTOR or WRITE SECTOR command with the m-bit set terminates after the third sector with a RECORD NOT FOUND error because the FDC couldn't find a sector number 4. Atari format is listed in the first column. Our table differs from the one found in A Hitchhiker's Guide to the BIOS. The length of the track trailer (GAP5) is listed there as 1401 bytes instead of 644 bytes. If you add the values given in the Hitchhiker's Guide, it would appear that the track length is about 7000 bytes, which is not the case in reality. A buffer is prepared that is somewhat larger than what the track can actually hold, but no more than about 6250 bytes fits in one track. 120 Abacus Atari ST Disk Drives Inside and Out Number of sectors/sector size 9 / 512 18 / 256 29 / 128 5 / 1024 GAPl 60 •k $4E 42 * $4E 40 k $4e 60 ★ $4E GAP 2 12 k $00 11 ★ $00 10 k $00 40 ★ $00 SYNC 3 k $F5 3 k $F5 3 k $F5 3 k $F5 ID-AM 1 $FE 1 k $FE 1 k $FE 1 k $FE TRACK NUMBER 1 ★ $XX 1 k $XX 1 k $XX 1 k $XX SIDE NUMBER 1 ★ $xx 1 k $xx 1 k $xx 1 k $xx SECTOR NYMBER 1 ★ $xx 1 k $xx 1 k $xx 1 k $xx SECTOR LENGTH 1 ★ $02 1 k $01 1 k $00 1 k $03 ID-CRC 1 k $F7 1 k $F7 1 k $F7 1 k $F7 GAP 3 22 k $4E 22 k $4E 22 k $4E 22 k $4E 12 k $00 12 k $00 12 k $00 12 k $00 SYNC 3 k $F5 3 k $F5 3 k $F5 3 k $F5 DAM 1 k $FB 1 k $FB 1 k $FB 1 k $FB DATA 512 k $E5 512 k $E5 512 k $E5 512 k $E5 GAP 4 40 k $4E 26 k $4E 25 k $4E 40 k $4E GAP 5 664 k $ 4E 34 k $ 4E 33 k $4E 420 k $4E Flowcharts of the Type III commands We have also prepared flowcharts for the Type III commands. These flowcharts are illustrated starting on the next page. 121 Abacus Atari ST Disk Drives Inside and Out 122 Abacus Atari ST Disk Drives Inside and Out 123 Abacus Atari ST Disk Drives Inside and Out 124 Abacus Atari ST Disk Drives Inside and Out / has an \ index impulse \^occurred^/ complete byte In DSR 125 Abacus Atari ST Disk Drives Inside and Out The Type IV command FORCE INTERRUPT Command word: 76543210 1 1 0 1 13 12 II 10 This command is the only one that may be passed to the controller while it is executing another command. It is used to stop the execution of a READ SECTOR command or WRITE SECTOR command with the m-bit set. There are three types of interrupts. These are determined by the status bits (10-13) in the command word. 10 and II have no meaning and should be cleared. Bits 12 and 13 select the type of interrupt as follows: ($D4) 12 =1, interrupt on every index pulse ($D8) 13 = 1, end current command with interrupt ($D0) 12-13 = 0, end current command without interrupt The interrupt on every index pulse ($D4) can be used to determine the speed of the drive. Another use is the synchronization of the READ ADDRESS command with the start of the track. It is not necessary to start with the index pulse for the READ TRACK and WRITE TRACK commands. A command currently being executed can be terminated by the interrupts $D8 and $D0. It should be noted that the INTRQ output is not reset by reading or writing the command register after a $D8 interrupt as it usually is. This line can be reset only by following a $D8 interrupt with $D0 interrupt and then reading the status register. If a FORCE INTERRUPT is sent to the FDC, a delay time of 16|is must be inserted before the next command or the interrupt won't be executed. 4.2.2.4 Status interpretation We said that the programming of the peripheral components responsible for data transfer is handled almost exclusively by the operating system. In most cases, these components offer more features than are necessary for normal system operation. This is the reason that the less-frequently-used capabilities cannot be accessed through the operating system. 126 Abacus Atari ST Disk Drives Inside and Out In certain cases, however, these "sleeping" talents of the peripheral chips can make a programming problem much easier or even solve it. Why don't many programmers have enough confidence to wake these "talents"? Just because there's no operating system call to do it? No, the most common reason is "fear of status." This says that a programmer knows how a chip can accomplish a given task, but he doesn't know how to interpret the status which the chip returns. Often he doesn't know what status is returned after error-free execution because certain bits in the status register are almost always set. An OK status can therefore vary. Not knowing how to interpret the status results can stop a programming project dead in its tracks. It's a waste of time to try out all of the possibilities and determine the status returned in each case experimentally. But this isn't the only way to do it either. We have taken care of this for you with the floppy disk controller. Here again we have a table of the meanings of the individual status bits, which were previously described in section 4.2.2.2: Bit 7 6 5 4 3 2 1 0 FDC status register C omman d Motor On (MO) Write Protect (WPRT) Record Type or Spin Up Record not found CRC-Error Track 0 lost data Index inpulse or Data Request Busy Bit=l means Drive motor Disk write protected DATA MARK cleared Turn number reached Sector not found Checksum error Head at track 0 Data loss Index pulse status Ready to transfer Command active It is of interest that we can determine whether or not the disk in the drive is write-protected, or even whether there is a disk in the drive at all, after the read/write head is positioned. All the commands from Type I to Type III have the following in common: Bit 7 is set (the motor is not turned off immediately) and bit 0 is cleared (the FDC has completed the command) in the status word, which is read immediately after a command. In addition, bit 5 (spin up) is set after a Type I command. 127 Abacus Atari ST Disk Drives Inside and Out The status after a Type I command Let's start with the status after a Type I command. In the command words used, the stepping-rate bits for 3ms track to track time are set (rO=l, rl=0). RESTORE command normal write protected 01 (with MO option, no verify) A6 E4 01 (note (1)) A6 E6 05 (with MO option, with verify) A4 E4 09 (no MO option, no verify) A4 E4 0D (no MO option, with verify) A4 E4 SEEK command normal write protected 11 (with MO option, no verify) A0 E0 11 (note (3)) A2 E2 11 (note (2)) A4 E4 11 (note (1)) A6 E6 15 (with MO option, with verify) A0 E0 15 (note (2)) A4 E4 19 (no MO option, no verify) A0 E0 19 (note (2)) A4 E4 ID (no MO option, with verify) A0 E0 ID (note (2)) A4 E4 STEP, STEP IN, STEP OUT normal write protected xl (with MO option, no verify) A) E0 xl (note (2)) A4 E4 x5 (with MO option, with verify) A0 E0 x5 (note (2)) A4 E4 x9 (no MO option, no verify) A0 E0 x9 (note (2)) A4 E4 xD (no MO option, with verify) A0 E0 xD (note (2)) A4 E4 (1) This value is valid if the read/write head is already over track 0 before a RESTORE or SEEK command to track 0. In addition to the track-0 bit, the IP bit is also set. This is because six index pulses are counted if the motor-on option is enabled. This means that the FDC determines that the desired track is reached during an index impulse and ends the command. 128 Abacus Atari ST Disk Drives Inside and Out (2) This status is encountered after a SEEK, STEP IN, or STEP OUT command when the read/write head is moved over track 0. (3) If the read/write head is already over the desired track (except for track 0) for a SEEK command, the IP bit is set in the status word. The same relationship applies here as in (1). In general, an error status can only be received after a Type I command if the verify bit is set in the command word. In addition: (a) If no ED field was found, the RNF bit is set. and (b) If no correct ID field was found, the RNF and CRC bits is set. The status is $B2 or $BA, or $F2 or $FA for a write-protected disk. If the whole thing happens on track 0, the track 0 is also set, of course. The status word then has the value $B6 or $BE, or $F6 or $FE for a write-protected disk. It occurs that the IP bit is always set in case of error. This has nothing to do with the motor-on option, however, but results because the fruitless search for an ID field is ended on the sixth index pulse. The status after a Type II command The status interpretation is somewhat simpler for Type II commands. After a successful WRITE SECTOR command the status register always contains the value $80. After a READ SECTOR command, the status word can also be $A0 if a sector with an "erased" data mark was read. Otherwise the value will also be $80 here. If the command was not successful, the status after a WRITE SECTOR command is one of the following: (a) $C0 after an attempt to write to a write-protected disk (b) $90 if the ID belonging to the desired sector was not found (c) $88 if the checksum (CRC) of the ID field was not correct (d) $84 if no reaction was made to a DATA REQUEST by the FDC 129 Abacus Atari ST Disk Drives Inside and Out After an error occurred during a READ SECTOR command: (a) $90 if the ID field belonging to the desired sector or the data mark was not found (b) $98 if the checksum (CRC) of the ID field was wrong (c) $88 if the checksum (CRC) in the data field indicated an error (d) $84 if no reaction was made to a DATA REQUEST by the FDC The status after a Type III command The status after a Type IE command is even simpler to evaluate. The value $80 indicates successful execution. In case of an error during a WRITE TRACK command, one of the following holds: (a) $C0 for a write-protected disk (b) $84 if no reaction was made to an FDC DRQ There is no such thing as improper execution of the READ TRACK command. The FDC simply reads the RD input between two index pulses, and it doesn't even matter if there's a disk in the drive or not. The only imaginable error, LOST DATA (status $84), cannot be evaluated because of a software error in a subprogram in the FDC. The LOST DATA is also set depending on the format read in addition to actual loss of data. For the READ ADDRESS command, a status value of $80 also means that no errors occurred. Otherwise, one of the following can result: (a) $90 if no ID field was found (b) $88 if the FDC found a checksum error in the ID field (c) $84 if no reaction was made to a DRQ 130 Abacus Atari ST Disk Drives Inside and Out 4.2.3 The floppy interface The floppy disk connector on the back of the ST is a rather unusual 14-pin socket. Complete control of the drive and the data transfer takes place over these fourteen lines. This control is fairly easy to describe because the drive doesn't possess any intelligence of its own. This has one big advantage. The interface to such drives is standardized and is called the Shugart interface, and is found on many drives. This is the reason that it is so easy to connect foreign drives to the Atari ST. The Shugart interface has a 34-pin connector which is usually equipped with a ribbon connector. Half of these 34 pins are tied together and grounded. On a ribbon cable every other wire is a ground, so all of the odd pins are grounded. This results in some shielding between the signal lines, which is important given the high clock rates of the signals. Fourteen of the remaining eighteen lines are connected to the Atari. Let's look at these signals on the Shugart connector: Pin 2: Head Load A low signal on this line sets the read/write head on the diskette. This feature is designed to protect the disk because the head rubs on the disk only when it is actually going to access it. Unfortunately, this signal is not available on the ST because the WD1772 controller does not have this connection available. This line is often connected to "Motor on" however. Pin 3: Ground All odd-numbered lines through 33 are tied to ground. This ground connection is used for operation as well as shielding. Pin 4: In Use This signal tells the drive that it is connected and is used. It is not available on the Atari. Pin 6: Drive Select 3 A low level on this line means that drive three is being selected. Only the drive which is assigned as drive three by a wire jumper in the drive reacts to commands, and all others remain neutral. This signal is not used on the Atari because a maximum of two floppies can be connected (0 and 1, or A and B). 131 Abacus Atari ST Disk Drives Inside and Out Pin 8: Index The drive uses this line to send a low pulse upon each rotation of the disk. This signal tells the controller that the data which follow are at the very start of the current track. This can be used to synchronize the controller. Pin 10: DriveSelect 0 This signal corresponds to the one on Pin 6, except that it concerns drive 0 (Drive A). Pin 12: Drive Select 1 As above, except for drive 2 (Drive B). Pin 14: Drive Select 2 As above, except for drive 2. Not connected on the ST because only two drives are possible. Pin 16: Motor on A high level on this connection starts the motors of all drives and a low stops them. Pin 18: Direction This signal indicates the direction of the next step of the read/write head. If this pin is zero, the direction is in, toward track 79, while a 1 means out, toward track 0. Pin 20: Step A low pulse causes the step motor in the drive to move the read/write head one step in the direction specified. Pin 22: Write Data This line carries the serial data which are to be written to the disk. Pin 24: Write Gate This signal selects the data direction. If it is low, the disk is written, while a high signal indicates read. If the disk is write-protected, no write access is allowed by the drive. Pin 26: Track 0 If the read/write head is over track 0, this pin is low. Pin 28: Write Protect A low on this line means that the diskette is write-protected. 132 Abacus Atari ST Disk Drives Inside and Out Pin 30: Read Data The data read from the disk are sent to the computer via this line. Pin 32: Side Select This line selects the desired side of the disk. A low level selects side 1 and a high selects side 0. This line is unused for single-sided drives. Pin 34: Ready A low level on this line indicates that a disk is inserted in the drive and that it is rotating normally. The computer can use this line to determine if the disk has been changed. This line is not connected to the Atari ST. All of these signals are TTL-compatible, meaning that 0-0.4 volts indicates a LO (zero) and 2.5-5.25 volts means HI (one). To ensure these signals, most drives have a set of pull-up resistors built into them. If several drives are connected in parallel, it is advisable to remove the resistors from all drives except the last one or the outputs on the Atari may be overloaded. On some drives (such as the Epson) the resistors are in a single package so that they can be removed easily. For the original Atari drives, which are Epson drives, by the way, this is not necessary. 4.3 Connecting the disk drives The floppy disk drives which Atari sells for the ST are very easy to connect: Plug the cable into the computer and drive and you're done. It becomes more complicated if you want to connect a different drive. The first problem which we encounter is the connector, which at the time of this writing (5/86) is difficult or impossible to find. Solder pins soldered to a suitable circuit board or held in position by some other means can be used as a substitute. Once you have devised a suitable connector, you have to wire it. A shielded cable is highly recommended if the length exceeds about one meter. The high transfer rates lead to electrical effects like inductance and capacitance, which can have unfavorable effects on data transfer. The best way to avoid this is to use a cable in which the lines are individually shielded. 133 Abacus Atari ST Disk Drives Inside and Out After the connection between the Atari and the drive has been made, the cable must be connected. Here is the wiring table: ari ST Line Shugart connector 1 Read Data 30 2 Side 0 select 32 3 Ground all odd lines 4 Index Pulse 8 5 Drive 0 select 10 6 Drive 1 select 12 7 Ground see above 8 Motor on 16 9 Direction in 18 10 Step 20 11 Write Data 22 12 Write Gate 24 13 Track 00 26 14 Write Protect 28 When these lines are connected to the two drives, all connections are in parallel. The selection between drives A and B is made directly in the drive. The jumpers in the drives must be set properly for this selection. Information on how to set the jumpers is found in the documentation for a given drive. 134 Chapter Five Abacus Atari ST Disk Drives Inside and Out The SH204 hard disk A hard disk is a significantly faster—and significantly more expensive— method of data storage. But the SH204 hard disk that Atari sells for use with the ST is available for a very reasonable price. What are the advantages and disadvantages of a hard disk? The first disadvantage is obviously the hard disk’s high cost. In addition, you can't exchange media for copying files and programs, or for backups and archival storage—all of which are possible with floppy diskettes. But if we look at the advantages which a hard disk has to offer, the investment becomes more appealing. A hard disk's first advantage is the speed at which it transfers data between the ST and the hard disk itself, approximately 10 times faster than floppy diskette operations. Another advantage is the capacity of a hard disk. The currently available device has a capacity of twenty megabytes. This can hold, for example, all of the programs and files for an extensive compiler together with the source files for your C or Pascal programs. Since these compilers are usually disk-oriented (i.e., they constantly access information on the disk), both the speed and large capacity of the hard disk represent significant advantages for working with compilers. A popular use of hard disks is in electronic data processing, where large quantities of data must be managed. Having to change disks all of the time in such applications simply wouldn't work. Imagine a bank teller having to insert the correct disk into the bank computer for every withdrawal! An Atari ST with a twenty megabyte hard disk probably isn't sufficient for the data processing needs of a bank. But it would work for managing a small business in which inventory, accounting, and payroll were done on a computer. This is the principle application of a hard disk. We will now look at how such a data set is managed by the hard disk and also by the computer connected to it. 137 Abacus Atari ST Disk Drives Inside and Out 5.1 Function and design The function of a hard disk is very similar to that of a floppy disk drive. In both cases, one or more disks (just one in the Atari hard disk) rotate at a constant speed and a read/write head moves over it. There are some important differences from a floppy drive, however. The rotation speed of the hard disk is significantly higher than that of a floppy diskette, which makes possible the high access and transfer speeds. In order to protect the read/write head, which flies over a disk that rotates about ten times faster than a floppy diskette, it does not contact the disk at all. The head floats oyer the disk on a cushion of air that is so thin that the head would collide with a dust particle on the disk surface. Human Hair Speck of dust Finger print Read/write head With the high tangential speed of the disk, if the head contacts such a dust particle, it can lead to severe damage to the disk and/or the read/write head. This is called a "head crash," and it can have very expensive consequences in terms of lost data and actual physical damage to the drive. To minimize the chances of a head crash, the disk and the head are sealed in an airtight enclosure. This is why the media in hard disks are usually not exchangeable like diskettes. There are exchangeable-media hard drives available on the market, but they are quite expensive. Also, no such drive is currently available for the ST, so we will not explore this any further. Another difference between a diskette drive and the hard disk on the Atari ST shows up in the controller. The floppy disk controller built into the Atari ST is, as the name indicates, only responsible for the floppy drives. The hard disk has its own controller, which is built into the drive housing. This makes it considerably harder to connect a foreign hard drive to the Atari. Let's take a closer look at this controller. 138 Abacus Atari ST Disk Drives Inside and Out 5.1.1 The hard disk controller The controller used in the Atari ST hard disk is a very powerful device. This controller can achieve data transfer rates up to eight megabits per second, which is about one megabyte/second. This would fill the memory of a 1040ST in one second! Unfortunately, this number does not apply to actual data transfer. One factor that dramatically slows the data transfer is the mechanism in the hard disk. This refers to the rotation speed of the disk and the step motor which moves the read/write head to the proper track on the disk. All of these points reduce the actual maximum speed of the data exchange, although it is still very high. The controller has a very simple internal structure. Its command set is so versatile that it even supports error correction. The hardware of the controller consists mainly of a disk controller, an encoder/decoder, and a microcontroller. These components have the following tasks or functions: The disk controller converts the data from serial into parallel and back again. In addition, it converts the data itself into a different bit pattern, which is then actually written to the disk. This different format allows simple read errors to be recognized. The encoder/decoder converts the data sent by the disk controller into electrical signals which control the write head. It also converts the signals which arrive from the head when reading into bits, whereby it also serves as a data separator (cf. floppy disk controller). The microcontroller works like an actual disk controller. Its tasks are: • interpretation of the commands from the computer • selection of the drive being accessed (usually there is only one) • selection of the head in the drive (top or underside of the disk) • control of the stepper motor which moves the read/write head • status reporting Here is a simple block diagram of the Atari hard disk controller: 139 Abacus Atari ST Disk Drives Inside and Out Atari Hard Disk Controller Block Diagram The operations which can be performed with the hard disk over the DMA bus are divided into five different phases, which are defined as follows: Reset phase Occurs when either the RESET button on the ST is pressed, the computer is turned on, or the RESET command is executed by the 68000 processor. The bus and the HDC are reset to their initial states. Bus-free phase Occurs when no device is accessing the bus. Destination-selection phase Starts with calling a device by reseting the SEL line. The desired device is addressed through a set data bit on the 8-bit parallel bus. The addressed device (here: HDC) answers with a BUSY signal, upon which the SEL line is set again. Then starts the Information-transfer phase During this phase the following data are transferred: • the command block, 6 bytes from the ST to the HDC • the data block(s), if the command requires it • the status byte from the HDC to the ST, which indicates whether the operation was successful or whether an error occurred. This byte is always zero, however, so status transmission is possible through the recognition of a timeout • the completion byte from the HDC to the ST, a zero byte which signals the end of the entire operation 140 Abacus Atari ST Disk Drives Inside and Out Bus-release phase This phase is initiated by setting the BUSY line and means that the bus is free for the next operation. The bus is then back in the bus-free phase. 5.1.1.1 Command structure The transfer of commands to the hard disk controller is precisely defined. Each command is sent a 6-byte block, called the command descriptor block. When the controller receives such a command, it acknowledges receipt to the initiator, the ST, through an interrupt. If the block contains a command to find a specific track (verify, format track, read, write), this will be performed automatically. The specified logical data block is converted by the controller to physical quantities like disk side and track number. The following diagram shows the structure of a command block: Byte 0: |xxxxxxxx| Byte 1 : xxxxxxxx Byte 2: xxxxxxxx Operation code .Controller Number Block address HI _Drive number Block address MID Byte 3 : Byte 4 : Byte 5: xxxxxxxx Block address LO xxxxxxxx xxxxxxxx Block number Controller byte 141 Abacus Atari ST Disk Drives Inside and Out Controller number This is a 3-bit value (0-7) which represents the number of the selected controller. This allows up to eight different controllers to be connected and served. The number which addresses each individual controller is set with the help of three switches on the controller board. When a command arrives at the controllers over the common bus line, each one tests to see if it is the one being accessed. If not, it behaves as if it didn't exist. If so, communication between the computer (initiator) and the addressed controller (target) begins. If no controller answers the command within four seconds, the computer gives up: time out. Operation code: This code, also called the opcode, contains the command to be executed in five bits. This means that only commands from 0 to 31 are possible. Drive number: Similar to the controller number, this is a 3-bit number which designates the selected drive. Each of the eight controllers can control up to eight drives, which means that 64 drives can be theoretically connected to the Atari. Block address: This 21-bit number designates the selected logical sector. The conversion of this number (up to 2097151) into the physical values is accomplished by the controller. The Atari hard disk contains 41616 sectors, so the block addresses may not exceed this value and, since the block numbers start at zero, may not reach it either. Block counter: This counter determines the number of sectors to read or write. The counter must have a non-zero value (1-255). Control byte: This byte contains various specifications, depending on the given command. 142 Abacus Atari ST Disk Drives Inside and Out The following procedure must be executed in order to send such a command to the HDC: First, the processor is placed in the supervisor mode through the SUPER function ($20) of GEMDOS (TRAP #1), because some privileged accesses must be made to hardware registers. After this, the floppy-processing routines are disabled by setting the system variable FLOCK ($43E). This is necessary because both the FDC and the HDC are controlled via the same hardware registers. To make sure that there are no crossed signals, such as an OK from one controller when we are waiting for the OK from the other one, the FDC is essentially removed from the system so that it doesn't interfere. In the hardware register $FF8606, called WDL, bits 7 and 3 are set and all others cleared by writing the value $88. This selects the HDC and places the line Al, which is addressed through bit 1, to zero. Line Al is used to signal the HDC that a command byte (the first byte of a command block) will be transferred. Following this, the command byte is placed in register $FF8604, called WDC. The HDC accepts this byte and acknowledges reception with a 0-signal on the HDC interrupt line. This line lies on bit 5 of the I/O port on the Multi-Function Peripheral chip (MFP), and can be found at address $FFFA01. This interrupt also occurs after each additional byte is transmitted. If it does not occur, the byte was either not recognized or the HDC is not ready to receive data. During the transmission of the command bytes, there is a maximum wait of 100 milliseconds for the interrupt, and up to three seconds after the complete transmission of the command block, because the command must be completely executed before the HDC can respond OK. If no interrupt occurs within this time, the transmission of the command bytes is terminated and a timeout is indicated. If the command byte is transferred and acknowledged on time through the interrupt, an $8A is written in the WDL, which sets the Al line to 1 again. The remaining five bytes of the command block are now transferred according to the same scheme, except with bit 1 (Al) set. The byte is 143 Abacus Atari ST Disk Drives Inside and Out written to WDC together with an $8A in WDL, the computer waits 100 ms for an interrupt (else timeout) and the next byte is transferred. After transmission of the last byte (byte 5) of the command block, the computer waits a maximum of three seconds for the interrupt, so that the HDC has enough time to execute the command. Finally, the system variable FLOCK ($43E) is again set to 0 in order to permit floppy operations again, and last of all, the computer can be switched back into the user mode. Here is a small program which performs the steps listed above and sends a command block to the HDC. Please note that this works completely only if the command does not cause data transfer via DMA (such as READ and WRITE), because the DMA controller must then be programmed. We will come to this later. ; ** Hard disk access S.D. ** ; ** Send command-bytes from COM-field to HDC ** wdc = $ff8604 wdl = $ff8606 wdcwdl = wdc port = $fffaOl flock = $43e run: move.b #'0',num clr.l -(sp) move #$20,-(sp) trap #1 addq. 1 #6, sp move.1 dO,spsave lea com,aO bsr send bra exit send: St flock move #$88,wdl clr.l dO moveq #5,d2 /Timeout-report preparation /Switch to Supervisor-Mode /Alter stack pointer—save /pointer to command-block /Send command-block on HDC /Ready /* Send command-Block on HDC * /Floppy block /Select HDC, A1=0 /Number: 6 Bytes 144 Abacus Atari ST Disk Drives Inside and Out loop: clr.l dO move.b (aO) +, dO /Greatest byte bsr send byte /Send byte to HDC bmi error /Timeout ! dbra d2,loop / Loop cont: move #$8a,wdl bsr waitl /Wait a max. of 3 seconds /interrupt bmi error /Timeout ! move #$8a,wdl move wdc,dO /Get status-byte move #$80,wdl /Deselect HDC move wdc,dl /Get completion-byte clr flock /Stop floppy disk access rts /Ready exit: move.1 spsave,-(sp) move #$20,-(sp) trap #1 /Switch to User-Mode addq.1 #6, sp t rts /End as subroutine clr. w - (sp) trap #1 /Return to desktop error: clr flock /Show error /Stop floppy disk access move. 1 #senderr,dO bsr pline /Display error message bra exit /and end send_byte : /* Semd a byte to the HDC swap dO /Byte in HIGH-value move #$8a,dO /$8A in LOW-value move. 1 d0,wdcwdl /Set WDC and WDL bra wait /Wait for OK (Interrupt) waitl: add.b #1,num /Running number+1 move. 1 #450000,d3 /Timeout for 3 seconds bra waitl /Wait... wait: add.b #1,num /Running number+1 145 Abacus Atari ST Disk Drives Inside and Out move.1 #15000,d3 /Timeout for 100 ms waitl: subq.l #1, d3 /Timeout counter-1 bmi timeout /Timeout ! move.b port,dO /Load I/O-Port and. b #$20,dO /Combine bit 5 bne waitl /If it's still set. /continue waiting moveq #0, d3 /Display OK rts /Ready timeout: moveq #-l,d3 /Don't display OK rts pline: /* Print a line on the move.1 dO, - (sp) move #9,-(sp) trap #1 addq.1 #6, sp rts spsave: dc.l 1 senderr: dc.b "ERROR in i send byte " num: dc.b "1. time! ", 10,13,0 com: dc.b $b,$0,$0, 0,0,$0 align end The command block bytes which this program transmits cause the read/write head of the hard disk to move to track 0 ($B=Seek). The program contains an error output for testing purposes which displays a timeout on the screen. This section can be omitted, of course, since it only checks to see that the command block was properly transmitted. The transmission of a read or write command to the HDC is somewhat more complicated. In addition to transferring the command block, the DMA (Direct Memory Access) controller, which is responsible for the transfer of data between the hard disk and computer memory, must be programmed. The DMA controller requires the following information: • the memory address from which the data will be read or to which they will be written. This address is written in the hardware registers $FF8609, $FF860B, and $FF860D—first the low byte, then the middle byte, and last the high byte of the address. Since 146 Abacus Atari ST Disk Drives Inside and Out this represents a 24-bit address instead of a full 32 bits, the address can "only" be between 0 and $FFFFFF (also used for FDC programming). • the direction in which the data are to be transferred (read or written). The DMA controller gets this information from bit eight of the WDL word, whereby 0 means read and 1 means write outside of memory. • the status of the DMA controller, on or off. The DMAC gets this information from bit six of the WDL register $FF8606. Normally the DMA controller is always on, meaning bit 6 = 0. The exact moment at which the DMAC is given this information is also important so that any operations in progress are not disturbed. If we want to read from the hard disk, the command byte is first sent to the HDC and then the DMA address is set. This prevents the DMAC from loading undesirable information into memory because the HDC first waits for the additional bytes of the command block after receiving the command block. To write to the hard disk, the DMA address is set first, and then the command byte is transferred. You can see how this is done in the program HDC tools in section 5.1.1.3. First, however, we stick to theory and take a look at the HDC commands. 5.1.1.2 List of commands The command set used for the ST hard disk contains only nine different commands. Other commands are listed in the various manuals for the hard disk, but either they don't work as indicated, or they don't work at all. Here is an overview of the working commands and their hexadecimal opcodes: Opcode Command 00 Test Unit Ready 01 Restore 03 Request Sense 04 Format Drive 08 Read 0A Write 0B Seek 15 Mode Select IB Seek to shipping position 147 Abacus Atari ST Disk Drives Inside and Out The following is an explanation of the individual commands, together with their parameter bytes. The character indicates that a byte has no meaning, and should be cleared. Test Unit Ready (00) With this command the computer can address the bus and determine which devices are connected. Byte 0 : Byte 1 : Byte 2 to 5: | xxxOOOOOI | XXX Command 00, Test Unit Ready Controller Number Drive number If the specified drive is turned on and ready, a zero will be returned in the status byte, else the Check Condition bit will be set Restore (01) This command resets the HDC to its initial state and causes the drive read/write head to move to track 0. Byte 0 : Byte 1 : Byte 2 to 5: | xxxOOOOl| | XXX-I Command 01, Restore Controller Number Drive number Request Sense (03) This command returns four bytes (read the WDC four times), of which only the first byte has any meaning. It contains the error code of the last command to be executed. If no error occurred, this will contain a zero. 148 Abacus Atari ST Disk Drives Inside and Out Byte 0 : Byte 1 : Byte 2 : Byte 3: Byte 4: Byte 5:: I xxxO 0 011| I xxx Command 03, Request Sense Controller Number Drive number | 00000100 | $04 bytes returned Format Drive (04) This command instructs the HDC to format the entire disk. It should go without saying that you shouldn't experiment with this command! Some parameters are included with the command: • the Data Pattern flag, which consists of two bits and determines what data will be written to the empty sectors. If the bits are not set (0), all sectors will be written with $6C. If the bits are set, the byte passed in command byte 2 will be written. • Data Pattern. Here is the byte with which the formatted sectors will be filled if the Data Pattern flag is set. If the flag is not set, this byte has no meaning. • Interleave factor. This value specifies the distance between two consecutively numbered sectors on the disk. If the factor is 1, the sectors will be written on the track in order. If, for example, it is 2, another sectors is placed between sectors 1 and 2. The order of the seventeen sectors on the track would be as follows: Floating number 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 — 1 - 1 1 - 1 - 1 - 1 - 1 - 1 - 1 1 1 1 - 1 — I - 1 - 1 110 2 11 3 12 4 13 5 14 6 15 7 16 8 17 9 Sector number 149 Abacus Atari ST Disk Drives Inside and Out This means that two revolutions of the disk are necessary in order to read all of the sectors in a track. This makes reading a track longer, but also more reliable because there is a small pause after reading each sector. Normally this factor is set to 1. Byte 0 : Byte 1: Byte 2 : | xxxO010 0| Command 04, Controller Format Number Drive | xxx-xx-| | xxxxxxxx| Data pattern flag Drive number Data pattern Byte 3: Byte 4: Byte 5:: | xxxxxxxx | Interleave factor HI (should be 0) | xxxxxxxx | Interleave factor LO (normally 1) Read sector (08) This command instructs the controller to move the read/write head to the track which contains the desired start sector, to read the specified number of sectors, and to transmit them to the computer. In addition to the transfer of the command block to the HDC, the DMAC must be programmed so that the arriving data are written into the appropriate area of memory. Byte 0 : Byte 1 : Byte 2 : Byte 3 : Byte 4 : Byte 5:: | xxxOl000 | Command 08, Read Sectors Controller Number Sector number HI Drive number | xxxxxxxx | I xxxxxxxx | I xxxxxxxx I I-, Sector number MID Sector number LO Number of sectors to be read 150 Abacus Atari ST Disk Drives Inside and Out Write Sectors (OA) This command writes sectors on the disk. The head is moved to the corresponding track and the data sent via the DMA channel are received and written to the sectors. The DMAC must again be programmed in addition to sending the command block. Byte 0 : Byte 1: Byte 2: Byte 3: Byte 4: Byte 5: : I xxxOOlOl| Command OA, ■Controller Write Number Sectors | xxxxxxxx| Sector number HI Drive number | xxxxxxxx| Sector number MID I xxxxxxxx| Sector number LO I xxxxxxxx | Number of sectors to be written Seek (OB) The read/write head in the drive is moved by this command. The controller calculates the corresponding track from the sector number passed with the command and moves the head there. 151 Abacus Atari ST Disk Drives Inside and Out Byte 0 : Byte 1: | xxxOlOll| Command OB, Seek Controller Number | xxxxxxxx| Sector Drive number number HI Byte 2: |xxxxxxxx | Sector number MID Byte 3: Ixxxxxxxx | Sector number LO Byte 4 : I-I Byte 5:: I-I Mode Select (15) This command is used for setting the parameters for formatting the hard disk. A 16-byte clock is sent to the HDC after passing the command (and programming the DMAC). Byte 0 : Byte 1: Byte 2 : Byte 3: Byte 4 : Byte 5:: | xxxlOlOl| Command $15, Mode Controller Number Select | xxx-I -*-*-Drive number | 00010110 | = 16 bytes will be given 152 Abacus Atari ST Disk Drives Inside and Out Seek to shipping position (IB) This command causes the read/write head to move to a position where it is safe against movements of the drive. This position is called the shipping position because it is provided for transporting the drive. The program SHIP . PRG moves the heads in all connected hard drives to this position. It should always be run before moving a hard disk. It should be noted that the screen should be free of all hard disk directory windows when calling this program, otherwise the hard disk directory will be read when the program is done, moving the head out of its safe position. Byte 0 : Byte 1: | xxxllOll| Command IB Seek to Shipping Position Controller Number | xxx .Drive number Byte 2 to 5 : | 5.1.1.3 HDC tools To demonstrate read and write accesses to the hard disk, here is a program which reads one or more sectors and transfers them to memory, or writes data to sectors from memory. In the example, eight sectors from sector 132 on are loaded into memory. This is where the directory of the first hard disk partition is stored. This program also contains a simple method of transmitting the command block, similar to the example in section 5.1.1.1. ;** Read/write hard disk sector, send command ** wdc = $ff8604 ;FDC/HDC access wdl = wdc+2 ; DMA-Mode/Status dm a = $ff8609 ;DMA-address HI flock = $43e ;Floppy-VBL-Flag port = $fffaOl /Parallel port. DMA-sector count bit 5=HDC-IRQ 153 Abacus Atari ST Disk Drives Inside and Out run: clr.l - (sp) move #$20,-(sp) trap #1 /Switch to Supervisor-Mode addq.1 #6, sp move.1 dO,spsave /Store user stack pointer bpl: bra put /for transfer only pea BUFFER /Buffer address move #8, -(sp) /8 sectors move.1 #132,-(sp) /Up to sector 132 bsr read /Read sector(s) in buffer bra bp2 put: bsr send /Transfer command block bp 2: move.1 spsave,-(sp) move #$20,-(sp) trap #1 /Switch to User-Mode addq.1 #6, sp / rts /Return for subroutine /or clr - (sp) trap #1 /Return to Desktop send: ;* Transfer command block * lea wdc,aO lea com,al /Pointer to command block st flock /Save floppy move #$88,wdl /Select HDC, A1=0 clr.l dO moveq #5, dl loop: clr. 1 dO move .b (al)+,dO bsr send byte /Send byte on HDC bmi tout /Timeout! dbra dl,loop /Otherwise keep looping bsr waitl /Wait a max. of 3 seconds bmi tout /Timeout! move wdc,d6 move #$80,wdl /Else clr flock /stop disk access rts /Ready 154 Abacus Atari ST Disk Drives Inside and Out read: write: * Sector (s) read * lea wdc,aO St flock move #$88,2(aO) nop move.1 #$08008a, (aO) move.1 10(sp),-(sp) bsr setdma addq.1 #4, sp bsr set parameters bmi tout move #$190,2(aO) nop move #$90,2(aO) nop move 8(sp),(aO) nop move #$8a,2(aO) nop move.1 #0,(aO) bsr waitl bmi tout move #$8a,2(aO) bra exec * Sector(s) write * lea wdc,aO st flock move.1 10(sp),-(sp) bsr setdma addq.1 #4, sp move #$88,2(aO) nop move.1 #$0a008a,(aO) bsr set_parameters bmi tout move #$90,2(aO) nop move #$190,2(aO) nop move 8(sp),(aO) /Save Floppy-VBL routine /HDC-access, A1=0 ;READ command /Buffer address /Set DMA /Amount and number of sectors /Timeout ! /Switch to READ /Send sector count to DMA /Start transfer /Wait a max. of 3 seconds /Timeout ! /Save Floppy-VBL /Set DMA address /HDC access, A1=0 /WRITE command /Amount and number of sectors /Timeout ! /Switch to WRITE /Send sector count to DMA 155 Abacus Atari ST Disk Drives Inside and Out nop move #$18a,2(aO) nop move.1 #$100,(aO) ;Begin transfer bsr waitl ;Wait a max. of 3 seconds bmi tout /Timeout ! move #$18a,2(aO) exec: nop move. 1 (aO),d6 ;Get HDC/DMA status in D6 and. 1 #$ffOOff,d6 ;HI=HDC, LO=DMA tout: move #$80,2(aO) /Switch to FDC nop move. 1 (aO),d7 /Get completion byte and. 1 #$ffOOff,d7 /HI=HDC (0), LO=DMA clr flock /Stop Floppy-VBL routine rts /Ready set parameters: ;Set sector number and sector count move #$8a,2(aO) bsr wait /Wait for HDC-OK bmi setpx /Timeout ! clr dO move.b 4 +5(sp),dO /Sector no. HI bsr send_byte bmi setpx move.b 4+6(sp),dO /Sector no. MID bsr send_byte bmi setpx move.b 4+7(sp),dO /Sector no. LO bsr send_byte bmi setpx move 4+8(sp),dO /Number of sectors bsr send_byte setpx: rts /Ready send byte: ; * Send 1 byte to HDC * swap dO move #$8a,dO move. 1 dO,(aO) 156 Abacus Atari ST Disk Drives Inside and Out bra wait waitl: ; Wait a max. of 3 seconds for OK move.1 #450000,count bra waitl wait: ; Wait a max. of 100 ms for OK move.1 #15000,count waitl: subq.1 #1,count bmi timeout move.b port,dO and.b #$20,dO ;HDC-Interrupt ? bne waitl ; no moveq rts #0, dO ;Yes => OK timeout: move.1 terrline,dO bsr pline /Send 'Timeout' moveq rts #-l,dO /Display Timeout setdma: ; * Set DMA-address * move.b 7(sp),dma+4 /LO move.b 6(sp),dma+2 /MID move.b 5(sp),dma /HI rts pline: . * p r int line on screen * move. 1 dO,-(sp) move #9, -(sp) trap #1 addq.1 #6, sp rts errline: dc.b "Timeout encountered!". 10,13,0 com: dc.b $b, 0, 0,132,0,0 ALIGN.L count: dc.l 1 /Timeout counter spsave: dc.l 0 /User stack point BUFFER: ds.B 512*8,$FF ;BUFFER FOR 8 SECTORS end This program gives you the ability to load sectors directly from the hard disk or write information to them. The status information is written in register D6, which can be displayed using a machine-language monitor or debugger such as SID or the AssemPro debugger. 157 Abacus Atari ST Disk Drives Inside and Out The difference between this program and the functions available in the operating system for reading and writing sectors is that the operating system functions can access only the selected partition on the disk. If you want to read sector 0 from the hard disk, for example, you must do it with the program above. 5.1.1.4 Partition analyzer Sector zero of the hard disk is very interesting because it contains information about the hard disk and its partitions. The following program can be used to read and evaluate this information. Among other things, it contains parts of the previous program (read), which can be taken directly from the previous source code. The program reads sector 0 of the hard disk and interprets the data it contains. These are then displayed on the screen in hexadecimal. ;** Partition Analyzer S.D. HD 3** wdc = $ff8604 /FDC/HDC-Access, DMA sector count wdl = wdc+2 ;DMA-Mode/Status dma = $ff8609 ;DMA-address HI flock = $43e ;Floppy-VBL flag port = $fffaOl /Parallel port, bit 5=HDC-IRQ run: lea stp,sp clr.l - (sp) move #$20,-(sp) trap #1 /Switch to Supervisor-Mode addq.1 #6, sp move.1 dO,spsave /Store user stack pointer pea buf /Buffer address move #1, -(sp) /1st sector move.1 #0,-(sp) /from sector 0 bsr read /Read sector(s) in buffer move.1 spsave,-(sp) move #$20,-(sp) trap #1 /Switch to Oser-Mode addq.1 #6, sp 158 Abacus Atari ST Disk Drives Inside and Out move.1 #head,dO bsr pline /Print amount move.1 #hi cc,dO bsr pmsg move buf+$lb6,dO bsr pword /Print cylinder number bsr pcrlf move.1 #hi_dhc,dO bsr pmsg move.b buf+$lb8,dO bsr pbyt /Display number of heads bsr pcrlf move.1 #hi_lz,dO bsr pmsg move.b buf+$lbe,dO bsr pbyt /Display park position bsr pcrlf move.1 #hi rt,dO bsr pmsg move.b buf+$lbf,dO bsr pbyt /Output seek rate bsr pcrlf move.X #hi_in,dO bsr pmsg move.b buf+$lcO,dO bsr pbyt /Output interleave factor bsr pcrlf move.1 #hi_spt,dO bsr pmsg move.b buf+$lcl,dO bsr pbyt /Display sectors/track bsr pcrlf move.1 #hd size,dO bsr pmsg move.1 buf+$lc2,dO bsr plong /Display sectors /of hard disk bsr pcrlf move.1 #bsl_count,dO 159 Abacus Atari ST Disk Drives Inside and Out loop: pon: noboot: bsr pmsg move. 1 buf+$1fa,dO bsr plong ;Display number of dead ; sectors bsr pcrlf clr d5 clr.l d6 lea buf+$lc6,a6 /Partition field 0 bsr key bsr pcrlf move.b d5, px_on add.b #'0',px_on cmp.b #0,0 (a6,d6) /Partition active? bne pon ;Yes move.1 #' out',px_on+14 ;Else display 'Out' move.1 #px_on,dO bsr pline bra nextp move.1 #' on ',px_on+14 move.1 #px on,d0 bsr pline /Display 'Partition on' and.b #$80,0(a6,d6) ;Bootable? beq noboot ;No move. 1 #boot,dO bsr pline /Else display 'Bootable move. b 1(a6,d6),px_id+18 move . w 2 (a6,d6),px_id+19 move. 1 #px_id,dO bsr pline move.1 #px start,dO bsr pmsg move .1 4 (a6,d6),dO bsr plong /Display start sector bsr pcrlf move. 1 #px_size,dO bsr pmsg move.1 8(a6,d6),dO bsr plong (•Display sectors/track bsr pcrlf 160 Abacus Atari ST Disk Drives Inside and Out nextp: read: tout: addq #1, d5 add #12,d6 cmp #4*12,d6 bit loop bsr key ;wait for keypress clr - (sp) trap #1 /Return to Desktop l sector(s) (as above) lea wdc,aO St flock /Save floppy-VBL routine move #$88,2(aO) /HDC access, A1=0 nop move.1 #$8008a, (aO) /READ command move. 1 10(sp),-(sp) /Buffer address bsr setdma /Set DMA addq. 1 #4, sp bsr set parameters /Set amount and bmi tout /number of sectors /Timeout encountered! move #$190,2(aO) nop move #$90,2(aO) /Switch to READ nop move 8(sp),(aO) /Send sector count to DMA nop move #$8a,2(aO) nop move.1 #0,(aO) /Start transfer bsr waitl bmi tout move #$8a,2(aO) move.1 (aO),d6 /Get HDC/DMA status and. 1 #$ffOOff,d6 /HI=HDC, LO=DMA move #$80,2(aO) /Switch to FDC nop move.1 (aO),d7 /Get completion byte and. 1 #$ff00ff,d7 /HI=FDC, LO=DMA clr flock /Release floppy-VBL-routine rts /Ready 161 Abacus Atari ST Disk Drives Inside and Out set parameters: ;Set sector numbers and sector count move #$8a,2(aO) bsr wait /Wait for HDC-OK bmi setpx /Timeout ! clr dO move.b 4+5(sp),d0 /Sector no. HI bsr send_byte bmi setpx move.b 4+6(sp),d0 /Sector no. MID bsr send_byte bmi setpx move.b 4+7(sp),d0 /Sector no. LO bsr send_byte bmi setpx move 4+8(sp),d0 /Number of sectors bsr send_byte setpx: rts send byte : /Send 1 byte to HDC swap dO move #$8a,d0 move.1 dO,(aO) bra wait waitl: /Wait a max. of 3 seconds for OK move. 1 #450000,count bra waitl wait: /Wait a max. of 100 ms for OK move. 1 #15000,count waitl: subq.l #1,count bmi timeout move.b port,dO and .b #$20,dO /HDC-Interrupt ? bne waitl /No moveq rts #0,dO /Yes => OK timeout: move. 1 #errline,dO bsr pline moveq rts #-l,d0 /Display timeout setdma: /Set DMA-addresse move.b 7(sp),dma+4 /LO 162 Abacus pline: pcrlf: pchar: pmsg: plong: pword: pbyt: phexwll: phexnib: Atari ST Disk Drives Inside and Out move.b 6(sp),dma+2 move.b 5(sp), dma rts /Print Line/CR bsr pmsg /Print CR,LF move #10,dO bsr pchar move #13,dO /Print Character DO move dO,-(sp) move #2, -(sp) trap #1 addq. 1 #4, sp rts /Print Line (DO) move.1 dO, -(sp) move #9, -(sp) trap #1 addq #6, sp rts /Display DO as an 8-digit hex moveq #7,dl bra phexwll /Print hex-word DO swap dO moveq #3, dl bra phexwll / Print hex- ■byte DO moveq #1, dl ror. 1 =#= 00 a o rol.l #4, dO move.1 dO, -(sp) move.1 dl, -(sp) bsr phexnib move.1 (sp)+,dl move.1 (sp)+,dO dbra dl,phexwll rts and. 1 #$0f,d0 add.b #$30,dO ;MID ;HI ; ** other subroutines ** number 163 Abacus Atari ST Disk Drives Inside and Out cmp. b #$3a,d0 bcs phexnl add. b #7, dO bra pchar ;Print character key: bsr pcrlf move.1 #keyrasg,dO bsr pmsg move #1,-(sp) trap #1 /Wait for keypress addq #2,sp rts head: dc. b "** Hard disk-Analysis 8/86 S.D. **",0 hi cc: dc. b "Cylinder ",o hi dhc: dc.b "Head ", 0 hi_lz: dc. b "Park Position ",0 hi_rt: dc.b "Seek Rate ",0 hi in: dc.b " Interleave ",0 hi spt: dc.b "Sectors/Track ",0 hd size: dc.b "Complete sectors ",0 bsl count: dc. b "Dead sectors ",0 align px on : dc. b "1st partition: ",0 boot: dc.b "Bootable ",0 px id: dc. b "Partition ID ",0 px start: dc. b "Start sector ",0 px size: dc. b "No. of sectors ",0 errline: dc. b "Timeout encountered!". 10,13,0 keymsg: dc. b "Press any key to continue",10,13,0 align bss ; DATA count: dc. 1 1 /Timeout counter spsave: dc.l 0 /User stackpointer ds. 1 200 STP: ds. 1 1 BUF: ds. b 512 /BUFFER FOR A SECTOR end And here is the BASIC loader, which creates the program anapart . TOS on the disk: 164 Abacus Atari ST Disk Drives Inside and Out 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 Analyze partition loader ?:fullw 2:clearw 2:gotoxy 0,0 ? "File >> anapart.tos << now being created":?:?:? dim c%( 634):cs#=0 for i=0 to 634 read a$:c%(i)=val("&H"+a$) check#=check#+(c%(i)) next i if check#= 6754423.04 then 70 ?"Can't go any farther;something wrong with the DATA." goto 80 bsave"anapart.tos",varptr(c%(0)), 1270 ? "The program >> anapart.tos « is now written." ?:?"Please press a key";:a=inp(2):end I i ********* DATA for anapart.tos ********** I DATA 601A, 0000,04AE,0000,0000, 0000, 052C, 0000 DATA 0000,0000,0000,0000,0000,0000,4FF9,0000 DATA 07D6,4 2A7,3F3C, 002 0,4E41,5C8F, 23C0,0000 DATA 04B2, 4879, 0000, 07DA,3F3C, 0001,2F3C, 0000 DATA 0000,6100,01AO,2F39,0000,04B2,3F3C,0020 DATA 4E41,5C8F,2 03C, 0000,0364,6100, 02AA, 203C DATA 0000, 0387,6100, 02BC,3039,0000, 0990, 6100 DATA 02C4,6100,02 96,203C,0000,039A, 6100, 02A4 DATA 1039,0000,0992,6100,02B4,6100,027E,203C DATA 0000, 03AD, 6100, 028C,1039, 0000, 0998, 6100 DATA 029C,6100,0266,203C,0000,03C0,6100,0274 DATA 1039,0000,0999,6100,0284,6100,024E,203C DATA 0000,03D3,6100,025C,1039,0000,099A,6100 DATA 026C,6100,0236,203C,0000,03E6,6100,0244 DATA 1039, 0000,099B, 6100,0254,6100, 021E, 203C DATA 0000,03F9,6100,022C,2039,0000,099C,6100 DATA 022E, 6100, 0206, 203C,0000, 040C, 6100, 0214 DATA 2039,0000,09D4,6100,0216,6100,01EE,4245 DATA 4286,4DF9,0000,09A0,6100,0242,6100,01DC DATA 13C5,0000,0420,0639,0030,0000,0420,0C36 DATA 0000, 6000, 6600, 001A,23FC, 206F, 7574,0000 DATA 042E,203C,0000,0420,6100,01AC,6000,0070 DATA 23FC, 2 0 6F,6E20, 0000,042E,203C, 0000, 0420 DATA 6100,0194,023 6, 0 0 80,6000, 67 00, 000C, 203C DATA 0000, 04 33, 6100, 0180,13F6, 6001, 0000, 044F DATA 33F6,6002,0000,0450,203C,0000,043D,6100 DATA 0166,203C,0000,0454,6100,0178, 2036, 6004 DATA 6100,017C,6100,0154,203C,0000,0467,6100 DATA 0162,2036,6008,6100,0166,6100,013E,5245 165 Abacus Atari ST Disk Drives Inside and Out 129 DATA 130 DATA 131 DATA 132 DATA 133 DATA 134 DATA 135 DATA 136 DATA 137 DATA 138 DATA 139 DATA 140 DATA 141 DATA 142 DATA 143 DATA 144 DATA 145 DATA 146 DATA 147 DATA 148 DATA 149 DATA 150 DATA 151 DATA 152 DATA 153 DATA 154 DATA 155 DATA 156 DATA 157 DATA 158 DATA 159 DATA 160 DATA 161 DATA 162 DATA 163 DATA 164 DATA 165 DATA 166 DATA 167 DATA 168 DATA 169 DATA 170 DATA 171 DATA 172 DATA 173 DATA 174 DATA 175 DATA DC7C,000C,BC7C,0030,6D00,FF52,6100,018E 4267,4E41,41F9,OOFF,8604,50F9,0000,043E 317C,0088,0002,4E71,20BC,0008,008A,2F2F 000A,6100,00E8,588F,6100,0058,6B00,003C 317C,0190,0002,4E71.317C,0090,0002,4E71 3OAF,0008,4E71,317C,008A,0002,4E71,20BC 0000,0000,6100,0076,6B00,0010,317C,008A 0002,2C10,CCBC,OOFF,OOFF,317C,0080,0002 4E71,2E10,CEBC,OOFF,OOFF,4279,0000,043E 4E75,317C,008A,0002,6100,0050,6B00,0030 4240,102F,0009,6100,0028,6B00,0022,102F OOOA,6100,001c,6BOO,0016,102F,OOOB,6100 0010,6B00,OOOA,302F,OOOC,6100,0004,4E75 4840,303C,008A,2080,6000,0010,23FC,0006 DDDO,0000,04AE,6000,OOOC,23FC,0000,3A98 0000,04AE,53B9,0000,04AE,6B00,0012,1039 OOFF,FA01,C03C,0020,66EA,7000,4E75,203C 0000,047A,6100,0020,7OFF,4E75,13EF,0007 OOFF,860D,13EF,0006,OOFF,860B,13EF,0005 OOFF,8609,4E75,6100,001A,303C,OOOA,6100 0006,303C,OOOD,3FOO,3F3C,0002,4E41,588F 4E75,2FOO,3F3C,0009,4E41,5C4F,4E75,7207 6000,OOOE,4840,7203,6000,0006,7201,E098 E998,2FOO,2F01,6100,0000,22IF,201F,51C9 FFFO,4E75,COBC,0000,OOOF,D03C,0030,B03C 003A,6500,0006,D03C,0007,60AA,619C,203C 0000,0491,61AC,3F3C,0001,4E41,544F,4E75 2A2A,2048,6172,6420,6469,736B,2D41,6E61 6C79,7369,7320,382F,3836,2053,2E44,2E20 2A2A,0043,796C,696E,6465,7220,2020,2020 2020,203A,2000,4865,6164,2020,2020,2020 2020,2020,2020,3A20,0050,6172,6B20,506F 7369,7469,6F6E,2020,203A,2000,5365,656B 2052,6174,6520,2020,2020,2020,3A20,0049 6E7 4,6572,6C65, 617 6,652 0,2020,2020,203A 2000,5365,6374,6F72,732F,5472,6163,6B20 2020,3A20,0043,6F6D,706C,6574,6520,7365 6374,6F72, 733A, 2000, 4465, 6164,207 3, 6563 746F,7273,2020,2020,3A20,0000,3173,7420 7061,7274,6974,696F,6E3A,2020,2020,0042 6F6F,7461,626C,6520,0050,6172,7469,7469 6F6E,2049,4420,2020,203A,2020,2020,2000 5374,6172,7420,7365,6374,6F72,2020,2020 3A20,004E,6F2E,206F,6620,7365,6374,6F72 7320,203A,2000,5469,6D65,6F75,7420,656E 636F,756E,7465,7265,6421,OAOD,0050,7265 7373,2061,6E79,206B,6579,2074,6F20,636F 166 Abacus Atari ST Disk Drives Inside and Out 176 DATA 6E74,696E,7565,OAOD,0000,0000,0002,1006 177 DATA 140E,OAOA,OEOA,OEOA,OEOA,OEOA,OEOA,OEOA 178 DATA OEOA,120E,0814,0612,0614,0C08,060A,16F8 179 DATA 0E06,1A90,0000 As we mentioned in the section on boot sectors, the px_f lag for each partition (maximum of four) indicates whether it is active and bootable. The Atari ST hard disk usually contains no bootable sectors because the ST cannot boot from the hard disk without the hard disk driver AHDI. PRG. The Seek Rate is usually given as 2, which corresponds to 3 ms per step. Interleave can be from 1 to 16 (sectors/track - 1), but it is usually 1. This represents the distance between two consecutively-numbered sectors on the track. The value behind Dead sectors indicates the number of defective sectors on the entire hard disk. These sectors are recognized and marked by the HDX. PRG program. A zero here means that the hard disk is perfect. Otherwise, one defective sector per megabyte is quite normal. 5.2 Connecting the hard disk The 19-pin jack on the back of the ST is the DMA interface. The hard disk is connected to this jack by the included (short) cable and thereby has direct access to the memory in ST via the DMA controller. The reason for the short cable lies in the high data transfer rate. Wires tend to act as antennae, so that signals from one wire can find their way into another and disturb the data exchange if the wires are too long. The data are transferred in parallel over eight data lines (pins 1-8), so that an entire byte can be transferred at one time. In addition, this interface has various service lines like Reset (pin 12), which the ST can use to reset the hard disk, or an interrupt line (pin 10), which the hard disk uses to signal the ST and acknowledge the reception of data. You can theoretically connect up to eight controllers with up to eight hard drives each to the Atari ST, but since there is no second connector on the hard disk, this would require some homebrew modifications. 167 Abacus Atari ST Disk Drives Inside and Out To communicate with the hard disk, the computer must send its commands in the form of command blocks over the data lines. These command blocks have already been described. In the HDC program this was accomplished by simply selecting the appropriate register and writing the command byte. The byte is then on the data lines and can be accepted by the hard disk, which then acknowledges it over the interrupt line. In order to permit any data exchange at all, the drive program AHDI. PRG (Atari Hard Disk Interface) must be loaded. This and the HDX program run only if TOS is built into the computer; the driver works, but the HDX program does not, which makes it impossible to use the hard disk. The hard disk must be formatted before it can be used and also partitioned, because the controller can process a maximum of 16 megabytes per partition. 5.3 Print the complete directory Folders are used heavily to organize the large number of files which the hard disk can store, and folders can even be nested within each other. This makes things much neater, but it can also make it harder to find a given file on the disk. To find the file, you must keep opening and closing folders in order to view their contents. It would be much more practical if we could just print out the entire contents of the hard disk or diskette. This is not directly possible with the Atari operating system, however. We will now present a program that does this. After it is loaded it asks for the drive designation (a-f) and then outputs the names of all the files on the selected disk to the printer, together with their folders. Folder contents are always indented two spaces to the right so that the nesting can be seen. In addition to the names, the length of each file is also given in decimal next to the name. An output like this can be quite long if the hard disk contains a lot of data, but it can be very useful for finding files and for checking to see if more than one copy of a file exists on the disk. Here is the program, written entirely in assembly language for the AssemPro assembler. If you use a different assembler, you may have to use an asterisk (*) instead of the semicolon for comments and replace the ds . x instruction with blk . x. 168 Abacus Atari ST Disk Drives Inside and Out ;** Display complete disk directory 8/86 S.D. ** run: sfirst: sea: seax: lea stp,sp move. 1 #menu,dO bsr pmsg bsr getkey cmp # ' a ' , dO bit run cmp #'f',d0 bgt run move.b dO,fname bsr pcrlf lea fname+7,a6 pea dta move #$la,-(sp) trap #1 addq.1 #6, sp clr d4 lea DEPTH,a4 move .b #0,(a4) bsr sfirst bra test move #$10,-(sp) pea fname move #$4e,-(sp) trap #1 addq.1 #8, sp cmp.b #'.',dta+30 bne seax bsr snextl tst dO bne seax bra sea rts /Input drive /False drive /False drive /Pointer to end of filename+1 /SETDTA /Depth=0 /Pointer to counter()-array /Counter=0 /subdirectory /SFIRST /Subdirectory? snext: add.b #1,(a4,d4) snextl: move #$4f,-(sp) trap #1 /SNEXT 169 Abacus Atari ST Disk Drives Inside and Out addq.1 #2, sp rts next: bsr snext test: tst dO bne up ;Go one level higher cmp.b #$10,dta+21 /Subdirectory ? bne output /No: Display entry bra down up: subq #1, d4 ;Depth=-l bmi ready /Ready! sub #6,a6 ml op: cmp.b #'\',-(a6) bne mlop bsr addwc /"*.* n ,0 added bsr sfirst clr d7 move.b (a4,d4),d7 /Counter(depth) in D7 addq #1, d7 /Counter+1 move.b #0, (a4,d4) selop: subq #1, d7 beq next /Ready for this level bsr snext /Look for counter(depth) entry bra selop down: move.1 #subl,a5 bsr prtline move.1 #dta+30,a5 bsr prtline bsr prtcr /Print CR addq #1, d4 /Depth+1 move.b #0,(a4,d4) subq.1 #4,a6 move #13,d7 lea dta+30,a3 flop: move.b (a3) +, dO beq f lopx move.b dO,(a6)+ /Transfer filename as path 170 Abacus Atari ST Disk Drives Inside and Out dbra d7,flop f lopx: bsr addwc ;0 added bp: bsr sfirst bra test /Look for next depth addwc: move.b (a6)+ move.b (a6)+ move.b ,(a6)+ move.b Desktop 171 Abacus Atari ST Disk Drives Inside and Out menu: subl: fname: ALIGN. W dc.b "** Directory Output S.D. **",10,13 dc.b "Please input drive letter(a-f)0 dc.b "Sub-Directory : ",0 dc.b "a:\*. *", 0, " ; ** Subroutines ** getkey: ;Get Key -> DO move #1,-(sp) trap #1 and.l #$ff,dO addq. 1 #2, sp rts pline: PCRLF: pchar: ;Print Line/CR bsr pmsg /PRINT CR,LF move #10,dO bsr pchar move #13,dO /Print Character DO move d0,-(sp) move #2,-(sp) trap #1 addq.l #4,sp rts prtline: prtx: /Print line from (a5) move.b (a5)+,dO beq prtx bsr prtchr bra prtline rts prtcr: prtchr: move bsr move move move trap addq. 1 rts #10,dO prtchr #13,dO dO, -(sp) #5, -(sp) #1 #4, sp /Print character /Print CR/LF /#2 for screen output /Print character 172 Abacus Atari ST Disk Drives Inside and Out pmsg: move. 1 dO, -(sp) move #9, -(sp) trap #1 addq rts #6, sp pdec8: number divu #10000,dO swap dO move dO, -(sp) swap dO and. 1 #$ffff,d0 move.1 #1000,dl bsr decl move (sp) +, dO pdec4: move.1 #1000,dl decl: divu dl, dO move.1 dO, -(sp) add #'0 1 ,dO move.b dO,(a5)+ move.1 (sp)+,dO swap dO and. 1 #$ffff,d0 divu #10,dl bne rts decl bss dta: ds . b 44 temp: ds. 1 0 depth: ds . b 10 ouTln: ds .b 80 ds. 1 200 stp: ds.l 1 end ;Print Line (DO) /Display DO as 8-digit decimal (•Remainder /Display DO as 4-digit decimal (•Characters in output line ; dat a Here is a BASIC loader that creates the program ALLDIR. TOS on the disk: 10 i********* ALLDIR loader A.S. ********* 15 1 20 ?:fullw 2:clearw 2:gotoxy 0,0 173 Abacus Atari ST Disk Drives Inside and Out 25 ? File >> alldir.tos << now being created":?:?:? 30 dim c%( 364):cs#=0 35 for i=0 to 364 40 read a$:c%(i)=val("SH"+a$) 45 check#=check#+(c%(i)) 50 next i 55 if check#= 3584742.08 then 70 60 ?"Can't go any farther;something wrong with the DATA." 65 goto 80 70 bsave"alldir.tos",varptr(c%(0)), 730 75 ? "The program >> alldir.tos << is now written." 80 ?:?"Please press a key";:a=inp(2):end 85 • 90 ********** DATA for alldir.tos ********** 100 DATA 601A,0000,02A8,0000,0000,0000,03AA,0000 101 DATA 0000,0000,0000,0000,0000,0000,4FF9,0000 102 DATA 064E,203C,0000,018C,6100,0250,6100,01FA 103 DATA B07C, 0061, 6DE6,B07C, 0066, 6EE0, 13C0, 0000 104 DATA 01E4,6100,01F8,4DF9, 0000, 01EB, 4879, 0000 105 DATA 02A8,3F3C,001A,4E41,5C8F, 4244,4 9F9, 0000 106 DATA 02D4,18BC, 0000, 6100, 0006, 6000, 0040,3F3C 107 DATA 0010, 4879, 0000, 01E4,3F3C, 004E,4E41,508F 108 DATA 0C39,002E,0000,02C6,6600,O00E,6100,0012 109 DATA 4A40, 6600, 0004,60E8,4E75, 0634,0001,4000 110 DATA 3F3C, 004F,4E41,548F,4E75,61EE,4A40, 6600 111 DATA 0012,0039,0010,0000,02BD,6600,0080,6000 112 DATA 002E, 5344, 6B00,00DE,9CFC, 0006, 0026, 005C 113 DATA 66FA,6100,005E,6196,4247,1E34,4000,5247 114 DATA 19BC,0000,4000,5347,67C0,61AE,60F8,2A7C 115 DATA 0000,01D3,6100,015E,2A7C,0000,02C6,6100 116 DATA 0154,6100,015E,5244,19BC,0000,4000,598E 117 DATA 3E3C,O00D,47F9, 0000, 02C6,101B, 6700, 0008 118 DATA 1CC0,51CF,FFF6,6100,000A,6100,FF42,6000 119 DATA FF7C,1CFC,005C,1CFC,002A,1CFC,002E,1CFC 120 DATA 002A,1CFC,0000,4E75,0C39,0008,0000,0E1B 121 DATA 6600,0006,6000,004E,41F9,0000,02C6,4BF9 122 DATA 0000,02DE,3A04,3AFC,2020,51CD,FFFA,1018 123 DATA 6700,0006,1AC0,60F6,1AFC,0020,BBFC,0000 124 DATA 02F8,6DF4,2039,0000,02C2,6100,OOFA,1ABC 125 DATA 0000,2A7C,0000,02DE,6100,OOBA,6100,00C4 126 DATA 6000,FF08,4267,4E41,2A2A,2020,2020,2044 127 DATA 6972,6563,746F,7279,204F,7574,7075,7420 128 DATA 2020,2053,2E44,2E20,2020,202A,2A0A,0D50 129 DATA 6C65,6173,6520,696E,7075,7420,6472,6976 130 DATA 6520,6065,7474,6572,2861,2D66,293A,0053 131 DATA 7562,2D44,6972,6563,746F,7279,203A,2000 132 DATA 613A,5C2A,2E2A,0020,2020,2020,2020,2020 174 Abacus Atari ST Disk Drives Inside and Out 133 DATA 134 DATA 135 DATA 136 DATA 137 DATA 138 DATA 139 DATA 140 DATA 141 DATA 142 DATA 143 DATA 144 DATA 145 DATA 2020 , 2020 , 2020 , 2020 , 2020 , 2020 , 2020,2020 2020,2020,2020,0000,3F3C,0001,4E41,C0BC 0000,00FF,548F,4E75,6100,0040,303C,000A 6100,0006,303C,OOOD,3FOO,3F3C,0002,4E41 588F,4E75,10ID,6700,0008,6100,0012,60F4 4E75,303C,000A,6100,0006,303C,OOOD,3F00 3F3C,0005,4E41,588F,4E75,2F00,3F3C,0009 4E41,5C4F,4E75,80FC,2710,4840,3F00,4840 COBC,0000,FFFF,223C,0000,03E8,6100,000A 301F,223C,0000,03E8,80C1,2F00,D07C,0030 1AC0,201F,4840,COBC,0000,FFFF,82FC,OOOA 66E6,4E75,0000,0002,061A,0A06,1016,1032 3A0A,1C44,061E,080E,0000 175 Chapter Six Abacus Atari ST Disk Drives Inside and Out The RAM disk The third member of the storage media family for the Atari ST which we will look at is the RAM disk. Using memory to imitate the actions of a disk drive is an interesting, and above all, very fast method of data storage. How does it work? First, we need an area of memory that cannot be used by any other application running on the computer. We will put data here instead of writing it to a diskette. The advantage is obvious: moving data in and out of memory can be done very quickly and easily by the 68000 processor in the ST. In addition, all of the mechanical operations that slow a disk drive (head positioning, spin up, etc.) are avoided. The result: a RAM disk is very fast. What we need now is a program to manage RAM disk memory and move the data into memory as required. There are such programs on the market, and some can be found in various books on the ST (such as Atari ST Tricks & Tips). They all follow the same principle, which we will now examine. First, the memory used by the RAM disk must be initialized. A boot sector must be created which contains all of the information about the type, partitioning, and size of the RAM disk. On real disks this sector is the first sector on the disk, so these parameters must be written at the start of the RAM disk memory. Next, the program must install itself so that it knows whether a data transfer is to take place, and if so, in what direction the transfer is to go. This is accomplished by changing three operating system pointers to point to our routine. These pointers are memory locations which contain the addresses of programs. If the operating system wants to call such a program, it reads the appropriate pointer and branches to the address indicated. The pointers which are used for installing the RAM disk are intended for servicing the hard disk. They lie at memory addresses $472 to $47E and point to routines which have die following significance: Address Name Significant $472 hdv_bpb Determine and return the parameter block, which contains specifications about the diskette or hard disk. $476 hdv_rw Read/write routine for the hard disk. Data transfer takes place via this vector. 179 Abacus Atari ST Disk Drives Inside and Out Address Name Significance $47 A hdv_boot Boot routine for the hard disk. Not required by the RAM disk because it cannot be booted. $47E hdv mediach Determine if the medium (disk) was changed. Once the pointers have been changed and their old contents saved, the program can be exited. A special BIOS call is used to do this which allows a given area of memory to be reserved. The RAM disk is now installed. Now we have to prepare a Desktop disk icon for the RAM disk. To do this we click on one of the disk icons, select the menu option Install disk drive . . . and change the name and designator of the drive. After selecting OK, another icon appears on the screen. This can then be used in the usual manner for loading and storing data and programs. Only the functions Format. . . and disk copy do not work, so only individual files can be copied or deleted. Now when the operating system wants to access the hard disk or RAM disk, it always jumps to the RAM disk program, which is still in memory, via the pointers mentioned above. The RAM disk program checks to see if the RAM disk is being accessed or not. If not, a branch is made to the actual routine, whose address was saved. If the RAM disk is accessed, the program starts to work. For a read/write access, the parameters like sector, number of sectors to read, and data transfer direction are read from the stack and the appropriate data are copied into memory. If the operating system performs a "media changed" test (mediach), the RAM disk program returns a 0, which means that the medium was not changed, which of course is impossible with a RAM disk. The third type of call means that the operating system wants the memory address of the parameter block. The address is returned in register DO. This is all a RAM disk program does. What it can't do is retain data after the computer is turned off. This is the main disadvantage of this program: The data are not really saved, just temporarily stored. For this reason, files and programs that you create or modify on the RAM disk must be copied to a real diskette or hard disk before you turn the computer off. But enough of theory. Let's take a look at a RAM disk program which contains all of these elements. 180 Abacus Atari ST Disk Drives Inside and Out 6.1 An easy-to-use RAM disk program The program listed in this chapter contains some features which really aren't required for the normal use of a RAM disk. But they are quite useful, and although they make the program somewhat longer, they also make the program easier to use. The program is designed for use of the RAM disk as drive C, but it can be easily adapted for a different drive letter. The program is a desk accessory, which appears in the Desk menu under the name ramdisk . ACC after booting. If this menu option is selected, a small dialog box appears which contains three options. The first option, which is outlined, is EXIT. If you click this button or press , the box will disappear and nothing will happen. This button is provided in case you accidentally select the menu entry RAMDISK.ACC. The second button is labeled MORE. Clicking this button changes the number in the selection box on the right. This number indicates the size of the RAM disk to be installed. Clicking MORE will cause this number to increase in steps of 100 up to 800, whereupon another click will return it to zero. Once the desired RAM disk capacity has been set, click on the button with the number. Since the old contents of the RAM disk will be erased when a new memory area is installed, another dialog box appears. This contains the question, Erase old contents of the RAM disk? which must be answered with Yes, or the RAM disk's old capacity and contents will remain intact. After all of the settings have been made by selecting Yes, the program goes to work. It first releases the memory area which the RAM disk has previously occupied back to the operating system. Then the program attempts to reserve the desired memory area for itself. If there is not enough memory available, you get the message Not enough RAM. After this message is acknowledged, both the message and the RAM disk disappear. You must call the RAMDISK .ACC accessory again and choose a smaller RAM disk size. If you select zero as the capacity of the RAM disk, it will be completely removed and will not occupy any memory. The program can thus change 181 Abacus Atari ST Disk Drives Inside and Out the size of its RAM disk and install and remove it as often as desired. Most of the RAM disk programs on the market do not have this capability, and if you use RAM disks a lot you will appreciate these advantages. One more thing should be mentioned before we look at the program itself. Since a RAM disk cannot be formatted (please don't try it, because it may address the disk drives and accidentally format a real diskette instead), each file must be deleted individually in order to delete such a "disk." With this program, you just select the same capacity in the dialog box, and the whole RAM disk will be erased. Here is the program: .***** RAM-Disk with comfort S.D. ***** hdv bpb = $472 hdv_rw = $476 hdv mediach = $47e drvbits = $4c2 start: move.1 #nstack,a7 ;set new stack move #10,opcode ;appl init move #0,sintin move #1,sintout move #0,saddrin move #0,saddrout bsr aes move intout,appid /Application ID move #77,opcode /graf handle move #5,sintout move #0,saddrin move #0,saddrout bsr aes move intout,grhandle /Graphic handle move #35,opcode /Menu Register move #1,sintin move #1,sintout move #1,saddrin move appid,intin move.1 faccname,addrin bsr aes 182 Abacus Atari ST Disk Drives Inside and Out move intout,accid /Accessory number /** Here is the preparation loop ** ok: bsr event /Event_Multi cmp #40,msgbuff /Acc open ? bne loop /no move msgbuff+8,dO cmp accid,dO /our accessory number ? bne loop / no bsr run /display menu bra loop /and again Selection ** move.1 #howmuch,addrin bsr formalert /display selection move intout,choice cmp #1,choice /Exit? beq ende /yes => end cmp #3,choice /OK ? beq ok /yes addq #2,size /display different size cmp #18,size /over 800 KByte? bit more / no clr size /no, back to 0 KByte lea sizes,aO clr .1 dO move size,dO move 0(aO, dO),capacit ;set new size lsl #1, dO lea deci,aO move.1 0(aO,dO),offer /display new size bra run /repeat :serve : memory * move.1 tclear,addrin bsr formalert /really erase it? cmp #2,intout beq okx /no => end bsr mf ree /release memory tst size /0 KByte ? bne okl / no 183 Abacus Atari ST Disk Drives Inside and Out okx: rts /0 Kbyte: done okl: move #2,changed /'Disk changed' clr. 1 d7 move capacit,d7 /capacity in Kbyte add. 1 #9,d7 /plus 9K for management asl. 1 #5, d7 asl. 1 #5,d7 /times 1024: capacity in bytes move. 1 d7, -(sp) /RAM area to install move #$48,-(sp) /MALLOC function trap #1 addq.1 #6, sp tst. 1 dO /error occurred ? beq terror /yes => error message move.1 dO,buffer /save start address of the RAM disk move.1 #init,-(sp) move #38,-(sp) /Initialization in Supervisor trap #14 addq.1 #6, sp rts terror: move.1 terror,addrin bsr formalert /'Not enough RAM !' bra ende /terminate init: move.1 hdv bpb,bpbsave /save old vectors move.1 #bpb,hdv bpb move. 1 hdv rw,rwsave /set vectors to new routines move. 1 #rw,hdv rw move. 1 hdv mediach. mediasave move.1 #media,hdv_mediach move. 1 buffer,aO move.1 #10240/4,dO iloopl: clr .1 (aO) + /clear boot sector and FATs dbra dO,iloopl ; * Generate boot sector ★ move.1 buffer,aO add. 1 #11,aO /at buffer+11 lea boottab,al moveq #tabend-boottab-l, dO 184 Abacus Atari ST Disk Drives Inside and Out bloop: move.b (al)+,(a0)+ /copy data in boot sector dbra dO,bloop move capacit,d7 move d7,numcl /capacity in KByte in BPB lsl #1, d7 /capacity in sectors add #18,d7 /plus 18 sectors move. 1 buffer,aO add. 1 #19,aO /in buffer+19 and +20 move. b d7,(aO)+ /LO lsr #8,d7 move. b d7, (aO) /HI bset #2,drvbits+3 /install drive C rts / done /* Function: Get BPB * bpb: cmp #2, 4(sp) /Drive C ? beq bpbl / yes move. 1 bpbsave,aO /old routine jmp (aO) / call bpbl: move.1 #bpbtab,dO /Pointer to BIOS parameter block rts /* Function: Read/Write * rw: cmp #2, 14(sp) /Drive C ? beq rwl /yes move.1 rwsave,aO /old routine jmp (aO) / call rwl: move 12(sp),dO /recno, logical sector number ext. 1 dO lsl.l #8,dO lsl.l #1, dO /times 512 move.1 6 (sp),a0 /buffer address move 10(sp),dl /number of sectors subq #1, dl move. 1 buffer,al /base address add. 1 dO, al /plus relative address in RAM-Disk move 4(sp),dO /rwflag 185 Abacus Atari ST Disk Drives Inside and Out btst beq exg rloopO: move. rloop: move.b dbra dbra clr rts ;* Function: media: cmp beq move.1 jmp medial: move clr rts event: move move move move move.1 lea lea moveq lopl: move dbra bsr rts aes: move.1 move trap rts mfree: tst. 1 beq #0, dO rloopO aO, al /read ? / yes /exchange destination and source 1 #511,dO (al)+, (aO) + dO,rloop dl,rloopO dO ;one sector /copy buffer /next sector /OK Media-Change #2,4(sp) medial mediasave,aO (aO) /Drive C ? ; yes /old routine / call changed,dO /Diskette changed changed /but just once #25,opcode /Event_Multi, determine GEM event #16,sintin #7,sintout #1,saddrin #msgbuff,addrin table,al intin,a2 #15,dO (al)+,(a2)+ /set parameters dO,lopl aes / AES call #aespb,dl #$c8,dO #2 /release memory buffer ende /is already removed 186 Abacus Atari ST Disk Drives Inside and Out move.l freinit,-(sp) move #38,-(sp) /reinitialization trap #14 ;in supervisor mode addq.1 #6,sp move.l buffer,-(sp) move #$49,-(sp) /MFREE function, release memory trap #1 addq.1 #6,sp tst.l dO /error? beq ende /no move.l #errorl,addrin bsr formalert /error message ende: clr.1 buffer /no more memory reserved rts reinit: move.1 bpbsave,hdv_bpb move.l rwsave,hdv_rw /set vectors to old routine move.l mediasave,hdv_mediach bclr #2,drvbits+3 /remove old routine rts formalert: move #52,contrl /form_alert, display alarm window move #l,contrl+2 move #l,contrl+4 move #l,contrl+6 move #0,contrl+8 move #1,intin bsr aes rts table: accname: align howmuch: offer: clear: error: error1: align . 1 dc.w $13,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0 dc.b " RAM-Disk C ",0 dc.b "[l][Size of RAM disk in Kbytes? ]" dc.b "[Exit I more I" dc.b " 100 ]", 0,0 dc.b "[1) [Erase old contents I of the RAM disk?]" dc.b "[ Yes! | No ]", 0,0 dc.b "[2][Not enough RAM !]" dc.b "[OK]", 0,0 dc.b "[2][Error during MFREE !]" dc.b "[OK]”, 0,0 187 Abacus Atari ST Disk Drives Inside and Out capacit: dc.w 100 size: dc.w 2 sizes: dc.w 0,100,200 ,300,400,500,600,700,800 deci: dc.b ' 0 100 200 300 400 500 600 700 800' buffer: dc.l 0 ;RAM disk buffer address changed: dc.w 0 /Flag for "disk changed" bpbtab: recsiz: dc.w $200 /Sector size clsiz: dc.w 2 /Cluster size in sectors clsizb: dc.w $400 /Cluster size in bytes rdlen: dc.w 7 /Directory length in sectors f siz: dc.w 5 /FAT size fatrec: dc.w 6 /FAT sectors datrec: dc.w 18 /Sectors for management numcl: dc.w 1 /capacity in Kbytes flags: dc.l 0,0,0,0 boottab: , ; data in 8086 format dc.b 0,2 /bytes per sector dc.b 2 /sectors per cluster dc.b 1,0 /reserved sectors dc.b 2 /FATs dc.b 112,0 /directory entries dc.b 2 /sectors on media dc.b 0 /media descriptor dc.b 5,0 /sectors per FAT dc.b 9,0 /sectors per track dc.b 1,0 /sides dc.b 0 /hidden tabend: align bpbsave: ds.l 1 /Space for old vectors rwsave: ds.l 1 mediasave : ds.l 1 aespb: dc.l contrl,global,intin,intout,addrin,addrout bss choice: ds.w 1 grhandle: ds.w 1 appid: ds.w 1 ; data ;Application ID 188 Abacus Atari ST Disk Drives Inside and Out accid: msgbuff: nstack: contr1: opcode: sintin: sintout: saddrin: saddrout: global: intin: ptsin: intout: ptsout: addrin: addrout: ds .w 1 ds.w 16 ds.L 128 ds.l 1 ds.w 1 ds.w 1 ds.w 1 ds.w 1 ds.l 1 ds.w 5 ds.l 8 ds.w 80 ds.w 80 ds.w 80 ds.w 80 ds.w 80 ds.w 80 end /Accessory unit /NEW STACK /GEM parameter block This program was created with the AssemPro macro-assembler, which differs in certain respects from the DRI assembler included in the Atari Developer's Package. The comment lines need to be changed, which for the DRI assembler must start with an asterisk (*), the align instruction which must be even, and the bss instruction must be data for DRI. The program is divided into a number of parts: 1. Installation of the accessory. 2. Preparation loop, which in normal operation of the Atari ST runs constantly in the background and may therefore never end. 3. Display and service dialog box, whereby the selected capacity is placed in CAPACIT. 4. Display dialog prompt 5. Release previously used memory (MFREE). 6. Reserve new memory, output error message if not enough. 7. Save BIOS vectors for the disk routines and set new vectors. 8. GETBPB function. 9. Read/write function. 10. Media change function. 11. Data fields for parameter blocks. 189 Abacus Atari ST Disk Drives Inside and Out Points 7 to 10 were already discussed in the previous section. A complete description of points 1 though 6 would be too comprehensive to take up here. Information on the functions used can be found in the books Atari ST Internals and Atari ST GEM Programmer's Reference by Abacus Software. Here is a BASIC loader program which creates the accessory program RAMDISK. ACC on the disk: 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 ramdisk.acc loader ?:fullw 2:clearw 2:gotoxy 0,0 ? "File >> a:ramdisk.acc << now being created":?:?:? dim c%( 735):cs#=0 for i=0 to 735 read a$:c%(i)=val("SH"+a$) check#=check#+(c%(i)) next i if check#= 4997481.92 then 70 ?"Can't go any farther;something wrong with the DATA." goto 80 bsave"a:ramdisk.acc",varptr(c%(0)), 1472 ? "The program >> a:ramdisk.acc << is now written." ?:?:?:?"Please press a key":a=inp(2):end I i********* DATA for a:ramdisk.acc ********** I DATA 601A,0000,053A,0000,0000,0000,0622,0000 DATA 0000,0000,0000,0000,0000,0000,2E7C,0000 DATA 0762,33FC,OOOA,0000,0766,33FC, 0000, 0000 DATA 0768,33FC,0001,0000,076A,33FC,0000,0000 DATA 076C,33FC,0000,0000,076E,6100,02EE,33F9 DATA 0000,08DC,0000,053E,33FC,004D,0000,0766 DATA 33FC,0005,0000,076A,33FC,0000,0000,076C DATA 33FC,0000,0000,076E,6100,O2C0,33F9,0000 DATA 08DC,0000,053C,33FC,0023,0000,0766,33FC DATA 0001,0000, 0768,33FC,0001,0000,076A, 33FC DATA 0001,0000,076C,33F9,0000,053E,0000,079C DATA 23FC,0000,03EC,0000,0A1C,6100,027E,33F9 DATA 0000,08DC,0000,0540,6100,022C,0C79,0028 DATA 0000, 0542, 66F2,303 9,0000, 054A,B07 9, 0000 DATA 0540,66E4,6100,0004,60DE,23FC,0000,03FC DATA 0000,0A1C, 6100,02BC,33F9, 0000,08DC, 0000 DATA 053A,0C79,0001,0000,053A,6700,0276,0C79 DATA 0003, 0000,053A, 6700,0042,5479, 0000, 04A6 190 Abacus Atari ST Disk Drives Inside and Out 118 DATA 0C79,0012,0000,04A6,6D00,0008,4279,0000 119 DATA 04A6,41F9,0000,04A8,4280,3039,0000,04A6 120 DATA 33F0,0000, 0000, 04A4,E348, 41F9, 0000, 04BA 121 DATA 23F0,0000,0000,042A,6090,23FC,0000,0432 122 DATA 0000,0A1C,6100,024C,0C79,0002,0000,08DC 123 DATA 6700,0010,6100,01D2,4A79,0000,04A6,6600 124 DATA 0004,4E75,33FC,0002,0000,04E2,4287,3E39 125 DATA 0000,04A4,DEBC,0000,0009,EB87,EB87,2F07 126 DATA 3F3C,0048,4E41,5C8F,4A80,6700,0018,23C0 127 DATA -0000,04DE,2F3C, 0000,01BA,3F3C, 0026, 4E4E 128 DATA 5C8F,4E75,23FC,0000,046A,0000,0A1C,6100 129 DATA 01E2,6000,01AE,23F9,0000,0472,0000,0516 130 DATA 23FC,0000,0250,0000,0472,23F9,0000,0476 131 DATA 0000,051A,23FC,0000,026A,0000,0476,23F9 132 DATA 0000,047E,0000,051E,23FC,0000,02BA,0000 133 DATA 047E,2079,0000,04DE,203C,0000,OAOO,4298 134 DATA 51C8,FFFC,2079,0000,04DE,D1FC,0000,000B 135 DATA 43F9,0000,0504,7010,10D9,51C8,FFFC,3E39 136 DATA 0000,04A4,33C7,0000,04F2,E34F,DE7C,0012 137 DATA 2079,0000,04DE,D1FC,0000,0013,10C7,E04F 138 DATA 1087,08F9,0002,0000,04C5,4E75,0C6F,0002 139 DATA 0004,6700,000A,2079,0000,0516,4ED0,2O3C 140 DATA 0000,04E4,4E75,0C6F,0002,000E,6700, OOOA 141 DATA 2079,0000,051A,4ED0,302F,000C,48C0,E188 142 DATA E388,206F,0006,322F,OOOA,5341,2279,0000 143 DATA 04DE,D3C0,302F,0004,0800,0000,6700,0004 144 DATA C348,203C,0000,01FF,10D9,51C8,FFFC,51C9 145 DATA FFF2,4240,4E75,0C6F,0002,0004,6700, OOOA 146 DATA 2079,0000,051E,4ED0,3039,0000,04E2,4279 147 DATA 0000,04E2,4E75,33FC,0019,0000,0766,33FC 148 DATA 0010,0000,0768,33FC,0007,0000,076A,33FC 149 DATA 0001,0000,076C,23FC,0000,0542,0000,0A1C 150 DATA 43F9,0000,03CC,45F9,0000,079C,700F,34D9 151 DATA 51C8,FFFC,6100,0004,4E75,223C,0000,0522 152 DATA 303C,00C8,4E42,4E75,4AB9,0000,04DE,6700 153 DATA 0032,2F3C,0000,036E,3F3C,0026,4E4E,5C8F 154 DATA 2F39,0000,04DE,3F3C,0049,4E41,5C8F,4A80 155 DATA 6700,0010,23FC,0000,0485,0000,0A1C,6100 156 DATA 0032,42B9,0000,04DE,4E75,23F9,0000,0516 157 DATA 0000,0472,23F9,0000,051A,0000,0476,23F9 158 DATA 0000,051E,0000,047E,08B9,0002,0000,04C5 159 DATA 4E75,33FC,0034,0000,0766,33FC,0001,0000 160 DATA 0768,33FC,0001,0000,076A,33FC,0001,0000 161 DATA 076C,33FC,0000,0000,076E,33FC,0001,0000 162 DATA 079C,6100,FF56,4E75,0013,0001,0001,0001 191 Abacus Atari ST Disk Drives Inside and Out 163 DATA 0000,0000,0000 164 DATA 0000,0000,0000 165 DATA 736B,2020,4320 166 DATA 206F,6620,5241 167 DATA 4B62,7974,6573 168 DATA 6D6F,7265,207C 169 DATA 5D5B,4572,6173 170 DATA 656E,7473,7C20 171 DATA 2064,6973,6B3F 172 DATA 4E6F,205D,0000 173 DATA 6F75,6768,2052 174 DATA 005B,325D,5B45 175 DATA 6720,4D46,5245 176 DATA 0064,0002,0000 177 DATA 0258,02BC,0320 178 DATA 3030,2033,3030 179 DATA 3030,2037,3030 180 DATA 0200,0002,0400 181 DATA 0000,0000,0000 182 DATA 0002,0201,0002 183 DATA 0000,0000,0000 184 DATA 0766,0000,0770 185 DATA 0A1C, 0000, OABC 186 DATA 0408,0808,080A 187 DATA 040C,0806,0E04 188 DATA 0808,0804,OCOE 189 DATA 0E06,0A12,OCOE 190 DATA 0808,0806,0406 191 DATA 0A16,0808,0808 ,0000,0000,0000,0000,0000 , 0000,2020,5241,4D2D, 4469 , 0000,5B31,5D5B, 5369, 7A65 , 4D2 0, 64 69, 736B,2 069, 6E20 , 3F20,5D5B, 457 8, 697 4,7C20 , 2 031,3030,205D, 0000,5B31 , 6520, 6F6C, 6420, 636F,6E74 , 6F66,2074,6865,2052,414D , 5D5B,2 059, 657 3,2120,7C20 , 5B32,5D5B,4E6F, 7420, 656E , 414D,2021,5D5B,4F4B,5D00 , 7272,6F72,20 64,757 2, 696E , 4520,215D,5B4F,4B5D,0000 , 0064,00C8,012C,0190,01F4 ,2020,3020,2031,3030,2032 ,2034,3030,2035,3030,2036 ,2038,3030,0000,0000,0000 ,0007,0005,0006,0012,0001 ,0000,0000,0000,0000,0000 ,7000,0200,0500,0900,0100 ,0000,0000,0000,0000,0000 ,0000,079c,0000,08DC,0000 ,0000,0002,0808,0808,08OA ,0408,0808,0806,0406,04OA ,0A04,080C,0A08,OA06,0808 ,0E08,2006,1004,1206,0E06 ,060C,2608,121C,3408,060A ,0614,OEOA,0E14,040A,080A ,0801,6204,0404,0404,0000 When you turn your computer on and install the RAM disk, you often have to copy certain files to the RAM disk before you can start working. To save you time and effort in doing this, we have written a program that takes care of this for you. 192 Abacus Atari ST Disk Drives Inside and Out 6.2 Disk to RAM disk copy The following program simply copies the entire contents of a single-sided disk to the RAM disk C. All sectors from 0 (logical sector number) to 9*80-1 (719) are read from the selected drive and copied to the "sectors" of the RAM disk. You must make sure that the RAM disk has a capacity of at least 400K so that sector 719 also exists. In order to make the program as fast as possible, we read or write nine sectors at a time each time we call the routine FLOPRW. This speed advantage over copying sectors individually is supplied by the DMA chip, which can be programmed to read nine sectors (an entire track) at once and send then to the computer. The speed advantage is not tremendous, but every little bit helps. Naturally, it would go even faster if all of the sectors on the disk were read with one call, but this would produce certain memory size problems. If you use this program with a double-sided disk, all of the filenames from the diskette will naturally appear in the directory of the RAM disk. The directory is copied in its entirety, but not the other side of the disk. If the original disk is more than half full, the programs and files on the other side cannot be loaded into the RAM disk. Otherwise this program also works with double-sided disks. Let's look at the program: ;*** Disk - to - RAM-Disk - Copy S.D. run: clr. 1 aplrsv clr. 1 ap2rsv clr.l ap3rsv clr. 1 ap4rsv move #10,opcode ;appl_init move 40 ,sintin move #1,sintout move #0,saddrin move #0,sintin jsr aes move #77, opcode ;graf handle move #5, sintout move #0, saddrin move #0, saddrout 193 Abacus Atari ST Disk Drives Inside and Out jsr aes move intout,grhandle move. 1 ♦alarm,dO bsr formalert subq #2, dO tst dO bmi quit move dO,drive clr sector loop: move drive,dl move #2, dO bsr floprw bne readerr move #2, dl move #1, dO bsr floprw bne wrerr add #9,sector cmp #9*80,sector bit loop quit: clr -(sp) trap #1 floprw: move dl, -(sp) move sector,-(sp) move #9,-(sp) pea buffer move dO, -(sp) move #4,-(sp) trap #13 add. 1 #14, sp tst dO rts readerr: move.1 freer,dO bsr formalert bra quit /output selection window ;correct drive number /terminate /save drive number /start with sector 0 /selected diskette / read /read 9 sectors /error during read! /drive C = RAM disk /write /write 9 sectors /error during write! /sector number + 9 /end ? / no /exit => desktop /read/write diskette /drive /start sector /read/write 9 sectors /buffer /read/write /rwabs function /test for error /"Error during read!" 194 Abacus Atari ST Disk Drives Inside and Out wrerr: move.1 #wrer,dO bsr formalert ;"Error during write" bra quit aes: ;AES call move.1 #aespb,dl move #$c8,d0 trap #2 rts formalert: move move move move move move move.1 jsr move rts alarm: dc.b "[1][Source drive to I copy from ?]" dc.b "[Exit| A | B 1 ", 0,0 reer: dc.b "[2] [Error during read!] [Quit]", 0,0 wrer: dc.b "[2][Error during write!][Quit]",0,0 ALIGN.L aespb: dc.l contrl,global,intin,intout,addrin,addrout bss ;DATA contrl: /various fields for the AES opcode: dc.w 1 sintin: dc.w 1 sintout: dc.w 1 saddrin: dc.w 1 saddrout: dc.l 1 dc.w 5 global: dc.w 7 aplrsv: dc.l 1 ap2rsv: dc.l 1 ap3rsv: dc.l 1 ap4rsv: dc.l 1 intin: dc.w 128 #52,contrl ;form_alert #1,contrl+2 #1,contrl+4 #1,contrl+6 #0,contrl+8 #1,intin dO,addrin aes intout,dO 195 Abacus Atari ST Disk Drives Inside and Out ptsin: dc .w 128 intout: dc .w 128 ptsout: dc ,w 128 addrin: dc .w 128 addrout: dc .w 128 grhandle: dc .w 1 drive: dc ,w 1 /drive number sector: dc .w 1 /sector counter buffer: dc .w 9*512 /buffer for 9 sectors end The rather simple construction of this program makes some variations easily possible. For example, you can copy double-sided disks to an 800K RAM disk by changing the end condition in CMP #9*80, SECTOR command by simply inserting #9*80*2. Another variation would be to make the copy direction selectable. This would make it possible to copy the RAM disk contents back to the diskette when you are done working. It would also be interesting to convert the program into a desk accessory. Equipped with various additional functions, it could be a very useful tool. Here is the BASIC loader for the program. It creates the program dsktoram . prg on the diskette: 10 i********* dsktoram loader A.S. ********* 15 20 ?:fullw 2:clearw 2:gotoxy 0,0 25 ? "File >> a:dsktoram.prg << now being created":?:?:? 30 dim c%( 279):cs#=0 35 for i=0 to 279 40 read a$:c%(i)=val("&H"+a$) 45 check#=check#+(c%(i)) 50 next i 55 if check#= 2432944.96 then 70 60 ?"Can't go any farther;something wrong with the DATA." 65 goto 80 70 bsave"a:dsktoram.prg",varptr(c%(0)), 560 75 ? "The program >> a:dsktoram.prg << is now written." 80 ?:?"Please press a key":a=inp(2):end 85 196 Abacus Atari ST Disk Drives Inside and Out 90 i********* DATA for a:dsktoram.prg ********** 95 100 DATA 601A,0000,01E4,0000,0000,0000,0034,0000 101 DATA 0000,0000,0000,0000,0000,0000,42B9,0000 102 DATA 01F4,42B9,0000,01F8,42B9,0000,01FC,42B9 103 DATA 0000,0200,33FC,OOOA,0000,01E4,33FC,0000 104 DATA 0000,01E6,33FC,0001,0000,01E8,33FC,0000 105 DATA 0000,01EA,33FC,0000,0000,01E6,4EB9,0000 106 DATA 0108,33FC,004D,0000,01E4,33FC,0005,0000 107 DATA 01E8,33FC,0000,0000,01EA,33FC,0000,0000 108 DATA 01EC,4EB9,0000,0108,33F9,0000,0208,0000 109 DATA 0210,203C,0000,015A,6100,0098,5540,4A40 110 DATA 6B00,0042,33C0,0000,0212,4279,0000,0214 111 DATA 3239,0000,0212,303C,0002,6100,002C,6600 112 DATA 004C,323C,0002,303C,0001,6100,001C,6600 113 DATA 0048,0679,0009,0000,0214,0C79,02D0,0000 114 DATA 0214,6DCC,4267,4E41,3F01,3F39,0000,0214 115 DATA 3F3C,0009,4879,0000,0216,3F00,3F3C,0004 116 DATA 4E4D,DFFC, 0000, 000E,4A40,4E75,203C, 0000 117 DATA 018C,6100,001E,60CC,203C,0000,01AB,6100 118 DATA 0012,60C0,223C,0000,01CC,303C,00C8,4E42 119 DATA 4E75, 33FC, 0034,0000,01E4,33FC, 0001,0000 120 DATA 01E6,33FC,0001,0000,01E8,33FC,0001,0000 121 DATA 01EA,33FC, 0000, 0000,01EC,33FC, 0001,0000 122 DATA 0204,23C0,0000,020C,4EB9,0000,0108,3039 123 DATA 0000,0208,4E75,5B31,5D5B,536F,7572,6365 124 DATA 2064,7269,7665,2074,6F20,7C20,636F,7079 125 DATA 2066,726F,6D20,3F5D,5B45,7869,747C,2041 126 DATA 207C,2042,205D,0000,5B32,5D5B,4572,726F 127 DATA 7220,6475,7269,6E67,2072,6561,6421,5D5B 128 DATA 5175,6974,5D00,005B,325D,5B45,7272,6F72 129 DATA 2064,7572,696E,6720,7772,6974,6521,5D5B 130 DATA 5175,6974,5D00,OOEA,0000,01E4,0000,01F2 131 DATA 0000, 0204,0000, 0208,0000, 020C, 0000,020E 132 DATA 0000,0002,0606,0608,0808,0808,0608,0808 133 DATA 0806,0604,0612,0606,2408,OEOA,180C,0C10 134 DATA 0808,0808,0806,0606,7804,0404,0404,0001 197 Chapter Seven Abacus Atari ST Disk Drives Inside and Out Programming a disk monitor The programs presented thus far in this book allow you to view and modify some data on the diskette, but what is a disk book without a disk editor which you can use to view and change all of the data on the disk? Since I've had seven years of experience in typing in programs from magazines and books, I'd like to try to save you some frustration and present a quasi- modular construction of the program. We'll look at the program section by section, and this will hopefully lead to a complete program with relatively little typing effort. This listing edit. s contains all of the menu options and all of the data and subroutines of the entire disk editor, but only the subroutines of the sector menu are listed and all others routines contain just an RTS. The listing sub rout . s contains the subroutines which are called by the main program, which you can insert at the locations of the place holders in the program edit. s as required, and thereby gradually build the program up to its full power and size. When entering the program, your biggest problem in producing the editor will be the naming of labels and variables. Eight significant characters are simply too few for such a comprehensive assembly language project in order to formulate suggestive and logical names for subroutines and variables. If you want to use the editor right away, or you don't wish to type it in, it is also possible to order the optional diskette from Abacus which contains many of the programs and sources in this book (see the back of this book for ordering information). 201 Abacus Atari ST Disk Drives Inside and Out 7.1 The TOS functions for disk access The editor functions are largely built on operating system functions (TOS or GEMDOS), and only a few directly access the disk controller and DMA chip of the Atari ST. It is possible to access these chips from various levels of the hierarchically-organized TOS. The high-level languages like Pascal, C, FORTRAN, and BASIC make it possible to work with sequential and random-access files, which means that a high-level language does not divide the disk into tracks and sectors, but that the disk access is file-oriented and moves only within the limits of these files. If we go one step deeper in the hierarchy of the operating system, we see that high-level languages use the GEMDOS functions. These GEMDOS functions are offered to the languages by operating-system routines, meaning that these GEMDOS functions are still file-oriented and offer only random and sequential access to files on the disk. At the next level we encounter the BIOS functions, which make the first real physical contact with the diskette possible, in which the disk is divided into logical sectors from 0 to the maximum possible number (1440 for double-sided disks, 720 for single-sided disks). The BIOS functions thus allow access to all sectors on the disk, but we still don't know which track and side the given logical sector is on. This information can be computed with the help of the specifications in the BIOS parameter block. If we use the XBIOS functions, it is possible to access tracks, sides, and physical sectors. The user must know how many sectors there are on a track, etc. The XBIOS functions offer ways of determining such disk-specific properties. For example, you can format individual tracks and specify the desired number of sector per track. The center of all routines are the WD1772 disk controller and the DMA chip, which occupy I/O addresses in the range $FF800 to $FFFFF. For example: The BASIC command WRITE#, which writes data to a sequential file, uses the GEMDOS function WRITE, which in turn calls the BIOS function RWABS, which makes use of the XBIOS function FLOPWR, which finally tells the DMA and controller chips where and what to write on the disk. 202 Abacus Atari ST Disk Drives Inside and Out All operating system functions (GEMDOS, BIOS, XBIOS) are described in detail in the Abacus book Atari ST Internals, so we will discuss only the eight BIOS and XBIOS calls which directly communicate with the disk. All of the calls expect their parameters on the stack, and return results or a negative error code in the case of an error in the register DO. Registers D0-D2 and A0-A2 are often changed after a call, and so must be saved if their contents are required. The two BIOS functions RWABS and GETBPB are called via the BIOS specific TRAP #13 and perform the following: RWABS: BIOS function number 4 This very flexible function is used to both read from and write to one or more logical sectors. These sectors can be on a physical diskette, the hard disk, or even a RAM disk. The following parameters are passed to it: device: determines the drive which will be accessed. The numbering starts with 0 for drive A and has no upper limit. The RAM disk presented in section 6.1 of this book is addressed as drive C, so it would have device number 2. recnr: specifies the logical number of sector to be processed. Numbering again starts at 0. The maximum number of sectors varies depending on the device: 720 logical sectors fit on a single-sided 80 track diskette in Atari format (double density), of which "only" 702 are available for user data. TOS uses the remaining 18 sectors to manage the user data with the directory and FAT (File Allocation Table). number: the number of logical sectors to be processed. buffer: an address from which or to which the data is to be written. If you want to read 4 logical sectors of the Atari-specific format (512 bytes/sector), there must be 4*512=2048 bytes available at this address. rwflag: determines whether the function will write to or read from the disk. Four possible values are possible: rwflag: Me aning! 0 Read sectors 1 Write sectors 2 Forced read sectors (even if disk changed) 3 Forced write sectors (even if disk changed) 203 Abacus Atari ST Disk Drives Inside and Out A possible call in machine language could look like this: move.w r- fd l o =#= * drive A (device) move.w #11,-(a7) •k recnr start at logical sector 11 move.w #5, -(a7) k number, all 5 directory sectors move.1 #buffer,-(a7) k address of free space move.w #2,-(a7) k rwflag, forced read move.w #4, -(a7) ★ BIOS function number trap #13 * BIOS call add. 1 #14,a7 k restore stack tst. w dO ★ check if error occurred bmi error * negative value means error . . . ★ continue here for no error k the data read is now in . . . ★ RAM at the address "buffer" GETBPB: BIOS function number 7 The BIOS parameter block contains the data about the current disk. These data are found in the boot sector of the diskette and are placed into the BPB (BIOS Parameter Block) in RAM by this function. Assembly language call: move.w device,-(a7) ★ drive 0 = A move.w #7,-(a7) ★ BIOS number trap #13 addq.1 #4,a7 ★ clean up stack tst .w dO bmi error ★ dO negative if error ★ else DO contains the addr of the k Normally this is $4DCE for drive k and $4DEE for drive B At the address returned in DO you will find the data in word (2-byte) quantities: Drive A Address: Name; Meaning: PS $4DCE recsiz sector size in bytes 512 512 $4DD0 clsiz cluster size in sectors 2 2 $4DD2 clsizb cluster size in bytes 1024 1024 $4DD4 rdlen directory length in sectors 7 7 204 Abacus Atari ST Disk Drives Inside and Out Address;, Name: Meaning; SS. ns $4DD6 fsiz FAT size in sectors 5 5 $4DD8 fatrec sector number in the second FAT 6 6 $4DDA datrec sector number of the first data cluster 18 18 $4DDC numcl number of clusters on the disk 351 711 $4DDE bflags various flags $4DE0 unknown $4DE2 nside number of sides on disk 1 2 The data shown apply for the Atari-specific recording format with 80 tracks (SS = single-sided, DS = double-sided). MEDIACH: BIOS function number 9 This function uses the disk name to see if the disk has been changed. The drive number is passed as the parameter. move.w device,-(a7) move.w #9,-(a7) trap #13 addq.1 #4 , a7 * drive number * BIOS function number * call * restore stack The value passed back in DO is between 0 and 2 and has the following meaning: Number: Meaning; 0 Disk was not changed 1 Disk might have been changed 2 Disk was changed Here are the four XBIOS functions which are important for our purposes. FLOPRD: XBIOS function number 8 With this function you can read one or more consecutive sectors on a track. The parameters to be passed are: number: determines how many sectors are to be read. The possible values for the Atari format vary from one to ten. Ten sectors can be read only if a special program is used to format the disk, because the Atari format program writes only nine sectors per track on the disk. 205 Abacus Atari ST Disk Drives Inside and Out side: specifies the side of the disk, 0 or 1. track: determines the track on which the sectors are located, sector: the physical sector itself, device: the drive parameter (0=A). filler: a meaningless longword, probably intended for later expansion, buffer: the address to which the data are to be transferred. move.w #1,-(a 7) k number, one sector move.w #0,-(a7) k side move.w #0, -(a7) * track zero move.w #1, -(a7) * sector one = boot sector move.w #0,-(a7) * drive A move.1 #0,-(a7) ★ filler, dummy long word move.1 ♦buffer,- (a7) * address of the data destination move.w #8,- ASCII move.b dO,mtrack ;* high byte swap dO f add.b #'0',dO move.b dO,mtrack+1 ;* low byte 219 Abacus Atari ST Disk Drives Inside and Out jsr rts dispmen dectrack: move. w wtrack,dO cmp.w #0, dO ble dectrl subq.w #1, dO bra dectr2 dectrl: move.w maxtrack,dO dectr2: move.w dO,wtrack ext. 1 dO divu #10,dO add.b # • 0 • ,d0 move.b dO,mtrack swap dO add.b # ' 0 ' ,d0 move.b dO,mtrack+1 jsr rts dispmen • ********************** incsect: move.w wsector,dO cmp. w maxsect,dO bit incsel move.w #0, dO bra incse2 incsel: addq.w #1, dO incse2: move.w dO,wsector ext. 1 dO divu #10,dO add.b o a o move.b d0,msector swap dO add.b O O move. b d0,msector+l jsr rts dispmen decsect: move. w wsector,dO cmp.w #0, dO ble decsel subq.w #1, dO bra decse2 decsel: move. w maxsect,dO decse2: move. w dO,wsector ext. 1 dO ;* display menu ;* decrement track ;* current track equals zero ;* then current track = maxtrack ;* enter in menu string ;* display and back ********************************** ;* increment current sector ;* see inctrack ;* decrement current sector 220 Abacus Atari ST Disk Drives Inside and Out divu #10,dO add.b #'0',d0 move.b dO,msector swap dO add. b =#= o a o move.b dO,msector+1 jsr dispmen rts *********************************************************************** * Reads the current sector, in wsector, if drbyte = 1024 then 1024 * * bytes will be read, because the operating system calculates the * * bytes to be read from the number of sectors by multiplying the * * number of sectors by 512. If you pass 2 sectors, then 1024 bytes * * will be read, regardless of whether they are organized as one * * sector of 1024 bytes, or 2 of 512 bytes, or 4 of 256 bytes. * *********************************************************************** ;* number of bytes/sector ;* default equals 1 sector ;* if drbyte = 1024, then ;* read one sector of 1024 bytes ;* number of sectors ;* side ;* track ;* sector, or start sector ;* drive ;* dummy long word ;* buffer address ;* floprd readsec: move.w move.w cmp.w bne move. w readweit: move.w move. w move. w move.w move. w clr .1 move.1 move.w trap add. 1 tst .w bmi jsr rts readser: move.w jsr jsr move.1 jsr rts drbyte,dO #1, dl #1024,dO readweit #2, dl dl,-(a7) wside,-(a7) wtrack,-(a7) wsector,-(a7) wdrive,-(a7) - ? ' ,27,'q',0 wrfrag3: dc.b 27, 'p Not written. ' ,27,'q',0 sefragl: dc.b 27, 'p SECTOR MODE ',27,'q',0 edfragl: dc. b 27, 'p EDIT MODE: < return > := END ' ,27, 'q',0 r ;* Addresses for the TRACK menu * • **★★★★****★****★★***★**★*★*★★*★★★★★★**★★★★★*★★★★★★★★★★★**★★*★*•****'★ / trincjmp: dc.l incdrive dc.l incside dc.l inctrack dc.l incstra dc.l readltr dc.l writltr dc.l edittr dc.l gomain trdecjmp: dc.l decdrive dc.l decside dc.l dectrack dc.l decstra dc.l readltr dc.l writltr dc.l edittr dc.l gomain mentrack: dc.l mlsecta dc.l mlsectb dc.l mlsectc dc.l mltracal dc.l mltracka dc.l mltrackb 245 Abacus Atari ST Disk Drives Inside and Out dc.l mltrackc dc.l mltrackd mltracal: : dc.b ' Sec/Trk : ' setrack: dc.b O kD O mltracka: dc.b ’ READ •,0 mltrackb: dc.b ’ WRITE ',0 mltrackc: dc.b ' EDIT Tr. ',0 mltrackd: dc.b ' BACK 1 ,0 trfragl: dc. b 27,'p TRACK MODE ; ',27 ,'q',0 trfrag2: dc.b 21,'p TRACK WITH SYNCS MODE •,27, 'q',0 trfrag3: dc.b 27,'p Sector: 1 , 0 trfrag4: dc.b ' ',27,'q',0 trfrag5: dc.b 27,'p Write this track to ' ,27, 'q',0 trfrag6: dc.b 27,'p < yes/no > ',27, ' q',0 ************************************************************************ t ;* Addresses for the TRACK with SYNCS menu * ************************************************************************ / align syincjmp: dc.l incdrive dc.l incside dc.l inctrack dc.l rdtracks dc.l readadr dc.l gomain sydecjmp: dc.l decdrive dc.l decside dc.l dectrack dc.l rdtracks dc.l readadr dc.l gomain mensync: dc.l mlsecta dc.l mlsectb dc.l mlsectc dc.l mlsynca dc.l mlsyncb dc.l mltrackd mlsynca: dc.b ' READ WITH SYNCS ',0 mlsyncb: dc.b ' ADDR. FIELD ',0 246 Abacus Atari ST Disk Drives Inside and Out •★★★**★**★**★★********★**★*★★★***★*★★★★★★*★★*★****★★**★★*★**** ;* Cluster * clincjmp: dc.l inedrive dc. 1 incclust dc. 1 rdclust dc. 1 nextclst dc. 1 wrclust dc. 1 edclust dc. 1 stclust dc. 1 gomain cldecjmp: : dc.l deedrive dc. 1 decclust dc. 1 rdclust dc. 1 nextclst dc. 1 wrclust dc. 1 edclust dc. 1 stclust dc. 1 gomain menclust: : dc.l mlsecta dc. 1 mlclusa dc.l mlsecte dc. 1 mlclusb dc. 1 mlsectf dc.l mlclusd dc. 1 mlclusc dc. 1 mlsecth mlclusa: dc.b ' CLUST: ' mlclusal : dc.b O o o o o mlclusb: dc.b ' NEXT ',0 mlclusc: dc.b ' STARTofFILE ',0 mlclusd: dc. b ' EDIT ’,0 clfragl: dc.b 27,'p CLUSTER MODE ',27,'q',0 elfrag2: dc.b 27,'p When leaving CLUSTER MODE, last read 1 dc.b 'Cluster is updated in SECTOR Menu ',27,'q',0 elfrag4: dc. b 27,'p This was the last cluster ',27,'q',0 selfragl : dc.b 27, 'p Filename: File attribute: ' dc.b ' Start cluster: Number of bytes: ',21, selfrag2 : dc.b 27,'p Put start cluster in menu with dc.b 1 read with , . ',27,'q',0 elfrag5: dc .b 27,'p Write this cluster to: ',27,'q',0 247 Abacus Atari ST Disk Drives Inside and Out trecsiz: dc.b ' Bytes per sector: ',0 tclsiz: dc.b ' Sectors per cluster: ',0 tclsizb: dc.b ' Bytes per cluster: ',0 trdlen: dc.b 1 Sectors per directory: ',0 tfsiz: dc.b ' Sector per FAT: ',0 tfatrec: dc.b 1 Sector number of second FAT:',0 tdatrec: dc.b ' Sector of first data cluster:',0 tnumcl: dc.b 1 Number of clusters: ',0 tnumsides: dc.b 1 Number of sides: ',0 tdirl: dc.b 27,'p First directory sector on Side: 0 Track: 1 dc.b ' Sector: 3 ',27,'q',0 tdir2: dc.b 27,'p First directory sector on Side: 1 Track: 0 dc.b 1 Sector: 3 ',21, 'q tfolder: dc.b ' Subdirectory ,0 treadwr: dc.b 1 Read/Write ,o treadon: dc.b 1 Read only ,0 thidden: dc. b ' HIDDEN File ,0 tdelet: dc.b ' Deleted ,0 tdisname dc.b ' Diskette name ,0 ;* Format menu align foincjmp: dc. 1 incdrive dc. 1 incside dc.l inctrack dc. 1 incstra dc. 1 formatl dc. 1 xformat dc. 1 gogaps dc. 1 gomain fodecjmp : dc.l decdrive dc. 1 decside dc. 1 dectrack dc. 1 decstra dc. 1 formatl dc.l xformat dc. 1 gogaps dc. 1 gomain formmen: dc.l mlsecta dc.l mlsectb dc. 1 mlsectc dc. 1 mltracal 248 Abacus Atari ST Disk Drives Inside and Out dc. 1 mlformd dc. 1 mlforme dc. 1 mlformf dc. 1 mlformg mlformd: dc. b ' FORMAT ' , 0 mlforme: dc. b ' XFORMAT ',0 mlformf: dc.b ' GAPS ',0 mlformg: dc .b ' BACK ',0 fofragl: dc.b 27,'p Format track mode ',27,'q',0 fofrag2: dc. b 27, 'p Track : ',0 fofrag3: dc. b ' format ? ',27,'q l ,0 fofrag4: dc.b 27, 'p Not formatted ',27,"q',0 fofrag5: dc. b ' on side : 1 ,0 fofrag6: dc.b ' of drive: 1 ,0 xffragl: dc. b 27,'p Really format with new GAPs ' dc. b 'between the sectors? ',27,'q',0 xffrag2: dc. b 27,'p Wait a second, then press key ',27, , q',0 M1F0RM1: DC. b 1 Format track ',0 • ★**★★*★*★★**********★*****★★*★***★******★***★★★**■*•★★★★★★*★★★**★ ;* Init menu • ★★★★★★★★★★*★**★*★★★★★★*★**★■*•***★*★*★★***★**★★***★***★*★★★*★**** inincjmp : dc. 1 incdrive dc. 1 incmaxtr dc. 1 incmaxse dc. 1 dodrivin dc. 1 showbpb dc. 1 gomain indecjmp : dc. 1 decdrive dc. 1 decmaxtr dc. 1 decmaxse dc. 1 dodrivin dc. 1 showbpb dc.l gomain meninit: dc. 1 mlsecta dc.l mldrina dc. 1 mldrinb dc. 1 mldrinc dc. 1 mldrincl dc. 1 mldrind 249 Abacus Atari ST Disk Drives Inside and Out mldrina: dc. b ' MAXTRACK: ' maxltr: dc.b ' 7 ' , ' 9', 1 ',0 mldrinb: dc. b ' MAXSECTOR: '• maxlse: dc. b •O’, ' 9•, ■ ',0 mldrinc: dc. b ' INIT DRIVE ',0 mldrincl dc.b • SHOW BPB ',0 mldrind: dc.b ' BACK ',0 drifragl dc.b 27, 'p INIT DRIVE MENU ',27, 'q',0 drifrag2 dc.b 27,'p Bios Parameter Block of active drive ' dc. b ' < press key > ',27,'q',0 catfral: dc. b 27,'p Directory starts at Side: 0 Track: 1 Sector dc.b 27, 1 q 1 ,0 catfra2: 1 dc.b 27,'p Directory starts at Side: 1 Track: 0 Sector dc. b 27,'q',0 device: dc .w 2 drive: dc .w 0 side: dc.w 0 track: dc .w 0 sektor: dc.w 0 seek : dc.w 3 savesr: dc. w 0 flstatus dc.w 0 • ★*★★★★★*★*★★■*■**★***★★★★****★★★★★★★*★★* ;* Gap menu ★ .***************************************************************** gpinc jmp dc.l incgapl dc. 1 incgap2 dc. 1 incgap3 dc. 1 incgap4 dc. 1 incgap5 dc. 1 incbyte dc. 1 goformat gpdecjmp : dc.l decgapl dc. 1 decgap2 dc. 1 decgap3 dc. 1 decgap4 dc. 1 decgap5 dc.l decbyte dc. 1 goformat 250 Abacus Atari ST Disk Drives Inside and Out mengap: dc. 1 mlgapa dc.l mlgapb dc.l mlgapc dc.l mlgapd dc. 1 mlgape dc. 1 mlgapf dc. 1 mlgapg mlgapa: dc. b ' GAP1 • 1 mgapl: dc .b '60 ' , 0 mlgapb: dc .b ' GAP 2 • 1 mgap2: dc. b ■12 ■ , 0 mlgapc: dc. b ' GAP 3 • 1 mgap3: dc .b ■22 ' , 0 mlgapd: dc .b * GAP 4 • 1 mgap4: dc. b ■40 ' , 0 ml gape: dc.b ' GAP 5 • 1 mgap5: dc.b •664 ' ,0 mlgapf: dc.b ' Bytes/ec: ' mdrisect : dc.b '0512 ', o mlgapg: dc.b ' BACK ', 0 drfragl: dc.b 27, 'p Drive format mode , ,27, , q',0 gpfragl: dc.b 27, 'p Change gaps between sectors , ,27,'q',0 slfragl: dc.b 27, 'p Please wait a second, then press a key ',21, 'q' ,0 slfrag3: dc. b 27, -p SECTOR MODE ',27,'q',0 dr byte: dc. w 512 gapl: dc .w 60 gap2 : dc .w 12 gap3: dc .w 22 gap 4: dc. w 40 gap5: dc. w 664 sadfragl : dc.b 27, 'p Track: Side: Sector: Bytes: Checksum(hex) 1 dc.b 27,'q' ,0 .*********************************************************************** ;* Here are the escape sequences for the terminal emulation, such as * ;* reverse on and off. cursor positioning, etc. * ; *********************************************************************** clrestl: dc. b 27,'J' , o clrest2: dc. b 27,'K' ,0 reversl: dc.b 27,'p' ,0 revers2: dc .b 27,'q' ,0 251 Abacus Atari ST Disk Drives Inside and Out loccursl dc.b 27, Y ,33,33,0 homel: dc.b 27, ■H' ,0 clearl: dc.b 27, ■E' ,0 curupl: dc.b 27, 'A' ,0 curdownl dc.b 27, 'B' ,0 inslinel dc.b 27, ■L' ,0 dellinel dc.b 27, •1' ,0 overoutl dc.b 27, ' w ,0 curoutl: dc. b 27, ■ f ,0 curonl: dc. b 27, 'e ,0 spaces: dc.b 1 ',0 hilcurs: dc. b 27, ' J ,0 • ★★★★★*★★★*★*★★*★*■**■*★******★★★★★*****★***★****★***★★*★*★★*'*■**★★** ;* Addresses of error strings * • **★**★**★★★*★★★★★★***★★*****★*'*•★★★*★*★**•**★*★★★**★****★★★**★*** * align errtab: dc. 1 errorl dc. 1 error2 dc. 1 error3 dc. 1 error4 dc. 1 error5 dc. 1 error6 dc. 1 error7 dc. 1 error8 dc.l error9 dc. 1 errorlO dc. 1 errorll dc. 1 errorl2 dc. 1 errorl3 dc. 1 errorl4 dc.l errorl5 dc. 1 errorl6 dc. 1 errorl7 dc.l error18 dc. 1 errorl9 dc. 1 error20 dc.l error21 dc.l error22 dc. 1 error23 dc. 1 error24 dc. 1 error25 dc. 1 error2 6 dc. 1 error27 dc. 1 error28 dc. 1 error29 252 Abacus Atari ST Disk Drives Inside and Out ;* Here are the actual error strings * error1: dc. b 27,'p',' NO BOOTSECTOR ',27,'q',0 error2: dc. b 27,'p Directory sector defective error3: dc. b ' error3',0 error4 : dc. b ' error4',0 error5: dc.b ' error5 ',0 error6: dc .b ' error6 ',0 error7: dc. b 27,'p',' Insert disk / track not present error8: dc. b ' error8 ',0 error9: dc. b 27,'p',' Sector does not exist!',27,'q', errorlO dc. b ' errorlO',0 errorll dc.b ' errorll',0 errorl2 dc. b ' errorl2',0 errorl3 dc. b ' errorl3 ',0 errorl4 dc. b 27,'p Please remove write protect. ',2 error15 dc. b ' errorl5 ',0 errorl6 dc. b ' errorl6 ',0 errorl7 dc.b ' errorl7',0 errorl8 dc. b ' errorl8 ',0 error19 dc. b ' errorl9 ',0 error2 0 dc.b 27,'p No more clusters ',27,'q',0 error21 dc.b ' error21 ',0 error22 dc.b ' error22 ',0 error23 dc.b ' error23 ',0 error24 dc.b ' error24 ',0 error25 dc.b ' error25 ',0 error26 dc. b ' error26 ',0 error27 dc.b ' error27 ',0 error28 dc. b ' error28 ',0 error29 dc. b ' error29 ',0 pattern dc. w bss $ffff menuadr ds.l 1 ganz: ds . 1 1 revnum: ds . 1 1 jmptable: ds.l 1 wtrack: ds . w 1 wsector ds . w 1 wside: ds . w 1 wdrive: ds . w 1 253 Abacus Atari ST Disk Drives Inside and Out wclust: ds . w 1 maxtrack: : ds .w 1 maxsect: ds . w 1 maxdriv: ds . w 1 maxside: ds. w 1 maxclust : ds .w 1 topptr: ds . 1 1 oldtop: ds . 1 1 botptr: ds . 1 1 column: ds. w 1 line: ds . w 1 headl: ds. w 1 head2: ds . w 1 curzeil: ds . w 1 curspal: ds . w 1 oldzeil: ds. w 1 oldspal: ds . w 1 lincount : ds .w 1 prcount: ds . w 1 retwl: ds . w 1 incvar: ds . 1 1 decvar: ds . 1 1 usstack: ds . 1 1 sustack: ds . 1 1 dmastat: ds. w 1 currdma: ds .b 1 highdma: ds .b 1 middma: ds .b 1 lowdma: ds .b 1 maxhead: ds . w 1 savebpb: ds . 1 1 recsiz: ds . w 1 clsiz: ds. w 1 clsizb: ds . w 1 rdlen : ds . w 1 f siz: ds. w 1 fatrec: ds . w 1 254 Abacus Atari ST Disk Drives Inside and Out datrec: ds . w 1 numcl: ds . w 1 bflags: ds . w 1 oldsec: ds. w 1 dflag: ds . w 1 eflag: ds . w 1 edflag: ds . w 1 numsides : ds .w 1 tabl: ds . w 1 oldclst: ds. w 1 newclst: ds . w 1 clstnum: ds. w 1 logsect: ds . w 1 asector: ds. w 1 topdma: ds. 1 1 editptr: ds. 1 1 savereg: ds.l 16 maxdown: ds . w 1 maxup: ds . w 1 lineavar : ds.l 1 varll: ds. 1 1 varwl: ds . w 1 varw2: ds . w 1 varw3: ds . w 1 dirptr: ds.l 1 dirbuf: ds . w 4000 fatbuf: ds . w 4000 formbuf: ds. w 6000 spacetr: ds. w 6000 end Next are the complete subroutines for the individual menu options, which end in rts in the above listing of edit . s. 255 Abacus Atari ST Disk Drives Inside and Out You should stick to the suggested order of implementation because some menu options access subroutines from other menus. If you complete the program in the manner suggested, you will not run into problems of this type. First is the subroutine for the Options menu, which allows you to set the maximum track and sector and view the BIOS parameter block. ; ;* OPTION.S subroutines should be implemented first because they allow* ;* access to the 10th sector, 82nd track, etc, and some routines are * ;* called by other parts of the program. * •★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★it*** t • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★■A - ****** r ;* initialize current drive (from the menu) and store the variables * ;* of the BIOS parameter block. * r initdriv: move.w wdrive,d0 move.w wdrive,-(a7) move.w #7,-(a7) trap #13 addq.l #4,a7 tst.l dO bne doinitl move.w dO,-(a7) jsr errhand bra doiniten doinitl: move.l d0,a0 move.w (a0)+,recsiz move.w (a0)+,clsiz move.w (a0)+,clsizb move.w (a0)+,rdlen move.w (a0)+,fsiz move.w (aO)+,fatrec move.w (a0)+,datrec move.w (a0)+,numcl move.w (aO)+,bflags move.w (aO)+,numsides move.w (aO)+,numsides doiniten: rts * current drive * on the stack * Getbpb function * BIOS trap * restore stack * error occurred? * if so than pass it * and return * else dO = base address of the BPB * bytes per sector * sectors/cluster * bytes/cluster * sectors/directory * sectors/FAT * sec. # of second FAT * sec. # of first data cluster * number of data clusters * flags * still dummy * number of sides * and return .*********************************************************************** ;* Read the FAT sectors from the disk into the FAT buffer * .****★★★*★**★*★★★★*★★★★★★★★★★★*★*****★**★★********★★★***********★★★★★*★* r 256 Abacus Atari ST Disk Drives Inside and Out rdfat: move.w wdrive,-(a7) • ★ t move.w fatrec,-(a7) • ★ r move.w fsiz,- (a7) • ★ r move.1 #fatbuf,-(a7) • ★ / move.w #2,-(a7) • ★ t move.w #4,-. * * * jsr cursmess ;* position cursor jsr delline ;* delete line move. 1 #hilcurs,aO ;* output message jsr printf move.w wdrive,dO cmp .w #2, dO bgt rdaderr 275 Abacus Atari ST Disk Drives Inside and Out jsr super • ★ r jsr seldrive . * jsr flreset . * r jsr searcht • ★ r jsr searcht • ★ jsr setspace • * t jsr rdadr • ★ t jsr flreset • ★ jsr user • ★ t jsr showadr . * r jsr super • •k r jsr deselect • ★ r jsr user • ★ r rts • ★ r / ;* Read 25 address fields from the • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★■A* supervisor on select current drive FDC reset seek current track twice so the drive is ready set DMA transfer address read address fields FDC reset user mode on display address fields supervisor on deselect current drive user mode on and return ★★★★*★★★**★★★★★★★★★★★★★★★★★★★★★★★★ disk * rdadr: jsr super move.w #$90,dmamode move.w #$190,dmamode move.w #$90,dmamode move.w #1, d6 jsr wrcontr move.w #$80,dmamode move.w #24,d4 rdadr1: move.w #$c8,d6 move.1 #$40000,d7 jsr wrcontr rdadr2: btst #5,mfp beq rdadrenl subq.1 #1, d7 beq rdaderr bra rdadr2 rdadrenl: dbra d4,rdadr1 rts rdaderr: move.w #-6,-(a7) jsr errhand rts * enable supervisor mode * toggle the read/write line * clear the DMA status, reset DMA * switch to read and sector count * register, read 1 sector * to FDC controller * switch to FDC register * read 24+1 address fields * read-address command * timeout counter * command to FDC * command already processed? * yes * else decrement timeout * timeout?, then error * else keep waiting * repeat 25 times * and return ;* error message ;* output, and terminate •★★★**★★★★***★*★★**★**★★★★**★★*★**★★*★**★★*★★*★★**★******★*★★★★★★*★★★★★* r ;* Display the address fields read. More address fields are read than * ;* are displayed because the DMA controller transfers the bytes in * ;* groups of 16, whereas an address field contains only 6 bytes. * r 276 Abacus Atari ST Disk Drives Inside and Out showadr: showadrl showadr2 showadr7 jsr cursmess ★ position cursor and delete and jsr delline ★ output message move. 1 #sadfragl,aO jsr printf jsr cursbuf ★ position cursor move.w #17,d5 ★ display 18 address fields move.1 #spacetr,a3 ★ buffer address of the address fields move.w #2,d4 * output 3 data (track, side. move. w #$20,-.* * If the filename in reverse is a subdirectory, this must first be * * entered. * *********************************************************************** jsr initdriv • ★ t initialize drive jsr rdf at • ★ / read FAT and directoi jsr rddir • ★ t into the buffer move.w #0,column move.w #2,line jsr loccurs . * / position cursor move. 1 #sclfragl,aO jsr printf move.w #17,lincount • ★ / display 18 lines jsr delrest • ★ t delete rest of line move.1 #dirbuf,a3 • * t store address of move.1 a3, a4 . ★ t directory buffer move.1 a3,topptr • -k r use as pointer move. 1 a3,oldtop • ★ r store cursor jsr cursbuf • ★ / again jsr showdir . * t display 18 lines jsr cursbuf . * r cursor to start move.1 #dirbu f,t oppt r • * r start of directory bi jsr revon • * r turn on reverse jsr dirline • ★ r first filename (disk jsr revout • ★ r write in reverse, th jsr key • ★ r and read keyboard swap dO cmp .b #$lc,dO • ★ / Return key pressed? beq dirclsel • ★ r yes cmp.b #$48,dO • * t cursor up? beq stclup • ★ r yes cmp.b #$50,dO • ★ r cursor down? beq stcldo • ★ f yes cmp.b #$4b,dO • ★ r cursor left? beq stclli • ★ r yes cmp. b #$4d,dO • ★ / cursor right bne stclstl 285 Abacus stclli: stclup: stclup3: stclupen stcldo: Atari ST Disk Drives Inside and Out jsr curright . * f yes, then call bra stclendl jsr curleft bra stclendl move. w line,dO • ★ r current cursor line cmp .w =#= a o • ★ r line 4 equals upper border ble stclup3 • * ! equals 4, then scroll move. w #0,column jsr loccurs jsr dirline • * r else subtract one from subq.w #1,line • ★ r the current line, cursor move. w #0,column . * t to the new line and set jsr loccurs • ★ column to zero jsr revon jsr dirline • ★ r and display this line in reverse jsr revout • ★ turn reverse off bra stclupen • ★ r and return cmp. 1 tdirbuf,topptr • ★ t top line in buffer reached? beq stclupen • ★ / if so, then don't scroll move.1 topptr,dO . ★ / else decrement ptr by number of lines move.w lincount,dO • ★ t times number of characters per line addq.w a O . * r changed 8/18/86 muls #32,dO sub.l dO,topptr • ★ decrement pointer in buffer jsr showdir • * / display 18 lines move.w #21,line move.w #0,column • ★ last displayed line reverse jsr loccurs • ★ r cursor pos. jsr revon • ★ r reverse on jsr dirline • * t display line jsr revout • * ! reverse off again : bra stclstl • ★ t to loop move.w line,dO . * r current line greater than 20 cmp .w #20,dO bgt stcldo3 • ic r yes move. w line,dO • ★ t if not, then add 1 addq.w #1 , dO • * f sub.w #4, dO • * f offset to upper screen border ext. 1 dO lsl.l #5, dO • ★ r multiply by 32 move. 1 topptr,a6 . * t pointer in directory buffer move.b 0(a6,dO.1),dO • ie t get first byte of this entry beq stcldoen • ★ r if byte=0, then empty entry move.w #0,column . * r else position cursor and 286 Abacus Atari ST Disk Drives Inside and Out jsr loccurs jsr dirline • * r addq.w #1,line • * r move.w #0,column jsr loccurs jsr revon • ★ t jsr dirline • ★ / jsr revout • * r jsr loccurs stcldol: bra stcldoen • * r stcldo3: move.w line,dO • ★ t addq.w #1, dO • * r sub .w #4, dO • ★ r ext. 1 dO lsl.l #5, dO • k r move.1 topptr,a6 • ★ t move.b 0(a6, dO.1),dO • * f beq stcldoen • ★ ! move.w lincount,dO • ★ t addq.w #1, dO • ★ f muls #32,dO • ★ t add.l topptr,dO • ★ r move.1 dO,topptr jsr cursbuf • * f jsr showdir • ★ r jsr cursbuf • ★ t jsr revon jsr dirline • ★ t jsr revout stcldoen : bra stclstl • * r stclendl : jsr cursmess • ★ / jsr delline jsr cursmess move.1 #clfragl,aO • * r jsr printf movem.1 (a7)+,a3-a5/d3- -d7 rts • -k t display old line normal increment line counter and display new line in reverse turn reverse off again to loop get first byte of the next directory entry, add one and subtract offset to top times 32 (# of bytes/directory entry) pointer to start of dir buffer is the next entry zero? then back to loop number of lines to display plus one times 32 equals offset from start of buf add offset to topptr pos. cursor display directory first entry in reverse display back to loop delete message line display mode ;* restore registers and return .★****★★★★**★★**★★★★***********★*★★★★*★***★★*★**★*★*★★★**★★★★**★***★*★** ;* reacts to pressing of Return key, accepts it or displays a * ;* subdirectory. * . ★*************★*★******★************************■*********************.*.* dirclsel: move.l topptr,aO ;* ptr to current start of buffer line,dO move.w 287 Abacus Atari ST Disk Drives Inside and Out dirsell: dirsel2: subdir: number subdir1: sub.w #4, dO • ★ t ext. 1 dO lsl.l #5,dO • * times 32 move.b 11(aO,dO.1), dl • ★ r get file type byte cmp.b #$10,dl • ★ r is it a subdirectory? beq subdir • ★ t yes move. w clstnum,dO • ★ r else current cluster number subq.w #1, dO • ★ r subtract 1 for incclust move. w dO,wclust move.1 #3,revnum • ★ f 3rd menu option in reverse jsr incclust • ★ f increment cluster number and display bra stclendl • ★ / menu, then back to loop jsr rddir • ★ t read directory sectors bra subdiren tst .w clstnum • ★ / if cluster number is zero then just beq dirsel2 « * reread directory sectors move.w clstnum,dO • * f else read at the initial cluster move.1 #dirbuf,dirptr clr .w d3 move.w clstnum,dO move.w wdrive,-(a7) • ★ / current drive subq.w #2, dO • * / convert cluster number to muls clsiz,dO • ★ f logical sector number add.w datrec,dO move.w dO,-