SK*DOS (R) : 6809 Configuration Manual :

by Peter A. Stark

Copyright (C) 1984, 1985

by Peter A. Stark
and licensed to 
Star-K Software Systems Corp. 
P. O. Box 209 
Mt. Kisco, NY 10549 
(914) 241-0287

All rights reserved

All Star-K computer programs are licensed on an "as is" basis without warranty. Star-K Software Systems Corp. shall have no liability or responsibility to customer or any other person or entity with respect to any liability, loss or damage caused or alleged to be caused directly or indirectly by computer equipment or programs sold by Star-K, including but not limited to any interruption of service, loss of business or anticipatory profits or consequential damages resulting from the use or operation of such computer or computer programs. Good data processing procedure dictates that the user test the program, run and test sample sets of data, and run the system in parallel with the system previously in use for a period of time adequate to insure that results of operation of the computer or program are satisfactory.
 

SOFTWARE LICENSE

A. Star-K Software Systems Corp. grants to customer a non-exclusive, paid up license to use on customer's computer the Star-K computer software received. Title to the media on which the software is recorded (cassette and/or disk) or stored (ROM) is transferred to customer, but not title to the software.

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.

NOTES

SK*DOS and HUMBUG are registered trademarks of Star-K Software Systems Corp. Whenever used in this manual, the term FLEX is a trademark of Technical Systems Consultants Inc.

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.

CONTENTS

       BEFORE STARTING
1     INTRODUCTION
2     A SK*DOS OVERVIEW
3     SECTOR READ ROUTINE AND THE BOOT PROGRAM
4     THE SUPER-BOOT PROGRAM
5     CONSOLE / TERMINAL DRIVERS
6     DISK DRIVERS
7     THE FORMAT PROGRAM
8     PRINTER DRIVERS
9     ADDING DATE AND TIME
10    OTHER MATERIAL
 
 

BEFORE STARTING

In general, it is important that you develop good habits when using any floppy disk system. It is important that you make frequent backup disks, since it is very easy to lose a file, or even the data on an entire disk, due to a slippery finger or careless mistake.

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.

SECTION I - INTRODUCTION

This manual describes how to adapt the SK*DOS Disk Operating System to systems different from those normally supported.

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.)

What Systems Star-K Supports

Although most disk controllers use one of the Western Digital disk controller IC's (such as the 1771 or 197x series), they sometimes have major differences in how they are programmed. This is especially true of those controllers which permit double-sided or double-density operation, which use eight-inch drives, or which use direct memory access (DMA). For that reason, it is difficult for us to support every conceivable kind of disk controller. Likewise, it is difficult for us to support every conceivable kind of system monitor, console terminal, etc.

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.

What You Need to Run SK*DOS

You need the following computer equipment to run SK*DOS:

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.

How is SK*DOS Configuration Generally Done?

Configuring SK*DOS to run on a new system can be simple or it can be complicated - it depends on many factors. In fact, the exact procedure depends on just how close that system is to existing systems on which SK*DOS already runs.

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.

SECTION II - A SK*DOS OVERVIEW

This section gives an overview of SK*DOS and some of its more important details. Before reading it, however, you should be familiar with the contents of the SK*DOS User's Manual, since this manual will not repeat any of the information in that manual except if it is specifically needed to understand some specific point.

What is a DOS?

The Disk Operating System, or DOS for short, is a program which acts as a file manager for a disk. The DOS acts as a buffer between the disk hardware, and the software which uses that disk. Its primary function is to maintain a disk directory on each disk, fetch program or data files from the disk as needed, and store programs or data back on the disk. As a secondary function, the DOS usually also contains various subroutines, such as character input or output routines, for communicating with other I/O devices in a simple and consistent manner.

What is SK*DOS

SK*DOS consists of four major parts:

(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.

SK*DOS Memory Map

The following table provides a memory map of SK*DOS:

$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.

Disk Format

A typical disk, whether hard or floppy, is divided into tracks; each track is then divided into sectors. The number of tracks and sectors on a disk depends on the type of disk and drive - a 5-1/4" floppy disk might have as few as 35 tracks with 10 sectors per track, or a hard disk might have as many as 256 tracks with 32 or more sectors per track. In addition, the disk drive might be able to use both sides of a disk, or a hard disk might have multiple disks spinning on the same spindle.

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:

The Super-Boot

Sector 1 on track 0 holds the super-boot program. This is a program which is loaded by the boot program (which is usually located in the boot program ROM monitor) and which in turn loads the rest of SK*DOS into memory. (This sector has a full 256 bytes of data, as the first four bytes of the sector are used for regular data storage rather than being used as pointer and sector count bytes.)

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.

The System Information Sector (SIS)

Sector 3 is the System Information Sector or SIS. It contains the following information:

/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.

The Directory

The directory begins on sector 5 of track 0 and fills up the rest of the track. The first 16 bytes of each directory sector are empty, and the remaining 240 bytes hold directory entries. Since each directory entry requires 24 bytes, there is room for 10 entries in each sector. For example, on a 5-1/4" single sided disk, there are 10 sectors in track 0. Hence there are six sectors in the directory, numbered from 5 to 10, for a total of 60 directory entries. The six sectors are linked (through the first two bytes in each sector), and the last sector has a pointer of 00-00. When the directory is filled up, however, SK*DOS will take a sector from the free chain and add it to the directory, so that the directory can be expanded to make room for more entries (although this may greatly slow down the operation of the system if the added directory sector is on one of the inner tracks of the disk since the disk head will have to step in and out each time it accesses the directory.)

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.
 

SECTION III - SECTOR READ ROUTINE AND THE BOOT PROGRAM

The most essential step in configuring SK*DOS to run on a new computer is to write the routines which read and write a single sector of the disk. These two routines are so similar, however, that once you do the read routine, it is usually a simple matter to do the write routine as well.

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 FOLLOWS
Now on to the actual program code:
0000 10CE DFBF     FLBOOT  LDS    #$DFBF    RESET STACK
The 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    CMDREG
Although 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 ON
Location 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    FLWAI2
The 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    CMDREG
Refer 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    SHORTD
Reading 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 BUSY
These 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    SHORTD
Since 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    SHORTD
The 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 DATA
The 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 DATA
The 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 LD
This 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-BOOT
If 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 FLBOOT
Although 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.

SECTION IV - THE SUPER BOOT PROGRAM

The normal boot process for SK*DOS is as follows:

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:
 

These addresses are not essential, as the super-boot could be written to either retry forever on an error, or to simply hang up in a loop and wait for a reset.

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.

IMPORTANT NOTE

It is essential that FIRSTS, the two bytes which tell the super-boot where to find SK*DOS, be in location 0005 and nowhere else. The reason is that LINK, the program which is used to put this track - sector information into the super-boot, is written for location 0005. It will put the location of SK*DOS into byte 5 of track 0 sector 1, the first sector of the super-boot program on the disk.

SECTION V - CONSOLE / TERMINAL DRIVERS

SK*DOS has a dedicated area for console and terminal interface routines and pointers. This area extends from address $D370 through $D3FF. Actually, in most instances not all of this area is used, and so many users put parts of their disk interface routines into unused portions of this space.

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:
 

All remaining entry points are either vectored to an RTS or RTI instruction, or else referred to the monitor's SWI3 or IRQ vector address. SK*DOS itself does not generate or use interrupts, so any interrupts generated will have to come from and be processed by user programs.

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 NOTHING
Once 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.

SECTION VI - DISK DRIVERS

In order to be compatible with disk driver routines already written by many manufacturers, we have standardized on the placement of disk drivers as follows:

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:

The last step, check the write sector routine, is extremely dangerous, and should not be done on the SK*DOS master system disk. Many other disk computers use the Western Digital disk format, and it should not be difficult to obtain a number of formatted disks from some other computer to practice on. Initially, you should work with a single-density disk (which can be obtained from a TRS-80 model I and many other computers), and only proceed to double density (which can be obtained from a TRS-80 Color Computer or other machines) after the single-density routine is operating properly.

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.

Appending Drivers to SK*DOS

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. We will describe this a bit more here, but actually this cannot be done until you have the FORMAT program finished so that you can do this on a fresh disk, rather than writing on your SK*DOS master system disk and possibly ruining it.

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.

SECTION VII - THE FORMAT PROGRAM

Once you have SK*DOS up and running, the next step is to be able to format more SK*DOS disks and make backups or copies of your 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             1
This 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.

SECTION VIII - PRINTER DRIVERS

Printer drivers allow output redirection within SK*DOS by allowing program output to go to a printer rather than to the normal console terminal. This is done with a P.CMD program and, to a lesser extent, a PRINT.SYS program.

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.

SECTION IX - ADDING DATE AND TIME

This section describes how to use a clock/calendar chip with SK*DOS, if available in the system.

Automatic Insertion of Today's Date

Systems with a clock/calendar IC can get today's date from this IC, rather than asking the user for it at booting. The code which asks for the date consists of a subroutine which begins at address $CA02 and ends at approximately address $CA50. You may append your own subroutine (ending with an RTS instruction) to SK*DOS, occupying these addresses and starting at $CA02, to get the current date from another source. If more space is needed, you may continue within the console driver area or the disk driver area.

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.

Adding Time to Files

In systems having a clock/calendar IC, it is possible to configure SK*DOS so that a time code is added to each new file. This option is described under the TCAT command description in the User's Manual. We will not repeat the procedure here, but the  ADDTIME.TXT  file on the enclosed disk shows how we implemented this function for the 146818 clock chip on the PT-69 computer by Peripheral Technology of Marietta GA.
 

SECTION X - OTHER MATERIAL

This section describes other material which you should know, and yet which doesn't seem to fit into any of the other sections.

Appending Drivers to SK*DOS

This subject was discussed at the end of the disk drivers section, but it bears repeating at this point as well.

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.

Memory Used During Booting

Although the actual SK*DOS code occupies only addresses from $CD00-DFBB, and the cold-start address is $CD00, some additional memory is used during booting.

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:

Once this second initialization is performed, the JSR instruction which led to it is replaced by NOPs, so that any subsequent cold starts will no longer perform these steps.

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.

Setting the Maximum Value of MEMEND

As part of the initialization described above, SK*DOS sets the value of MEMEND. This is done by doing a quick memory test of all memory from location $C000 up to the value of MEMEND initially loaded from disk, and then assuming that memory ends with the last 4K block of memory successfully tested.

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.

Upper/Lower Case File Names

Commands and file names entered in SK*DOS may be entered in either upper or lower case, but are automatically converted to upper case. There are times, however, when it is desired to use lower case file names or extensions (such as when using C compilers). SK*DOS does allow that.

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.

RAM Disks or Disk Caches

In order to permit the orderly integration of RAM disks or disk caches, we have added an exit point in all versions of SK*DOS dated November 23, 1984 or later. Here is how it is done.

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 $D432
At address $D432, there are the three instructions
    RTS
    NOP
    NOP
Hence 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 return
2. 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!',4
AN 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.
 

Memory Conflicts

Memory conflicts are a frequent cause of problems in implementing a DOS. These are caused by the need to mesh the operation of several different programs:

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.

Interfacing to a Second Controller

This manual tells you how to interface SK*DOS to a new system. But suppose you have an SK*DOS which already works on your system, but just want to add a second disk controller or a hard disk? Is there an easy way of interfacing a second controller without starting all over?

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.

Other Changes

It is possible to make other changes to SK*DOS, and you may be tempted to do so. Remember, however, that any locations you change or refer to are subject to change in future versions. The only locations which are likely to remain constant from version to version are those referred to in either the User's Manual or this Configuration Manual.

NOTE

As you probably know, software and documentation are subject to constant change. The fifth revision of a program or manual is seldom the same as the first.

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.

---------------------------

Name:

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: