B. In consideration of this license, customer shall not reproduce copies of Star-K software except to reproduce the number of copies required for use on customer's computer and shall include the copyright notice on all copies of software reproduced in whole or in part.
C. The provisions of this software license (paragraphs A through C) shall also be applicable to third parties purchasing such software from customer.
Some versions of 6809 SK*DOS are available under the name STAR-DOS (tm).
This is revision 1.05 of the manual, last revised on April 11, 1989.
Since a Disk Operating System (DOS) is an extremely powerful program which allows you to access the disk on a most elementary basis, exercising caution and making frequent backups is especially important.
If at all possible, you should make backups of your original SK*DOS disks before proceeding. You may do so on a system running SK*DOS (either 6809 or 68K) or FLEX. It may also be possible to make a backup on other computers if they allow a "mirror image" copy of one disk to another. The SK*DOS system distribution disk is in the following format:
(a) if 5-1/4", it is single-sided, single density, with 35 tracks numbered 0-34, and 10 sectors per track, numbered 1-10, with 256 bytes per sector.
(b) if 8", it is single-sided, single density, with 77 tracks numbered 0- 76, and 15 sectors per track, numbered 1-15, with 256 bytes per sector.
During the entire configuration process, make sure to write-protect your SK*DOS system disk at all times. On a 5-1/4" disk this is done by putting an opaque tape strip over the notch on the side of the disk; on an 8" disk it is done by making sure there is no tape on the write-protect notch at the rear of the disk.
In order to do that properly, it is important to fully understand how disk operating systems work and what they do. The next section will give you a short overview of SK*DOS, but before proceeding further, you should read the SK*DOS User's Manual. (This manual does not repeat any of the information in the User's manual.)
We therefore provide off-the-shelf versions of SK*DOS for
(1) one main kind of monitor - the HUMBUG monitor from Star-K Software Systems Corp., and other monitors which use predominantly the same entry points and conventions, such as the SBUG monitor from Southwest Technical Products Corp., and
(2) two main kinds of disk controllers:
(a) a simple controller for 5-1/4", single side, single density disks. Several companies make controllers of this type, including Southwest Technical Products Corp. (their DC-1 and DC-2 controllers), and Peripheral Technology Associates (their FD-1 controller). We generically call this the "DC-2 type controller."
(b) a somewhat more complex controller, still for 5-1/4" disks, but permitting either single or double density, and one or two sides. Several companies make controllers of this type, including Southwest Technical Products Corp. (their DC-4 controller), and Peripheral Technology Associates. In addition, several single-board computers (such as the PT-69 from Peripheral Technology Associates) also have controllers of this type. We generically call this the "DC-4 type controller".
In addition, some manufacturers (such as AAA Chicago Computer Center, manufacturer of the Elektra equipment) have provided interface software for adapting SK*DOS to their equipment, or have done the adaptation themselves. Two adaptations of SK*DOS have also been made for the Radio Shack Color Computer, one by Star-K, and one by Data-Comp Inc. of Hixson TN.
It is thus entirely possible that the copy of SK*DOS you have obtained is already configured to run on the specific equipment you have. In that case, all you need do is to boot the system as described in the User's Manual. (And you should try to do so ... but make sure that your master SK*DOS disk has a write-protect tape installed!) You may not even need this Configuration Manual.
If, however, you have hardware for which a specific version of SK*DOS does not yet exist, then this manual will provide information to allow you to adapt it to your system. BUT NOTE: Such adaptation requires a good knowledge of machine language programming and disk system theory. It may be better for you to contact the manufacturer of your equipment, and ask them for assistance. It is quite likely that they already have the same interface software developed for use with another DOS, and that they can supply the adaptation to SK*DOS without any effort on their part.
1. A 6809-based computer with a minimum of 8K of RAM, addressed from $C000 through $DFFF. Although SK*DOS will run in this limited amount of memory, some of its programs will require additional memory starting at $0000. 8K seems to be about the absolute minimum, and 16K or more (up to 48K extending up to $C000) would be helpful.
2. A ROM-based monitor, such as HUMBUG, which allows you to enter machine code into that memory, and start and stop programs. The more functions that monitor has, the better; ideally, it should also contain simple character input and output routines which can be used by SK*DOS as well.
3. At least one, and hopefully two or more, disk drives with an appropriate disk controller. Although any kind of a disk drive can be used to run SK*DOS (with the appropriate driver programs), you will need either a 5-1/4" or 8" floppy disk drive for the initial work of reading the SK*DOS disk we supply, and implementing SK*DOS on your system. This drive must be able to read soft-sectored disks with 256-byte sectors.
4. A means of communicating with this computer, such as a CRT terminal or video board / keyboard combination. This device will probably already be able to communicate via your monitor ROM.
5. Finally, another system, preferably 6809-based, which has a text editor and 6809 assembler (or cross-assembler) which you can use. This manual is supplied with a disk containing the source code for the various programs you will need to work on; you will need a system on which to read this disk, edit its files, assemble the output, and then feed the resulting object code to your 6809 system.
In general, though, a SK*DOS configuration procedure consists of the following steps:
1. A sector read routine must be written to allow you to read a single sector from a disk. You will need this routine to allow you to read parts of the supplied SK*DOS system disk. This simple routine will then become part of the 'boot' program.
2. The sector read routine is then also incorporated into a "super-boot" program which will allow you to load SK*DOS into memory from the system disk. This allows you to load SK*DOS into memory, but does not yet give you a runable version.
3. Next, you must write console drivers so that SK*DOS can communicate with you via the keyboard and display.
4. The next step is to write disk drivers so SK*DOS can read and write on disks.
5. Next, you must write a working super-boot program which can be used to load SK*DOS from disk into memory.
6. Next, you must write a FORMAT program so you can format new disks and write on them.
7. Finally comes the time when you can add the frills which make the system really useful.
Although the ultimate aim may be to provide a lot of bells and whistles, the main idea of the above procedure is to start small and simple. Once you have SK*DOS up and running in a simple way, with just single-density and one-sided disks, then you can elaborate and extend it to double density, two sides, or even hard disks. But that time does not come until later.
We will return to the configuration procedure in Section III, after we look at some more specific details about SK*DOS, in Section II.
(1) The Command Processor System or CPS, which is the major interface to the user. When SK*DOS is active, the CPS monitors the keyboard and waits for user commands. At that time, you can load and execute programs from the disk and do certain other functions. In addition, the CPS has a number of subroutines which can be used by other programs to simplify input and output for the terminal.
(2) The File Control System or FCS is the interface for programs running under SK*DOS. The FCS does the actual work of managing the contents of the disk. It has various routines which can be called by user programs for managing the disk contents.
(3) Memory- and disk-resident commands provide additional functions which work in conjunction with the CPS and FCS to provide an easy way of maintaining the disk.
(4) The Basic Input-Output System or BIOS, which is the interface between SK*DOS and the hardware of your system. This manual primarily describes this BIOS and how to write one.
$C000-C07F stack for SK*DOS and user programs
$C080-C0FF line input buffer
$C100-C6FF Disk Command Area used by some disk commands
$C700-C83F FCB for O command
$C840-CCBF User FCB, system FCBs, and system variables
$CCC0-CCCF Printer drivers
$CD00-D36F SK*DOS Console Command processor
$D370-D3FF Console driver portion of BIOS
$D400-DDAF SK*DOS File Control System
$DDB0-DDFF Clock/Calendar portion of BIOS
$DE00-DFBF Disk driver portion of BIOS
In the above, the console driver and disk driver portions of BIOS are essential; the clock/calendar portion is optional, and may not even apply to most systems.
As far as SK*DOS is concerned, the exact number of sides, tracks and sectors is unimportant as long as there are at most 256 tracks (numbered 0 through 255) per drive and 255 sectors (numbered 1 through 255) per track. The exact positioning of those sectors and tracks is controlled by the disk drivers and FORMAT routine, not by SK*DOS itself.
(A short detour at this point, included more for completeness than because of an immediate need. We often differentiate between a 'logical address' and a 'physical address'. The physical address is the actual address where the hardware looks to find a particular item; the logical address is the address where the software thinks that item is located. In most systems the logical and physical addresses are the same, but it is possible to make them different. In that case there has to be some sort of conversion table or map which converts the logical address into a physical address so the desired item can be found quickly.
SK*DOS refers to locations on a disk by their track and sector numbers; this is the SK*DOS logical address. Usually the data will in fact be on that physical track and sector. But it is possible for the disk drivers to convert each logical track and sector number into a (different) physical track and sector number where the data is really located, and go to that track and sector instead. This is often done with hard disks. End of Detour.)
Each sector in turn contains 256 bytes of data. Of these 256 bytes, the first four are used for system information, and the remaining 252 bytes are usable for file data. SK*DOS thus has a limit of about 16 megabytes of data per logical drive, computed as follows:
256 tracks x 255 sectors x 252 bytes/sector = 16,450,560 bytes
Although this is a limit on the size of a logical drive, it does not limit the size of a physical drive, since a larger physical drive could be treated as two or more logical drives. Since SK*DOS allows up to ten logical drives, SK*DOS's maximum on-line storage capacity is about 160 million bytes.
SK*DOS uses a linked-chain disk format. That is, the sectors used in files, as well as sectors which are in the so-called 'free chain' are linked to each other much like the links in a chain. Each sector contains a two-byte pointer which points to the next sector in that chain (unless it is 0, which indicates the end of that chain.) This pointer occupies the first two bytes of every sector. In addition, the sector also has a number, which occupies the third and fourth byte, and which counts the sectors within a file.
Thus the sector format looks like this:
Bytes 0 and 1
pointer to next sector
Bytes 2 and 3
sector counter
Bytes 4 through 255 252 bytes of data
Some sectors have a slightly different format, and may omit the pointer and/or sector counter.
All the tracks on a disk can be used for storing data and program files except for track 0. Track 0 is special in two ways: first, it is always written in single density even if the rest of the disk is double density. Second, the sectors on this track have special uses as follows:
Sector 2 is usually empty in single-density systems, but is often used as an extension of sector 1 if the super-boot is too long to fit into the first sector. This manual gives examples of both one-sector and two-sector super-boot programs later.
/col [1 4]
Bytes 16-26 Disk name (and
extension)
Bytes 29-30 Track and sector
number of first free sector
Bytes 31-32 Track and sector
number of last free sector
Bytes 33-34 Number of free
sectors
Bytes 35-36 Month, day, and
year of disk creation
Byte 37
1 + number of physical tracks on the disk
Byte 38
Number of logical sectors per track
All other unused bytes of the SIS are 00.
Sector 4 is unused.
Note several items: if the disk is double-sided, then the directory will also continue on track 0 of side B, but the entire track 0 will always be in single density. If it is continued on other tracks, however, the directory continuation might be in single or double density. Hence if the system allows double-sided or double-density operation, the super-boot must be capable of reading both sides and both densities to properly boot SK*DOS.
The 24 bytes in each directory entry are used as follows:
Bytes 0-10 File name and extension
Byte 11
File protection status (same as byte 15 of FCB)
Byte 12
Copy protection status (same as byte 16 of FCB)
Bytes 13-14 Track and sector number of
first sector
Bytes 15-16 Track and sector number of
last sector
Bytes 17-18 Number of sectors
Byte 19
Random file indicator (same as byte 23 of FCB)
Byte 20
User-defined or time (same as byte 24 of FCB)
Bytes 21-23 Month, day, and year of file
creation
Some of the entries above are marked 'same as ... FCB'; actually, the 24 bytes of each directory entry match bytes 4 through 27 of the corresponding FCB, and are explained further in the User's Manual. Note also that the track and sector numbers listed in both the directory as well as the SIS are logical track and sector numbers, and may not necessarily be the same as the physical track and sector numbers, although in most instances they will be.
When a disk is initially formatted, the entire directory is filled with
zeroes. Whenever SK*DOS encounters a directory entry which begins with
0, it assumes that the remainder of the directory is still empty and has
never been used, and thus stops reading. When a directory entry is deleted,
however, the first character of the file name is replaced by $FF. When
SK*DOS encounters a directory entry beginning with $FF, it skips that entry
and goes to the next.
Our purpose in suggesting that you write a boot program first is simple - it is a good experience to tackle a simple read program first, and the boot program is about as simple as it can get. The boot program is purposely written to take as little memory space as possible, for two reasons: in many systems it resides in the monitor ROM and there is little room for it, and sometimes it must be typed in from the keyboard every time you want to boot the disk. Brevity is therefore a great virtue in this program, even at the expense of performance. That makes it a great candidate as a practice program. (If your ROM monitor already has such a boot program, and you are sure that it works on your SK*DOS system disk, you may not need to write the boot program yourself. You may then choose to read this chapter, but not actually follow its advice. That decision is up to you.)
Most floppy disk interfaces use the floppy disk controller (also called FDC) IC's made by Western Digital Corporation. These include the 1771, 1791 through 1797, and 2791 through 2797 series of chips, and we will use these as examples in this manual. It is also possible to use other floppy disk controllers, such as those made by Motorola and some of the Japanese companies, but it is very important to make sure that whatever controller you use can read and write disks compatible with the Western Digital IC's. There are two reasons for this - first, you want to be able to read the master SK*DOS system disk, and you want to be compatible with other SK*DOS users.
The first step is to get the spec sheet or manual on whatever floppy disk controller IC you use. These are available from Western Digital, as well as from some of the second source companies such as Standard Microsystems. Carefully read the information presented there.
Since most 6809 systems either use the SS-50 bus, or are designed by engineers who have been trained on SS-50 bus systems, they often have great similarities in philosophy and addressing. This manual will describe that addressing. The disk controller IC uses four addresses from the system address space, and these are usually $E018 through $E01B. These four addresses are used as follows:
$E018 is used for two purposes: (1) When writing into it, this is the FDC command register. Command codes put into this location tell the FDC what to do. (2) When reading it, this is the FDC status register. The computer reads this location to determine what the FDC is doing or whether there are any errors.
$E019 is the track register, which contains the number of the current track.
$E01A is the sector register, and contains the number of the current sector.
$E01B is the data register. It holds the data being read or written to the disk, and also is used to give the FDC a track number when it is told to 'seek', or move the head to a new track.
In addition, there is usually a fifth location which is used to communicate with the rest of the disk controller circuit. This is usually address $E014. The rightmost two bits are used to specify a drive number (0 through 3). Other bits are sometimes used to specify a side, and in some controllers this location can also be read to determine controller status.
In addition, in most disk controllers, reading or writing to any of the above locations also turns on the drive motors; the motors stay on until automatically turned off a specified time after the last disk access.
The procedure to read a disk sector usually consists of the following parts:
1. Select the drive and turn on the motor
2. Wait for the motor to come up to speed if needed.
3. Move the head to the specified track
4. Give the sector number to the FDC
5. Send a 'read sector' command to the FDC
6. Execute a loop which reads 256 bytes
7. When finished, check the FDC for errors
Though you don't need a boot program at this time, this is an ideal opportunity to experiment with the read sector routine and learn some of its characteristics. The boot program is normally needed to boot SK*DOS, but in many systems it is contained within the monitor ROM and so often we don't need to write our own.
The function of the boot program is to load sector 1 of track 0 of the disk into memory and transfer control to it. The data loaded from this sector is called the 'super-boot', and takes care of loading SK*DOS itself.
The BOOT.TXT file on the enclosed disk is the actual boot program that is used in our HUMBUG monitor. Since this may be your first attempt at writing a disk interface program, here is a listing of the program with remarks.
The program begins with the customary equates which refer to actual FDC addresses.
* DISK BOOT FOR SK*DOS
* DISK CONTROLLER EQUATES
E014 DLATCH EQU $E014 DRIVE SELECT LATCH E018 CMDREG EQU $E018 FDC COMMAND REGISTER E018 STAREG EQU CMDREG FDC STATUS REGISTER E019 TRKREG EQU CMDREG+1 FDC TRACK REGISTER E01A SECREG EQU CMDREG+2 FDC SECTOR REGISTER E01B DATREG EQU CMDREG+3 FDC DATA REGISTER
* ACTUAL BOOT PROGRAM FOLLOWSNow on to the actual program code:
0000 10CE DFBF FLBOOT LDS #$DFBF RESET STACKThe very first thing in the boot program is to reset the stack into a place where it will not conflict with the super-boot or parts of SK*DOS. In this example, the stack is placed just below $DFBF, since many 6809 systems use the space starting at $DFC0 for monitor storage. Depending on your own adaptation, however, you may eventually have to change this address. The reason is that the disk drivers you will write later will start at $DE00 and extend up toward $DFBF. If these drivers take up too much room, they will interfere with the stack. Alternative locations for the stack are at $C600, or at $BFFF. The latter is just below SK*DOS, and obviously assumes that you have enough memory installed in the system.
0004 86 D0 LDA #$D0 FORCE INTERRUPT/RESET COMMAND 0006 B7 E018 STA CMDREGAlthough your disk drivers will never have this command, it is a good idea to begin the boot process by sending the $D0 command to the FDC to force it to abandon whatever it is doing and reset. This is particularly important with some of the older 1771 controllers, which do strange things during the first 30 seconds or so after a hardware reset.
0009 7F E014 CLR DLATCH DRIVE = 0 AND MOTOR ONLocation E014 is usually used to select the drive number, and clearing it selects drive 0. Although in some systems it is also used to select the disk side, clearing it is safe because this selects side A.
Incidentally, it is quite possible to write SK*DOS so it will boot from a drive other than 0. You must then somehow tell the boot program what drive you want to use so it can put the appropriate drive number into DLATCH. In addition, though, it must also pass the drive number to the super-boot program after it is loaded, and the super-boot must in turn put that drive number into the SYSTDR and WORKDR locations of SK*DOS after that is loaded, so that SK*DOS will go to the correct drive for any possible STARTUP file.
000C C6 03 LDB #3 000E 30 1F FLWAIT LEAX -1,X WAIT A BIT FOR MOTORS 0010 26 FC BNE FLWAIT 0012 30 1F FLWAI1 LEAX -1,X 0014 26 FC BNE FLWAI1 0016 30 1F FLWAI2 LEAX -1,X 0018 26 FC BNE FLWAI2The preceding commands wait a second or two for the drive motor to get up to speed.
001A 86 0F LDA #$0F RESTORE, LOAD, VERIFY, SLOW 001C B7 E018 STA CMDREGRefer to the FDC manual to see how commands are structured. In most cases, the left four bits of the command byte specify the command while the right four bits specify options. In this case, the $0x specifies a restore (move the head to track 0), and the four bits in F specify that the head is to be loaded (pressed against the disk), the FDC is to verify that it is on the correct track by reading the disk information, and that the restore operation is to be done at the slowest disk head stepping speed.
001F 8D 33 BSR SHORTDReading the fine print in the FDC information tells us that every command to the FDC must be followed by a delay of 28 microseconds. It doesn't hurt to make the delay a bit longer, which avoids problems if the clock speed is not quite right. The SHORTD delay, which appears later, is used to provide that wait.
0021 F6 E018 FL1 LDB STAREG LOOK AT FDC STATUS 0024 C4 01 ANDB #$01 BUSY BIT 0026 26 F9 BNE FL1 WAIT IF STILL BUSYThese three instructions load the FDC status byte and look at the rightmost bit. This bit is the 'busy' bit, and tells us that the FDC is still busy completing the last command. We simply repeat this loop as long as the busy bit is a 1; when it becomes 0 we will continue.
0028 C6 01 LDB #1 002A F7 E01A STB SECREG READY FOR TRACK 0 SECTOR 1 002D 8D 25 BSR SHORTDSince we have already restored the head to track 0, we now tell the FDC that we want sector 1.
002F 86 8C LDA #$8C READ SECTOR COMMAND 0031 B7 E018 STA CMDREG 0034 8D 1E BSR SHORTDThe preceding sends the read sector command to the FDC, and again waits for a short delay. It is important not to make this delay too long, or we will miss the first few bytes of data in the sector!
0036 8E C100 LDX #$C100 POINT TO MEMORY 0039 20 09 BRA CHEKST GO WAIT FOR DATAThe super-boot read in from the disk will be placed starting at location $C100, and so we point the index register to it. Then the program branches to CHEKST.
003B C4 02 GTDATA ANDB #$02 CHECK DRQ BIT 003D 27 05 BEQ CHEKST NO DRQ, GET STATUS AGAIN 003F B6 E01B LDA DATREG DRQ, SO GET DATA 0042 A7 80 STA 0,X+ AND STORE INTO MEMORY 0044 F6 E018 CHEKST LDB STAREG CHECK STATUS 0047 C5 01 BITB #$01 BUSY BIT 0049 26 F0 BNE GTDATA IF STILL BUSY LOOK FOR DATAThe foregoing loop is performed until all 256 bytes in the sector have been read. The best way to follow it is to look at CHEKST LDB STAREG, which looks at the FDC status byte. As long as the FDC is busy, the program loops to GTDATA (it will exit the loop once the FDC is no longer busy). The GTDATA ANDB #$02 instruction then looks at bit 1 of the status byte just loaded. If this bit is a 1, then this is the DRQ or 'Data ReQuest' signal from the FDC, which signals that a data byte has been read; in this case, it is red in from DATREG and stored into memory. If DRQ is zero, then no data is present yet and so the program returns to CHEKST to check the status again.
It is important to note that this loop must be short enough so that no data is missed. Once the FDC starts to read the disk, data will arrive at a fixed rate; failure to read every byte from the FDC will result in an LD or 'lost data' error message. The time between data bytes is as follows:
3-1/2" or 5-1/4" disk single density
64 us
3-1/2" or 5-1/4" disk double density
32 us
8" disk single density
32 us
8" disk double density
16 us
Note that 64 microseconds is certainly enough time to go through a typical loop; 32 microseconds often requires some special care and techniques, and 16 microseconds on a 6809 usually requires either that the CPU clock speed be increased to almost 2 MHz, or that a more complex disk controller (using either DMA or a self-contained hardware sector buffer) be used.
Once the loop has read all 256 bytes, the next step is
004B C5 2C BITB #$2C IF NOT BUSY CHECK CRC OR LDThis instruction checks three bits in the status register for three kinds of errors - wrong record type, CRC error, or lost data. CRC error would indicate that an error was made in reading the data and it did not have the correct CRC check sum; lost data would indicate that the loop wasn't fast enough to get every byte read in.
004D 27 02 BEQ FLDONE DONE WHEN NO ERROR 004F 20 AF BRA FLBOOT REPEAT ON ERROR 0051 7E C100 FLDONE JMP $C100 GO TO EXECUTE SUPER-BOOTIf there are no errors, then the boot program jumps to the beginning of the program just loaded at $C100, else it repeats again.
* SHORT DELAY FOR FDC TO SETTLE 0054 C6 14 SHORTD LDB #20 0056 5A SHORT1 DECB 0057 26 FD BNE SHORT1 0059 39 RTS END FLBOOTAlthough the boot program reads an entire sector into memory, it does take quite a few shortcuts. Rather than moving the head to any track on the disk, it specifically restores it to track 0 - this is quite a bit easier than going to any random track. Second, the program does not do as much error checking as it should. Some errors are not tested at all; other errors (such as CRC or lost data errors) will cause repeated retries forever. Ultimately, our disk routines must get around some of these faults, but for now this is a good beginning.
In this program, the boot program was origined at $0000 and loaded the sector into $C100. For your first efforts, you should replace the JMP $C100 instruction at the end with a return to your monitor program, so that the boot program loads the sector but does not actually try to execute it.
To test the boot program, you should fill the memory at $C100 with a steady stream of zeroes or $FFs, and examine it after the boot programs has executed to see that the data has been changed. The super-boot on the SK*DOS disk will always start with a $20 (BRA) or $7E (JMP) instruction, and a few bytes later will have three ASCII characters identifying the generic controller type, such as "DC2" or "DC4". You can look at the data loaded to check that these bytes have been properly loaded into memory.
Once you have it properly working, another worthwhile test is to load SECREG with a 5 rather than 1, and thus load track 0 sector 5. This is the beginning of the disk directory, and you should be able to examine memory and detect valid file names.
Once you have had some practice with disk reading and the boot program, it is time to proceed to the next section to write a super-boot program.
1. The boot program (either resident in the monitor ROM or typed in from the keyboard) is used to load the super-boot program into memory. This program is very simple because the super-boot is always in the same place on the disk - track 0 sector 1 - and is always written in single density.
2. The super-boot program then in turn loads SK*DOS into memory. The super-boot is quite a bit more complicated because (a) it has to find SK*DOS on the disk, and (b) the rest of the disk might be double-density or double-sided.
In almost all cases, you will have to write a super-boot program when configuring SK*DOS to a new machine. Although you will initially test and use it by itself to load SK*DOS at the beginning, eventually it will become part of the FORMAT program so that it is automatically placed on every new disk you format.
This section will provide the code for two sample super-boot programs. The first is for the generic DC-2 - type controller, and works for a simple single-density, single-sided application. The second is for the generic DC-4 - type controller, and supports double-density and double-sided disks. It also supports double-stepping -it allows 40-track disks to be booted on 80-track drives.
The two programs have some common features. First, they must both be written in fully relocatable code for the simple reason that they should work regardless of where in memory the boot program places them. There is some variability in ROM monitors as to super-boot placement. (On the other hand, if you will be putting your own boot program into your own ROM monitor, then you can specify and control the addressing, and it may not be necessary to use relocatable addressing for the super-boot. This might be a necessary step if there is not enough room.)
In order to avoid having to search the disk directory to find SK*DOS, the super-boot program usually has the logical track and sector address placed into bytes 5 and 6 by the LINK program. This is done in the variable FIRSTS in both of the sample programs. Note that both programs initialize this to 00 00, but both go to an ERROR routine if it is still 00 00 at the time of execution. SK*DOS can never be located at track 00 sector 00, and so a value of 00 00 indicates that the LINK program was never executed.
It is assumed that the super-boot is executed directly after being loaded by the boot; hence, the drive motor is on and the correct drive has been selected. Hence the super-boot omits these steps.
The first super-boot program is on the supplied disk under the name SUPRBOOT.DC2; you should refer to it for the following discussion.
In addition to the four addresses for the disk controller, the super-boot
also uses three addresses in the ROM monitor:
The second super-boot program is for the generic DC-4 - type controller; it is on the supplied disk under the name SUPRBOOT.DC4. It allows either single or double density, either one or two sides, and either single or double stepping (to boot a 40-track disk on an 80-track drive). In order to avoid the necessity of trying out different densities and sides, this version uses two locations which must be preset by the FORMAT program when it places the super-boot on the disk:
DIDENS indicates the disk density, and should be 0 for single density, non-zero for double density. Note that this does not apply to the density of track 0, which is ALWAYS single-density; DIDENS describes the density of the rest of the disk.
SECSID indicates the number of sectors per side of each track after track 0. The default for single density is 10 (or $0A), but the FORMAT program should place 18 (or $12) into SECSID if the disk has been formatted in double density.
It should be noted that this super-boot program is too long to fit into just one sector, and so it occupies both track 0 sector 1 and also sector 2. But since the boot program can only boot one sector, the very first part of the super-boot must be able to load the second sector. In writing such a super-boot program, you must make absolutely certain that all the routines needed to load the second sector (including any routines needed to handle errors) are entirely contained within the first sector. The end of these essential routines is indicated with a comment in the listing.
While writing and testing the super-boot program, you should remove the JMP 0,X instruction (four lines after DONE) and substitute a return to your monitor. This is the instruction which would normally jump to the beginning of SK*DOS; during the beginning stages your SK*DOS, though loaded into memory, is still not ready to be run and so it would be dangerous to jump directly to it. Later, after you have written the console drivers in the next section, you can put the JMP 0,X instruction back.
This area consists of two parts:
1. A vector table beginning at $D3E5 and containing twelve two-byte vectors which point to the routines themselves. The following table gives the list of vectors:
D3E5 Input character
from keyboard and don't echo
D3E7 IRQ interrupt handler
*
D3E9 SWI3 interrupt vector
*
D3EB IRQ interrupt vector
*
D3ED Timer off *
D3EF Timer on *
D3F1 Timer initialize *
D3F3 Monitor reentry address
D3F5 Serial port initialization
routine *
D3F7 Terminal keyboard
status check
D3F9 Output character to
terminal
D3FB Input character from
keyboard and echo
The vectors marked with * above are not currently used, but are included for compatibility with future versions.
2. Corresponding routines beginning at location $D370. These routines can go up to $D3E4, but generally will be much shorter than that.
In the SK*DOS implementation at the time of writing this manual, the actual code for these drivers and pointers is shown in the file CODRIV.TXT on the enclosed disk.
Many of these routines can simply be routed through to the ROM monitor
(locations $F804 through $F80A in the listing). Here is a short explanation
of each necessary routine:
IRQHAN is a bit of a problem. Ideally, it should do nothing except RTI. In practice, however, many user programs written for the FLEX DOS enable interrupts and then forget to turn them off. You may therefore wish to take the safe way out and assume that you should disable interrupts in case the user doesn't know any better. In that case, the IRQHAN routine would be
IRQHAN ORCC #$50 TURN OFF INTERRUPTS RTI AND THEN DO NOTHINGOnce you have written and tested the console drivers, you may test them as follows:
1. Load the super-boot program and execute it to load SK*DOS from the master system disk. (But make sure that the JMP 0,X instruction at the end has been replaced with a return to the monitor.
2. Now load the console drivers into memory at the correct address.
3. Now that you have a copy of SK*DOS in memory with the appropriate console drivers, fill the area from $DE00 through $DE1D with 39 (RTS) instructions. This replaces all disk drivers with dummy RTS instructions.
4. Making sure to remove the SK*DOS disk from the drive, start SK*DOS by jumping to address $CD00.
SK*DOS should now sign on with its greeting, ask you for the correct date, and then go to the SK*DOS prompt. Since you do not yet have any disk drivers installed, you cannot do much at this point, but this is a good test to make sure that all is well so far.
When you are satisfied, type the MON command and you should return to your monitor.
The area from $DE00 through $DFBB can be used for disk drivers. If more room is required, then it is possible to put them (1) into unused space in the console terminal driver area, (2) in dedicated RAM on some CPU boards, or (3) in RAM just below $C000, adjusting MEMEND correspondingly to make room for these drivers.
The disk driver area starting at $DE00 should begin with the following three-byte JMP or LBRA instructions which point to the actual driver routines. SK*DOS does a JSR to these vectors. (In the following discussion, the notation c() means "contents of". For example, c(X) means contents of X.) Unless indicated otherwise, each of the following routines should return to the main program with registers intact, and with the Z bit indicating zero if no error occurred, or non-zero if an error occurred. In case of an error, the B register should contain the contents of the Western Digital disk controller's status register. If another FDC is used, the disk drivers may have to convert the actual FDC errors into equivalent Western Digital error codes and place them into the B register.
$DE00 READ SINGLE SECTOR. This subroutine should read a sector from logical track c(A) and sector c(B) into memory at c(X). If the head is not on the correct track, then this routine should step to it first.
$DE03 WRITE A SINGLE SECTOR This subroutine should write a sector from memory at c(X) to logical track c(A) and sector c(B). If the head is not on the correct track, this routine should step to it first.
$DE06 VERIFY THAT A SECTOR HAS BEEN WRITTEN This subroutine should read back the sector just written to make sure that it can be read without error. It might be nice to compare the data read with the data written, but this is usually not done.
$DE09 RESTORE HEAD TO TRACK 00 On entry, X points to an FCB which contains the drive number. Since this drive number may be different from that last used, this routine should do a drive select before doing the restore.
$DE0C SELECT A DRIVE On entry, X points to an FCB which contains the drive number.
$DE0F CHECK IF DRIVE IS READY On entry, X points to an FCB which contains the drive number. This routine depends to a large extent on the capability of the hardware. In those cases where the disk controller hardware has no way of checking the drive, it may be quite sufficient to just check for a valid drive number. If the controller turns motors on and off, then it might be nice (if possible) to check whether motors are on. If not, then the routine should turn them on, wait the required time for them to come up to speed, and then check again. In other cases, it may be sufficient to check the drive's own ready line.
$DE12 CHECK IF DRIVE IS READY This routine can be the same as the preceding one, though often it does not wait for the motors to come up to speed.
$DE15 COLD START INITIALIZATION This program initializes the disk drivers. This usually involves storing data in a drive table, etc. Cold start initialization is only done when SK*DOS is initially booted.
$DE18 WARM START INITIALIZATION This initialization is done each time SK*DOS is re-entered through its warm start entry. In most instances, this vector will simply point to an RTS instruction.
$DE1B SEEK TO DESIRED TRACK This subroutine should move the head to track c(A). This program is usually executed just prior to reading or writing a single sector, so c(B) is the sector number. On double-sided or multi-platter disk drives, c(B) must be used to select the head.
$DE1E LAST DRIVE NUMBER Since SK*DOS supports up to ten drives, numbered 0 through 9, it is necessary to provide some way of detecting invalid drive numbers. As seen below, location $DE1E provides the last drive number; attempts to access a drive number larger than this result in a NR (Not Ready) error. This feature is optional, but strongly recommended.
$DE1F DRIVE STEP RATE SK*DOS disk drivers allow you to change the drive step rate to match the speed of your drives; this location should contain a number between 0 and 3 which sets the step rate for all drives according to the following table:
5-1/4"
drive 8" drive
0
6 ms
3 ms
1
12
6
2
20
10
3
30
15
The SK*DOS STEPRATE command allows the drive steprate constant to be changed. Note that standard SK*DOS disk drivers use the same number for all drives, but there is no reason why your disk drivers could not assign different rates to different drives (although this would require you to write a different STEPRATE command.)
The DIDRIV.DC2 file on the enclosed disk shows the disk drivers we have developed for the generic DC-2 - type controller.
These disk drivers are strictly for single density, single-sided operation. Note that they do not retry any operations in case of an error; this function is handled by SK*DOS, which will call the appropriate routine again on an error, and will retry the operation a number of times before issuing an error message.
Even if you intend ultimately to support double-density or double-sided operation, you should begin by writing single density drivers for two reasons. First, you must support single-density operation since track 0 on a SK*DOS disk is always in single density; second, it is easier to start small and build up later. In any case, you will need single-density operation now to read the supplied SK*DOS master system disk.
In reviewing the listings in this section, note that sometimes one routine calls another. It is essential that the contents of registers be preserved if needed. For example, the read and write sector routines both call the seek routine to get the head to the right track. The seek routine must therefore preserve the contents of registers later needed by read or write.
The DIDRIV.DC4 file on the enclosed disk shows how we have handled double-density and double-sided operation in our generic DC-4 - type disk drivers. This type of disk controller uses the DLATCH location at $E014 to control the side selection, and also allows us to check for FDC DRQ and INTREQ signals via DLATCH. This feature is used only in double-density operation to speed up disk accesses (since there is a limited time between bytes). Although it could have also been used for single density, the testing for DRQ and completion (via the BUSY bit) is handled through the status register so that the same routine will also work with the generic DC-2 - type controllers, which do not implement status signals through DLATCH.
The absolute top limit for disk drivers should be $DFBB, since many monitors use the area above $DFBC for internal storage.
Notice how the DENSTE array is used to control density and double-stepping. Each time a RNF (record not found) error is encountered, indicating that the FDC was not able to find the requested sector, the corresponding entry in DENSTE for the selected drive is incremented, and the routine returns to SK*DOS. The next time it is called, bit 0 of this entry is used to select the density ( a 0 specifies single, a 1 specifies double density), while bit 1 is used to specify double-stepping (a 0 specifies normal stepping, while a 1 specifies double-stepping so that an 80-track drive takes two steps for each track to position itself over the spot where a 40-track drive would have written that track.)
Note also that TRTABL stores the current track number for each drive. The Western Digital FDC controllers have only one track register, and cannot keep track of which drive is on which track. hence each time you switch to a different drive it is necessary to save the current track number in the TRTABL array and put the current track number for the new drive in the FDC's track register. In this way the FDC can quickly switch to the appropriate track on the new drive without having to restore to track 0 on an error. The TRTABL array is initialized to 255 for each drive, so that the first time the disk driver is called (even if the restore routine should somehow be skipped), the drive will automatically do a restore.
Note too that the disk drivers have their own LASDRV location which tells them the last drive number installed on the system. This may be different from the MAXDRV location maintained by the SK*DOS file control system, since it is entirely possible that a different set of drivers may be used to handle other drives (such as hard drives). In configuring the system, you may want to provide a utility whereby the user can change the value of either LASDRV or MAXDRV (although obviously the POKE command can be used for that purpose.)
Once you have written the disk drivers, you should check them one routine at a time, generally in this order:
Once you have written and tested the disk drivers, you may test them as follows:
1. Load the super-boot program and execute it to load SK*DOS from the master system disk. (But make sure that the JMP 0,X instruction at the end has been replaced with a return to the monitor.
2. Now load the console drivers into memory at the correct address.
3. Then load the disk drivers into memory at the correct address.
4. Making sure to remove the SK*DOS disk from the drive, start SK*DOS by jumping to address $CD00.
SK*DOS should now sign on with its greeting, ask you for the correct date, and then go to the SK*DOS prompt.
Using a disk known to be empty (preferably straight from a box of fresh disks), try to execute a command such as CAT. Since the disk is empty (and presumably cannot be read), you should get an ERROR NR (not ready) message after a few seconds. If anything else happens (including the computer locking up) then probably there is some error in your disk drivers.
The next steps depend on how brave (or foolhardy) you are, and on whether you have access to other SK*DOS formatted disks. If so, make sure to write-protect them, and try to execute some command such as CAT. Since we cannot possibly anticipate every contingency, we will leave that part up to you.
Assume that you have the console drivers saved in a file called CODRIV.BIN, and the disk drivers in a file called DIDRIV.BIN. (There are several ways to get them into this kind of a binary file - either assembling them with a resident assembler, or else getting them into memory some other way and then using SAVE.) DIDRIV.BIN should contain a transfer address of $CD00.
Making sure that there is no SK*DOS.SYS on the disk (which is why we want to format a disk first), we type
APPEND SK*DOS.COR CODRIV.BIN DIDRIV.BIN SK*DOS.SYS
(you may want to add 1. in front of file names to use a different drive) to append the three programs together into a single file called SK*DOS.SYS (note that DIDRIV.BIN had a transfer address of $CD00, so the resulting file will also. Although the super-boot programs described in this manual allow multiple transfer addresses, with the last one being the one used, some other super-boot programs allow only one such transfer address, and will in fact not read any other data after that address. If you use another super-boot or FORMAT program, make sure to have a transfer address only at the very end of the file.)
Next, use
LINK SK*DOS.SYS
to link it into the super-boot program. The result is a bootable disk.
Formatting a brand new disk writes data into every sector of every track of the disk, numbers each sector so the FDC can find it later, and initializes the disk so that the directory is empty, the System Information Sector contains the correct information, sector 1 (and possibly sector 2) of track 0 contains the super-boot program, and the rest of the disk is one long chain of free space.
As with preceding portions of this manual, we again present two different FORMAT programs as illustrations. The first (called FORMAT.DC2) on the enclosed disk) is a simple one for generic DC-2 - type controllers, which supports only single density and single-sided operation.
The DC-2 FORMAT program is about as simple and straightforward as one can get. Note that it uses the disk drivers developed in the previous chapter to select the drive and read sectors, although it has its own write routine (since the FORMAT program writes an entire track, rather than just one sector, when formatting.)
The file FORMAT.DC4 contains a more complex FORMAT program for generic DC-4 - type controllers:
Unlike the first FORMAT program, this one is partially table driven. The two tables, SDTABL for single density and DDTABL for double density, specify how each track will be initialized. For example,
SDTABL FCB 10,10
specifies the number of sectors per side on track 0 (10) and the number of sectors per side on the remaining tracks (also 10). The next entry,
FCB 12,$FF
specifies that the track entry will contain 12 bytes of $FF and so on. The last entry,
FCB 1,4,7,10,3,.....
is the sector interleave table which specifies the physical layout of the sectors on each track; in this example, the very first physical sector is logical sector 1, followed by logical sector 4, then 7, and so on. (Interleaving is used so that logically consecutive sectors are somewhat separated on the track so that the computer is given some time between reading or writing consecutive sectors for other calculations.)
Note that both FORMAT programs contain the code for the super-boot program developed earlier.
Operation of the DC-4 - type controller is somewhat unusual, leading to some gross incompatibility problems. This affects the FORMAT program (and to some extent, also disk drivers), and so some explanation is needed.
A disk controller capable of double-sided and double-density operation must have two latches, controlled by software, which are used to select the side and density. The density latch is used to send the DDEN' (Double Density ENable, inverted or active low) signal to the floppy disk controller to select density, while the side select latch sends a signal to the disk drive to select the head.
Western Digital 1795/2797/2795/2797 floppy disk controllers (FDC) contain a side select latch. It is controlled by the U bit (bit 1) of the control register, and outputs a signal called SSO (Side Select Output) which should be sent to the drive to select the side. The density select latch should be provided separately, on the controller p.c. board, to drive the FDC's DDEN' input. Unfortunately, in designing the DC-4 controller board, Southwest Technical Products (SWTP) did the opposite (and, in an effort to be compatible with this controller, several other manufacturers have followed the same approach) - they used the SSO side select latch to feed DDEN' (through an inverter), and used the external latch to select the side.
To see why this causes a problem, we have to look at how a floppy disk is formatted. During formatting, each sector is preceded by a 'sector header' which identifies that particular sector. In the DC-4 - type FORMAT program, this header is specified by the SDTABL for single density, and DDTABL for double density. If you look at the listing, you will see in SDTABL the entry
FCB 1,0 SINGLE DENSITY (*** See Text)
which means that there is one byte of 00, indicating single density. The corresponding entry in DDTABL reads
FCB 1,1 DOUBLE DENSITY (*** See Text)
which indicates that one byte of 01 indicates double density.
Now here is the problem. Examination of the Western Digital FDC specification sheets indicates that this byte, which is located between the track and sector numbers, is supposed to be used to indicate the side, NOT the density! When reading or writing a disk sector, the FDC is supposed to check this byte (which is recorded on the disk) against the state of the side-select latch, to make sure that the controller is reading or writing the correct side of the disk. If the side-select latch does not agree with the side bit as written in the sector header, the FDC will fail to read or write and will return a Record Not Found (RNF) error.
In Western Digital 1791/1793/2791/2795 controllers, the side comparison is optional and can be disabled, but in the 1795/1797/2795/2797 controllers it cannot be disabled and must always be done. But since the SSO latch in DC-4 - type controllers is used to select density rather than side, that means that the FORMAT routine must use this byte in the sector header to indicate the density, rather than side, so the comparison of this byte with the SSO latch will be done properly. This is why our FORMAT program makes this byte a 0 for single density, and a 1 for double density.
This neatly solves the problem, except for one hitch - there are some manufacturers, such as Gimix, who do not follow this convention, and who use the side select latch for its correct purpose of indicating the side; they provide a separate latch for density selection (and sometimes a separate latch for side selection as well). Because they use the SSO latch properly, they also use the side-select bit in the sector header properly. Although this approach is theoretically correct, it means that their disks are not fully compatible with those written by DC-4 - type controllers. The following table shows why:
'SIDE' bit in Sector header --------------------------- SWTP DC-4 Gimix --------- ----- Side 0 of single-density disk 0 0 Side 1 of single-density disk 0 1 Side 0 of double-density disk 1 0 Side 1 of double-density disk 1 1This table indicates that the two types of controllers may not be able to read each other's disks - they cannot read the second side of single-density disks, or the first side of double-density disks. This essentially means that only single-sided, single-density disks should be used to interchange data between the two systems.
It should be noted that 1791/1793/2791/2793 controllers can disable the side compare. Assuming they are properly programmed, they can read disks formatted on either system, or convert from one to the other. Likewise, those controllers which provide separate latches for side and density selection can also be programmed for either disk format (because they can select the side and density independently of the state of the SSO latch in the FDC). Thus, for example, SK*DOS or Data-Comp Flex, as implemented on the Color Computer, which uses the 1793 FDC, can read or write either disk format, in either density, although they format disks in the DC-4 - type format. Other Color Computer Flex implementations, such as FHL Flex, format disks in the Gimix format and cannot read DC-4 - type disks.
A second compatibility problem has to do with the number of sectors per side. SK*DOS (and Flex) standardize on the following numbers for various disk formats:
Single density Double density
5-1/4"
10 1
18
8"
15
26
Moreover, sectors are correctly numbered on both sides (that is, sector 11, which is on side B of a single-density double-sided disk, is numbered $0B), even though it may be the first sector on the second side. This is not universal among all computers, some of which may number it $01 because it is the first sector on that side.
Either way, the disk drivers must know which side to access for a particular sector. This is done by looking at the sector number - numbers above 10 on single density 5-1/4" disks, for example, automatically mean side B.
A problem arises if you choose to put a different number of sectors per side (as some implementations of Flex have done!) If, for example, you put only 17 sectors per side on a double-density 5-1/4" disk, your sector 18 will be on side B, whereas other SK*DOS implementations will have their sector 18 on side A. You will not be able to read each other's disks.
When implementing SK*DOS on other computers, you should carefully consider these problems, and decide how you wish to tackle the compatibility question. Wherever possible, you should aim to stay compatible with standard SK*DOS (and Flex), even when it seems expedient to do things differently.
The P.CMD program is one of the SK*DOS disk-resident commands. In order to use P.CMD, you type its name prior to another command. For example,
P CAT 1
would print the catalog of disk 1 on the printer instead of displaying it on the normal terminal screen.
The P.CMD command is simply a standard SK*DOS program which substitutes a call to itself instead of SK*DOS's normal OUTCH address. The normal OUTCH address is then restored when SK*DOS resumes control.
The P.CMD program puts a short startup routine in the transient command area at $C100, and puts the printer driver itself at $CCC0. Once this is done, the startup routine at $C100 is no longer needed, and can therefore be overlaid by other programs or commands.
This program is contained in the file P.CMD on the enclosed disk.
Note carefully the ORG statements in the program; certain user programs look for the printer driver routines to be origined exactly at those addresses.
This printer driver is for a serial port, interfaced through an ACIA at address $E000 (commonly called 'port 0' in SS-50 bus systems). Although we do not supply parallel printer drivers, it is a fairly simple matter to write such a program.
The P.CMD program is self-contained and does everything it has to do in a fairly compact and speedy manner. Historically, however, there are some programs which want the $CCC0 portion of the driver to be located in a separate file called PRINT.SYS. Although such a file is totally unnecessary, we nevertheless supply it for historical purposes. The code is identical with part of P.CMD, but is shown separately in the PRINT.SYS file on the enclosed disk.
The GETDATE.TXT file on the enclosed disk shows how we implemented this function for the PT-69 computer by Peripheral Technology of Marietta GA.
Once you have the console and disk drivers written, you should save them in a binary file and append them to SK*DOS.COR. The resulting file should then be renamed to SK*DOS.SYS and LINKed so that it will be used for booting.
Assume that you have the console drivers saved in a file called CODRIV.BIN, and the disk drivers in a file called DIDRIV.BIN. (There are several ways to get them into this kind of a binary file - either assembling them with a resident assembler, or else getting them into memory some other way and then using SAVE.) DIDRIV.BIN should contain a transfer address of $CD00.
Making sure that there is no SK*DOS.SYS on the disk (which is why we want to format a disk first), we type
APPEND SK*DOS.COR CODRIV.BIN DIDRIV.BIN SK*DOS.SYS
(you may want to add 1. in front of file names to use a different drive) to append the three programs together into a single file called SK*DOS.SYS (note that DIDRIV.BIN had a transfer address of $CD00, so the resulting file will also. Although the super-boot programs described in this manual allow multiple transfer addresses, with the last one being the one used, some other super-boot programs allow only one such transfer address, and will in fact not read any other data after that address. If you use another super-boot or FORMAT program, make sure to have a transfer address only at the very end of the file.)
Next, use
LINK SK*DOS.SYS
to link it into the super-boot program. The result is a bootable disk.
After the super-boot program jumps to $CD00, the cold-start entry point, SK*DOS does some initialization and then jumps to a second initialization program located in the region from $C780 through $CCFF. This second initialization consists of those steps which are performed only at the very first boot of SK*DOS:
If you need any extra room for disk or console drivers, it is therefore important to know about this extra second initialization routine, and not to use any memory from $C780 through $CCFF for them.
As loaded from disk, MEMEND is initialized to $BFFF, so that the memory test is only performed up through $BFFF. (This prevents the 8K of memory devoted to SK*DOS from being accidentally assigned to user memory.)
If you need additional space below $C000 for drivers, custom routines, or I/O assignments, you may change the initial value of MEMEND so that the SK*DOS memory test ends below this space. The easiest way is to insert an ORG and FDB into either the console or disk driver code so as to change MEMEND to a different value.
SK*DOS contains a location called FNCASE, at $CC49, which is used by the GETNAM routine as follows: Each character submitted for a file name is checked against the value of FNCASE; if it is larger than FNCASE, then it is converted to upper case by subtracting $20. FNCASE is initially set to $60, so that all lower case letters (whose ASCII codes begin at $61) are converted to upper case. If you change FNCASE to $7F, then lower case letters will be used without change.
FCS functions 9 (read a single sector) and 10 (write a single sector) are the only routines which read or write a sector. These routines both have a section of code which reads
LDX #address of user's FCB LDA #1 if reading, or #0 if writing JSR $D432At address $D432, there are the three instructions
RTS NOP NOPHence FCS functions 9 and 10 exit to $D432, but then immediately return to continue reading or writing the disk, respectively. You may substitute a JMP instruction at $D432 to your own virtual (RAM) disk or disk cache routine, if desired.
There are several things to watch out for:
1. Just before the above JSR, the stack contains the A, B, X, Y, and U registers, plus a return address. The JSR return address is then added below these. If you desire to return directly to the calling program, you should do so with
LEAS 2,S remove the JSR return address PULS A,B,X,Y,U restore registers RTS and then return2. If you return to the FCS 9/10 function, you need not preserve any registers except the stack pointer.
The CACHE.TXT file on the enclosed disk shows how we have implemented a disk cache for the Color Computer. This particular implementation uses the DSL 128K RAM module, which adds 64K RAM to the computer's normal 64K. This new 64K is divided into two 32K pages (called page 1 and page 2 in the program) which are banked with the lower 32K of normal memory (called page 0). The upper 32K of RAM is fixed and always there. Switching between pages is done by the PAGE0, PAGE1, and PAGE2 routines near the end of the program.
The program is called CACHE, and is invoked by the command CACHE. It is loaded at $C100, but after some initialization, it moves the MEMEND pointer down by about 2K and then copies part of itself (the part from CSTART to CEND) down into that 2K. The portion remaining at C100 can then be overwritten.
CACHE maintains a flag at CACVEC ($CC60-61, a reserved location for this purpose) which indicates whether the cache is loaded and active. If so, invoking CACHE a second time flushes the cache but does not move MEMEND or put another copy into memory. Calling CACHE with a drive number (such as CACHE 1) flushes all cache entries for that drive, but leaves other cache entries intact.
A 1K cache map called CMAP maps the contents of the cache. Although it takes room in main memory, we decided to put it there rather than into page 1 or page 2, for simplicity. The map is divided into 256 four-byte entries, one for each of the 256 sectors which can be stored in the cache. The four bytes contain the drive number, track number, sector number, and an activity indicator called 'timer' which indicates how much time has passed since the last time that sector was used. The timer byte means the following:
0 this cache entry is empty
1 this entry is very old
and can be deleted if space is needed and no empty space exists
$FE this entry is brand new
Values between 2 and $FD indicate the relative age of the entry, with $FD being the youngest.
The cache works like this:
1. Each time a sector is written to the disk, it is also stored in the cache. It is placed into an empty location if possible, or else it replaces the oldest entry in the cache (one having the timer value of 1).
2. Each time FCS function 9 attempts to read a sector, the cache program tries to find it in the cache. If it is there, then it is returned immediately. Otherwise it is first read from disk and then also stored in the cache, as in 1 above.
Due to the structure of the memory expansion in the Color Computer, all disk reads and writes are double-buffered through the BUFFER. Though this slows down the operation of the system, it is necessary if data is to be copied from one page to another. We could have chosen a single-step move for those cases where the FCB is located in the upper 32K of memory, but chose to keep the program simple.
Although the CACHE program is fairly general, we could have speeded it up a bit by noting that there is room for 256 sectors in the cache, while there are only 254 valid values of 'timer', all of which must be different (except for 0 and 1). Hence the search at location $C367 for the oldest entry is not really necessary (because there will always be at least two entries with the values 0 or 1). Hence we need only look for an entry of 0 (empty) or 1 (very old), and are guaranteed that such a value will always be found. Then this portion of the code can be replaced by the following; we chose not to put it in the CACHE program because it might not work in other cases.
* WHEN ENTRY IS NOT FOUND, THEN FIND * EITHER AN EMPTY ONE (CONTAINING 0) * OR VERRRRRY OLD ONE (CONTAINING 1) C367 30 8D 01B5 LEAX CMAP,PCR POINT BACK TO CACHE MAP C36B A6 03 ELOOP LDA 3,X CHECK IF EMPTY C36D 27 1F BEQ FOUNDE FOUND AN EMPTY ONE C36F 4A DECA ELSE CHECK IF VERRRRY OLD C370 27 1C BEQ FOUNDE A 1 MEANT ENTRY WAS VERY OLD C372 30 04 LEAX 4,X NOW POINT TO NEXT ENTRY C374 5A DECB DECREMENT COUNTER C375 26 F4 BNE ELOOP LOOK FOR NEXT OLDER OR EMPTY C377 30 8D 0006 LEAX CERMSG,PCR WE MUST NEVER GET HERE! C37B BD CD1E JSR PSTRNG PRINT 'CACHE ERROR' C37E 7E CD03 JMP WARMST AND IMMEDIATELY QUIT! C381 43 41 43 48 CERMSG FCC 'CACHE ERROR!',4AN IMPORTANT NOTE: If you do implement a disk cache, make sure to warn all users to flush the cache when they change disks or before they format a new disk. Not doing so can lead to disastrous consequences!
Current versions of SK*DOS are supplied with the text file for a user-implementable
RAM disk. If your version did not include this program, you may obtain
the latest SK*DOS update by returning your original SK*DOS disk
to Star-K, along with a $5 shipping and handling fee.
1. The monitor
2. The monitor's boot routine
3. The superboot program
4. SK*DOS itself
5. Any user programs which might be called by the STARTUP file
Most of the problem is caused by the fact that the programmer who writes the monitor does not know how much room the DOS needs, and the author of the super-boot does not know where the monitor's boot routine is going to load it. Both are trying to limit their use of memory to $C000 through $DFFF to avoid potential problems with other programs which the user may put into lower memory prior to booting the DOS.
In addition to making sure that the programs themselves don't overlap, it is also important to make sure their stacks don't cause problems. Here is a general discussion:
Monitors, such as SBUG or HUMBUG, generally occupy ROM locations $F000 (or $F800) through $FFFF. Since nothing else uses these locations, this creates no problems. Instead, the problem lies with their use of RAM for their stack and for working storage. As a matter of convention, they use RAM from $DFC0 - $DFFF for a scratchpad. (Depending on their capabilities and needs, their RAM storage may extend as far down as $DFBC or so.) If your disk drivers extend up to $DFBF, they may get partially erased if you return back to the monitor, so that you may have to reboot the DOS if you do so.
A bigger problem is the monitor's stack. In looking at this, we have to look at the monitor stack under three different conditions:
1. When the monitor itself is running. This is no problem with those monitors (like HUMBUG) which put their stack down below $C080; this is an area used by SK*DOS for its stack, and so the monitor can use it while the DOS is not. On the other hand, SBUG puts its stack just below $DFC0. This is almost certain to overlay the top of long disk drivers if you jump out of the DOS into the monitor to, for instance, debug a program. The obvious solution is to reboot the DOS when you finish using the monitor, but this is not the answer if you are trying to debug a program which reads or writes disk files.
2. When the monitor executes a user program. This situation is similar to 1. above. As before, it is a problem with monitors such as SBUG, which use the area under $DFC0 for their stack.
3. Of more concern is the monitor stack position during the disk boot. SBUG, as well as most versions of HUMBUG, positions its stack just below $DFBF during the disk boot. (Later versions of HUMBUG, as well as other monitors such as PTMON, put it at $C6FF). This may again cause problems with disk drivers, but this problem may be alleviated if the super-boot program resets the stack pointer. Thus it is strongly recommended that you insert an LDS #.... instruction at the very beginning of the super-boot program. (There is plenty of room in the DC-4 style driver listed in this manual, but not in the DC-2 type driver (where we tried to keep it down to one sector).
The question remains where the super-boot should place its stack. If you don't have control over the monitor's boot routine, then you might accidentally place the stack in the very same place that the monitor loads the super-boot itself! There are several possibilities - place the stack somewhere between $C300 and $C700 on the off chance that the super-boot will be elsewhere, or else position the stack a fixed offset below or above the super-boot. The problem here is doing this will prevent a conflict with the super-boot, but might place the stack somewhere where parts of the DOS will overlay it.
There is really no clear-cut answer to this problem. There are just too many combinations of hardware and software, and it might pay to experiment a bit to find the solution that works most often. We have chosen to place it at $C980, in the middle of one of the SK*DOS internal file control blocks, but you may have to use another location in your application. Note that SK*DOS resets the stack to $C080 at both warm and cold start, so the stack position during the super-boot is strictly temporary.
The answer is Yes. If you go back and read the section on the disk cache, you will see that SK*DOS FCS functions 9 and 10 (which read and write a disk sector) jump out to a trap at location $D432. You can easily write some new code for your new disk which ties in through this trap, much like the cache program shown earlier.
The basic process would work like this:
1. When the FCS exits at $D432, check the drive number.
2. If the drive number corresponds to one of your existing disks, simply RTS back into SK*DOS and let it do the work.
3. If the drive number is for your new floppy or hard disk, do the specified read or write, and then return directly to the calling program (read the description of the cache routine to see how to do this right.)
This leaves open two questions - how do you initialize this new code, and how do you load it into memory?
Initializing could be done by patching one of the existing entry points (such as the one for disk initialization) to initialize your new code as well. An easier method is to put a flag into that code which indicates whether it has been initialized or not, and then do the initialization when it is first called.
This new code can be appended to SK*DOS.SYS and loaded in during booting, or else it could be a command file loaded in manually or by STARTUP.
Note that this new code can play games with drive numbers as well. That is, the existing SK*DOS disk drivers are set up to address your existing drives as drives 0 through 3. If you add new floppy or hard drives to your system, you may want to move the existing ones up to, perhaps, drive 4 through 7, and make your new drive(s) Drive 0. This can be done simply by changing the drive number in the FCB while you are checking it. It is necessary, however, to change it back to its original value before returning to the calling program, and this complicates the process a bit. Before doing the RTS to return to SK*DOS's disk drivers, you have to change the return address on the stack so the disk drivers will return back to your program, rather than to the calling program. Then you can restore the original drive number before returning to the calling program through your own routine.
It is quite possible, even likely, that this manual has omissions and errors. Even more likely, it probably has fuzzy areas which seem perfectly logical to those of us in the know, but which are totally incomprehensible to someone not already familiar with SK*DOS.
We are anxious to clear up any such problems, and invite your help. If you do spot any such omissions, errors, inconsistencies, or just plain fuzzy thinking in this manual, please let us know and we will try to correct them.
The best way is by returning this sheet as soon as you have had a chance to read this manual a few times.
Thank you for your help.
Street Address:
City, State, ZIP:
Here are errors I found:
Here are areas I think you should cover better:
Here are things you forgot:
This is what I think of SK*DOS: