THE OSI® GAZETTE
A Small Operating System: OS65D, The Disk Routines
T. R. Berger
Coon Rapids, MN
Editor's Note: Part I appeared last month. Here, the author presents a map of the disk routines.—RTM
Let's turn to track zero. Exactly one ms. after the index hole a two byte address is recorded on the disk in high byte-low order. This address is read by the ROM on boot. It is the start address for loading track zero into memory. Next comes the number of pages in track zero. Finally, that many pages of data are written on the track. There are no track start or stop markings. After track zero is loaded, the computer always jumps to $2200. Hopefully, track zero has been loaded in that vicinity. It would appear that OSI did not think the track zero format over very carefully.
Most of the disk routines are self-explanatory. Because these routines are far more involved than those in the kernel, many more flow charts are needed. Let's run through the memory map in order, commenting on special properties of certain subroutines.
The timing routines at $2678, $267A, and $26A2 are independent of the system clock. The wait time in the routines at $2700, $289F, and $28A4 should be divided by T if the system clock is T MHZ.
OS65D does not use binary track numbers, but BASIC does. Thus BASIC uses $26A6, but OS65D enters this routine at $26BC with the BCD track number in the accumulator. With a binary track number in the accumulator, this routine may be entered at $26A9. It will move the disk head over the correct track after some error checking.
The sequence beginning at $2728 may be viewed as the standard startup to read or write a track or sector. It puts the head on the disk, finds the index hole, then initializes the disk data ACIA.
The EXAMINE command uses $2739 to load the entire contents of a track into memory without regard to error checking, track formatting, or sectoring. This type of command is only possible with the asynchronous data format used by OSI. If you crash a track, this command can prove invaluable in retrieving what may remain. I view this routine as a utility. It should reside on the disk and not in memory, unless needed. The initialize routine at $2768 used on a full disk falls in the same category. Such programs as these should be transient, i.e. only called when needed.
The major "Save a Sector" routine begins at $27D7. It uses the data in $265E-$2661. Most of OS65D's disk data is stored in page zero. Because Zpage is swapped out when BASIC comes in, the most important data is repeated in $265C-$2662. BASIC passes its values to these latter locations. LOAD and SAVE routines must then move this data to Zpage. Since OS65D can put information directly into Zpage, it puts the save vector into $FE, $FF directly, entering the Save routine at $27E1. Except when SAVE or CALL are used, all saving is done in Sector one for 12 ($OC) pages on 8" floppies and for eight pages on minifloppies. After a write, the sector is reread and compared with memory. If the comparison fails, the sector is reread again. This may occur up to four times. If comparison still fails, another attempt is made to write the sector. If comparison fails after four rereads again, the operation is aborted with Error #2. To my recollection, I've never seen Error #2 occur. It might happen on an old worn disk, on a midnight special, or with a very dirty head.
The major "Read a Sector" routine is $295D. It uses data in $265-E-$2662. Again OS65D may enter this routine at $2967 if the load vector at $FE, $FF has been set. This program tries to read a sector seven times. The only error check (other than sector seek errors which abort immediately) is a parity check for each byte. If, after seven tries, a read still fails, then the head is moved down then up one track. This whole process may be repeated up to four times before Error #1 is reported. This error also seems to be very rare.
Both read and save routines use the sector seeking routine at $28C4 which, in turn, calls $2998. Further, they both use a dual purpose routine at $2905. If the accumulator is zero on entry, this routine reads to memory. If it is nonzero, then the routine compares with memory. The actual read and compare loops within this routine are separate. With 8&inch; floppies and a 1 MHZ clock, the 6502 is not fast enough to get from one disk byte to the next if the read and compare loops are combined into one. As it stands, the compare loop just barely returns in time for the next comparison. With a 2 MHZ clock there is plenty of time.
I view the sector directory routines at $29F3 and $2A41 as utilities. They do not need to be resident in memory.
Machine language routines may access the disk directly. For example, to write a sector, locations $265E-$2662 should be assigned correct values. The following segment of code will write a sector to the disk.
10 JSR $26A6 ;Move head to track 20 JSR $2754 ;Engage head, find start of track 30 JSR $27D7 ;Write sector 40 JSR $2761 ;Disengage head 50 RTS
If the write address is already in $FE, $FF then $27D7 may be entered at $27E1. In this case, lines 20-40 may be replaced by JSR $2CA7, a kernel routine.
To read a sector, again assign correct values to $265E-$2662 then perform the following.
10 JSR $26A6 ;Move head to track 20 JSR $2754 ;Engage head, find start of track 30 JSR $295D ;Read sector 40 JSR $2761 ;Disengage head 50 RTS
If the read address is already in $FE, $FF then $295D may be entered at $2967. In this case, lines 20-40 may be replaced by the kernel routine:
When we discuss the I/O section of OS65D we will see additional ways to read from and write to the disk.
1. Jefferson Harman, "IBM Compatible Disk Drives", Byte October 1979, p. 100
2. Ira Rampil, "A Floppy Disk Tutorial", Byte December 1977, p. 24
3. Les Solomon, "BASICS of Computer Disk Systems", Popular Electronics November 1980, p. 53
MAP – OS65D DISK HANDLER
|265D||CURRENT BCD TRACK NUMBER|
|265F||PAGE LENGTH OF SECTOR|
|2660||LOW BYTE LOAD/SAVE VECTOR|
|2661||HIGH BYTE LOAD/SAVE VECTOR|
|2662||BINARY TRACK NUMBER|
|E5||LAST TRACK OF FILE BEING HANDLED|
|F6||NUMBER OF RETRIES ON WRITE|
|F7||NUMBER OF HEAD MOVE RETRIES ON READ|
|F8||NUMBER OR READ RETRIES BEFORE HEAD MOVE|
|FA||TARGET TRACK NUMBER ON SEEK|
|FB||SECTOR NUMBER READ ON DISK|
|FC||STACK POINTER (IN $29F3)|
|FD||SECTOR PAGE COUNT (IN $27D7)|
|FE||SYSTEM POINTER. USED AS|
|FF||LOAD AND SAVE VECTOR BY DISK|
Subroutines–OS65D Disk Handler
|2663||Home the Disk. Move the disk head to track 0.|
|2678||Wait 12 ms.|
|267A||Wait X ms.|
|2683||Step up one track toward track 76.|
|268A||Step down one track toward track 0.|
|26A2||Wait 8 ms.|
|26A6||Fetch binary track number from 2662 then:|
|26A9||Convert track number to BCD then:|
|26BC||Check for track 0-76 BCD, check for drive ready, move disk head to track, adjust head current, and if an error occurs, abort and send an error message via 2A4B.|
|2700||Wait 20 Y + 7 microseconds (1 MHZ clock).|
|2708||Adjust head current.|
|271D||Find trailing edge of index hole.|
|2728||Engage head then:|
|272B||Find index hole then:|
|272E||Initialize disk ACIA.|
|2739||Engage head, read from index hole full around to index hole, then quit.|
|2768||Initialize full disk.|
|277D||Initialize one track.|
|27C2||Send a byte to the disk.|
|27CD||Fetch a byte from the disk.|
|27D7||Fetch sector save vectors then:|
|27E1||Save a sector.|
|289F||Wait 800($FA) microseconds.|
|28A4||Wait 100Y microseconds.|
|28B0||Fetch a byte from the disk. Abort with an error message if over the index hole.|
|28C4||Find the end of the sector preceding the one in 265E.|
|2905||Read a sector to or compare a sector with memory.|
|295D||Fetch disk read vector then:|
|2967||Read and reread a sector to memory, quit if successful or the full number of retries are exhausted.|
|2998||Find the end of the present sector.|
|29C6||Select the drive in 265C then:|
|29DA||Check if the drive is ready.|
|29EB||8 drive select data bytes.|
|29F3||Output a sector directory.|
|2A41||Output subroutine for 29F3.|