Disc Formatting Code ==================== By Dave Edwards =============== My original intention was to keep this code relatively private, as I am using it in a number of applications. However, Mark has requested information about how to format discs within user applications, with the possibility of append- ing such a feature to the ACC Menu system. Also, the actual meaty bits of the format code don't reveal anything that the user couldn't find out simply by reading the relevant sections of the Amiga Systems Programmer's Guide, and I decided therefore to make everyone's life easier by providing an edited ver- sion of the format code. It uses various functions ripped off from exec_support.i, which is one of the include files supplied on the DOC DISCS. Like all include files of that origin, exec_support.i is Public Domain, and freely copiable/editable by users as desired. In particular, it uses functions to create a message port, and functions to create an extended IORequest structure for the trackdisk de- vice. The format source code is located in the file: Format_Code.i and in that form is Public Domain. Various private versions exist, and I wish to state that this version AND ONLY THIS VERSION is Public Domain for the mo- ment. I may, of course, release enhanced versions in future into the Public Domain, but this version is the ONLY PD version for now. Before going on, I need to define some terms to be used in the fol- lowing text. These terms are: Boot Block : Exists on the disc at Track 0, Side 0, Sector 0 and Track 0, Side 1, Sector 0. Contains in- formation usable by the Amiga upon power-up/reset. Actually consists of TWO sectors of the disc. Root Block : Normally exists on the disc at Track 40, Side 0, Sector 0. This sector of data is the sector that is examined whenever the Root dir- ectory of the disc is required by the system. Bitmap Block : Can exist on the disc almost any- where other than those sectors used above, because of the way Amiga discs are formatted. Contains a 'bit map' describing which sectors of the disc are free, and which are in use. Since the Boot Block contains a pointer to the Root Block, and the Root Block contains a pointer to the Bitmap Block (or blocks, if more than one is needed by the disc) and pointers to all of the blocks needed by files and director- ies, the Amiga is able to obtain ALL of the information it needs courtesy of the Boot Block. Similarly, if the Root Block is located where stated above, which is the standard location for AmigaDOS, information about the remainder of the disc can be accessed with ease once this block is obtained. Ok, preamble/waffle over, now to how it works. First, it obtains a message port from CreatePort(), then an extended IORequest structure from the CreateExtIO() function. Then, it couples the port to the IORequest. For ease of debugging, both the message port and the IORequest have names attached to them. Having got the message port and the IORequqest, the code then waits for a new disc to be inserted. It uses the trackdisk device commands first to check if a disc is in the drive, and then to wait until that disc has been changed. Then it turns on the drive motor (quite trivial). Now the hard part. A block of memory, sufficient for an entire track of the disc, is allocated and prepared for use. Basically, it is filled with long words of the form: $444F53xx the uppermost three bytes of the long word being the three letters "DOS". For a normal disc, the last byte should be zero, but I investigated how the CLI FORMAT command formatted a disc, and on my 1.3 A500, FORMAT writes a stream of long words as follows: $444F5300 $444F5301 $444F5302 $444F5303 $444F5304 $444F5305 ... to each track, only the FIRST long word of each sector being $444F5300. Since the CLI FORMAT command works, I thought logically enough that it would be a good idea to copy its modus operandi and make my format code do the same. So, an entire track is filled with long words of this form. Then the contents of the track are written out to the disc, and scribbled onto every track from Track 0 to Track 79. Unfortunately, this is NOT enough. The boot block needs to be con- structed porperly, and the root block, AND the disc bit map block! Needless to say, this is pretty heavy work. First, the boot block (since that is the easiest). In this table, 'Offset' is the number of bytes from the start of the block (Track 0, Side 0, Sector 0): Offset Value ------ ----- $0000 $444F5300 $0004 See below $0008 $00000370 $000C zero ... ... Offset $0004 within the boot block contains the checksum, which is a number such that when all the contents of the boot block are summed (including the checksum), the result is $FFFFFFFF (-1). Now strangely enough, my WorkBench disc has a checksum resulting in $FFFFFFFC (-4) but still boots (perhaps a WorkBench disc is handled separately by the system). Anyway, my code creates a checksum resulting in $FFFFFFFF, and the discs have worked so far, so it is advised not to tamper with this. Now the root block. This is a big one. The root block is block 880 (or block $00000370), and its data is constructed according to the following table: Offset Value ------ ----- $0000 $00000002 ;denotes root block $0004 zero ;no meaning here $0008 zero ;no meaning here $000C $00000048 ;size of hash table $0010 zero ;no meaning here $0014 ???? ;checksum $0018 zero ;start of hash table ... .... $0138 zero ;BM Flag (see below) $013C $00000371 ;bitmap pointer $0140 zero ... .... $01A4 datestamp $01A8 minutes $01AC seconds $01B0 Disc name ... .... $01E4 creation date $01E8 creation minutes $01EC creation seconds $01F0 zero ;next hash key $01F4 zero ;ptr to parent dir $01F8 zero ;extension $01FC $00000001 ;secondary type To begin with, the root block data area is filled with zeros, then the var- ious special information items are inserted into the block. First, the root block identifier (offset $0000), known as T.SHORT, signalling that this is the root block. Then, the hash table size at offset $000C (the hash table, located within offsets $0018-$0134) contains pointers to the first block of each directory entry, and the position of each entry within this region is determined by a hashing algorithm (hence the term hash table). This area is filled with zeros to start with. Then, the next block (block $0000371 or block 881) is designated as the home of the disc bit map (structure described later!). The long word at offset $0138 is zeroed out (signals that disc bit map is invalid), and offset $013C is filled with the block number of the disc bit map (i.e., block 881). Which block is chosen is actually quite arbitrary, buf if the block follows the root block, then access to the disc bit map is faster since it will be in close proximity to the root block (and the disc read-write head will not need to move to locate it). The zeroing out of the BitMap Valid flag (offset $0138 from the start of the sector) informs AmigaDOS that the Disk-Validator should be called up to ensure that the disc is fine (and then, if an error HAS been placed on the disc, the Disk-Validator will undo the error if possible). The datestamp and timestamp entries can be left empty (zeroed), but a disc name should be supplied. The disc name is up to the programmer, but it MUST be a BCPL string (the first byte is the size of the string in bytes, and a BCPL string does not need to be null terminated). For example, if the disc name chosen if "FRED", then the string written to the disc at offset $01B0 is given by: $04 $46 $52 $45 $44 size 'F' 'R' 'E' 'D' The remaining bytes of the space reserved for the disc name should be set to zero. Again, the creation date and time can be left zero, or alternatively both the creation date/time stamps and the modification date/time stamps can set set to reflect the true date of creation. My code leaves both entries set to zero, which corresponds to 12:00 am on Jan 1 1980. Finally, there are four entries at offsets $01F0-$01FC. These are, in order: $01F0 : Next hash key. For the root directory this is always zero. $01F4 : Pointer to the block on the disc con- taining the parent directory. Since this is the root directory, this poin- ter should always be zero. $01F8 : Extension. Always set to zero. $01FC : Secondary block type. This value is set to one for the root block. The code fills in the appropriate values for these entries, and then the code writes the root block to the disc. Actually, the code also sets up the bitmap block (which follows the root block in this code), whose structure is defined by: Offset Value ------ ----- $0000 Checksum. See below. $0004 These long words contain the allo- cation bits for all disc blocks. ... Bit 0 of the first of these longwords represents Block 2, Bit 1 represents $00DC Block 3 etc. A set bit indicates a free block. For the bit map block, the checksum is obtained by adding all of the block's long words together, and then negating the result (the entry for the actual checksum should be zero BEFORE computing this sum). This resulting value is negated and then stored in the first long word of the block. These two blocks are then written out to the disc, and then the boot block is constructed and written out. Once that the disc has thus been formatted, the code falls through to a section that I have left blank in this version of the code, except for the comment about providing one's own code. At this point, the programmer should write a suitable piece of code to inform the user that formatting is comple- ted. This can be either a simple call to the DOS library Write() function to write a message to the CLI, or a sophisticated routine using Intuition to let the user know via flashy graphics. This is up to you, the programmer, and you should feel free to fit any desired code here to perform this job. Finally, the code cleans up after itself, deallocating any resources allocated upon entry to the code, and closing the trackdisk device. After the cleanup, the disc error code variable should be read, and should this be non- zero, an error message should be displayed (again, the code to do this is en- tirely the programmer's own choice). Please note : ALL of my code thus far has used variables defined us- ing GenAm RS directives, and all programs using my code allocates a suitable block of memory for those variables, and references this block using register A6. Also, various macros are used to call EXEC, DOS and other functions that save A6 on the stack prior to loading the library base into A6 for the call. These macros are: CALLEXEC MACRO \1 move.l a6,-(sp) move.l 4.w,a6 jsr \1(a6) move.l (sp)+,a6 ENDM CALLDOS MACRO \1 move.l a6,-(sp) move.l dos_base(a6),a6 jsr \1(a6) move.l (sp)+,a6 ENDM For CALLDOS, the variable dos_base(a6) needs to be defined and this variable loaded using a call to OpenLibrary() BEFORE using CALLDOS. This variable is best defined within the same block as the other variables. The typical scheme I use is: rsreset dos_base rs.l 1 ;library bases first ... ;other variables follow vars_sizeof rs.w 0 ;defines size of variable block main move.l #vars_sizeof,d0 move.l #MEMF_PUBLIC+MEMF_CLEAR,d1 CALLEXEC AllocMem tst.l d0 beq cock_up_1 move.l d0,a6 ... ;rest of program follows cock_up_1 moveq #0,d0 rts Use a scheme like this in your own programs and the format code will fit in neatly within your own programs. If you INSIST on using absolute variables in your programs, however (why? The above uses less memory and executes 4 cycles per word access quicker!) then you'll have to reserve some memory via another means and link that to register A6 prior to calling the DoFormat() routine in the format code file. OK, this should cover the code and its interfacing to other programs. As always, fellow ACC members: Live fast, code hard & die in a beautiful way Dave Edwards.