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.
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, B, and C) shall also be applicable to third parties purchasing such software from customer.
Wherever used in this manual, SK*DOS and HUMBUG are registered trademarks of Star-K Software Systems Corp.
Some earlier versions of 6809 SK*DOS are also known as STAR-DOS.
This is revision 1.16 of the manual, last revised on October 29, 1990.
APPENDICES
A., USER-ACCESSIBLE VARIABLES
B., THE FILE CONTROL BLOCK (FCB)
C., NON-DISK FUNCTIONS
D., DISK FUNCTIONS
E., SK*DOS ERROR CODES
F., DEFAULT EXTENSION CODES
G., SK*DOS COMMAND SUMMARY
H., ADDENDA AND OTHER INFORMATION
I., ASM - THE 68000/68010 ASSEMBLER
USER REGISTRATION AND UPDATE POLICY
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 possible, you should make a backup of the SK*DOS system disk before doing anything else. If your SK*DOS was supplied to you already configured for the disk controller and other hardware you now have, then making such a backup is easy; if your hardware is different from what SK*DOS needs then it is not.
Assuming that your hardware is capable of running this version of SK*DOS as is, here is what to do to get a backup.
(1) Make sure the original SK*DOS disk is write-protected. On a five-inch disk you should place the write-protect tab over the slot on one edge of the disk; on an eight-inch disk you should remove the write-protect tab from the disk.
(2) Place the SK*DOS disk into drive 0, close the door, and boot it.
(3) After SK*DOS is booted and you get the prompt, use the FORMAT command to initialize a blank disk in drive 1. See Appendix G for instructions on using FORMAT. Format the disk in double density, including track 0.
(4) Once a new disk is formatted, use the BACKUP command to copy the SK*DOS system disk onto the blank disk. (See Appendix G for instructions on BACKUP.)
(5) Make several such backup disks, and put the original SK*DOS disk away into a safe place.
In any case, it might be a good idea to read this entire manual before proceeding.
PLEASE NOTE: Several years of work and effort have gone into writing and improving SK*DOS. It pains us to see this work taken lightly by individuals who give away or sell bootleg copies of this software or this manual. Please don't do it.
This manual describes the latest additions to SK*DOS to adapt it to the WD37C65 floppy disk controller used in the PT68K-4 computer.
In order to boot SK*DOS from a floppy disk on this computer, you must first do an Initial Setup, using HUMBUG's IS command, to specify what kind of disk drive(s) you are using. If your computer has a battery-backed up static RAM, the initial setup configuration will be saved until you change it; otherwise, you will have to enter it each time you turn on the computer. (If you use the PT68K-4, you should therefore make sure you have a HUMBUG version 2.0 or later, which has the IS command.)
If you are using this new version of SK*DOS on an older computer (such as the PT68K-2, which does not have a WD37C65 controller or recent HUMBUG), it may happen that the system will not properly work if the initial setup locations of the RAM accidentally contain information which fools SK*DOS into thinking that you have this floppy disk controller. If that happens, use HUMBUG's FM command to zero out memory locations $FF0BE8 through FF0BFF. Normally, this will not be a problem since it is very unlikely that these locations will contain just the right information to confuse SK*DOS's disk drivers.
Chapter 14 gives further information on the disk drives and formats which are supported by this version of SK*DOS.
SK*DOS consists of three 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 routines 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.
In addition to the various commands supplied with SK*DOS, there are other programs available from other vendors which are designed to also work with SK*DOS. Furthermore, SK*DOS/68K floppy disks are compatible with those used by 6809 SK*DOS and Flex (a trademark of Technical Systems Consultants), so text and data files may be brought over from 6809 systems and used on your 68K system as well.
The interface between SK*DOS and user programs is similar to that standardized over a number of years on earlier 6809 systems, and is fully documented in this manual.
Since a DOS must be customized to run on a particular system, Star-K Software Systems Corporation, in conjunction with manufacturers who license SK*DOS, provides several different versions. Depending on the hardware configuration you specified with your order, you may already have received a version which is customized for your hardware, or you may have received a generic version which must still be adapted to your particular hardware. In general, you will have one of the following two files on your disk:
SK*DOS.SYS is a bootable version (which is started with the appropriate boot command of your monitor) and which is already configured for your hardware.
SK*DOS.COR is a generic version which lacks all console terminal and disk driver interfacing. In order to run this version, you will have to provide your own interface routines. An SK*DOS Configuration Manual, available from Star-K Software Systems Corp., describes the process of configuring SK*DOS for other hardware systems, and shows sample interfacing routines which can be used as a guide.
After you finish trying it out, we suggest you put it away and go back to reading this manual.
To start, make sure that your disk is write-protected and place it into drive 0. Read Section 0 for information regarding how to boot SK*DOS on your system.
The computer will load the program and respond with
WELCOME TO SK*DOS/68K (C) 1986, 1990 BY PETER A. STARK STAR-K SOFTWARE SYSTEMS CORP.
ENTER TODAY'S DATE (MM,DD,YY):Respond with the date, using one or two digits for the month and day, and two digits for the year, as in 9,26,82, and hit the ENTER key. (If your system has a calendar clock and it has been set to the correct date and time, SK*DOS may take its date from the clock instead of asking you to enter it.) You will now get the prompt
SK*DOS:SK*DOS is now running, and awaiting your further command. (You are looking at just the tip of SK*DOS - the part which is visible to the user. There is much more to SK*DOS than this, but this is the only part which you can see and experiment with without doing a bit more reading.)
You can now type in a variety of commands. Some commands will be immediately recognized by SK*DOS and acted upon; these are called 'memory resident' commands. Others are not recognized, and so SK*DOS will try to find them on the disk; these are called 'disk resident' commands.
To try a memory resident command, type the word
XEQand hit ENTER. XEQ tells SK*DOS to execute the last program loaded in by SK*DOS. Of course, we haven't yet used SK*DOS to load anything, and so we get an error message which reads
ERROR 28 - MISSING TRANSFER ADDRESSSK*DOS error codes are explained in Appendix E; in this case there is no transfer address so SK*DOS does not know what to execute.
Let us next execute a disk-resident command:
ACAT 0The ACAT command prints an alphabetized catalog of the disk or current directory. (ACAT or ACAT 0 means drive 0, ACAT 1 means drive 1, and so on). In response to the ACAT 0 command, SK*DOS loads the ACAT.COM program from the disk and executes it, displaying a catalog of the disk in drive 0. (To halt the listing, just press the ESC or escape key. Pressing ESC again will continue the catalog listing, or pressing CR or RETURN will return to the SK*DOS prompt.)
Once the ACAT command is finished, you may repeat the entire command by pressing control-A. The control-A displays the entire previous command line (ACAT 0) as if you had typed it again. At this point you may simply perform that command by pressing CR, or may backspace and change all or part of the line. For example, you may backspace to the 0 and replace it with a 1 (assuming you have a drive 1), thereby changing the command to ACAT 1. Pressing the CR now would result in a catalog display of the disk in drive 1.
Another way of repeating a command is by using the XEQ command. XEQ tells SK*DOS to execute the last program loaded by SK*DOS. In this case, the last disk-resident command loaded is ACAT, so XEQ repeats the ACAT command.
There are two interesting points to note:
1. Running ACAT again by typing XEQ is faster than typing ACAT again, because typing ACAT loads the program from disk and then executes it, whereas XEQ merely restarts the ACAT program without loading it again.
2. Pressing control-A repeats an entire previous command line, including any arguments typed as part of that line (for example, the 0 in ACAT 0 is the argument and specifies the drive number.) XEQ restarts a program but does not supply any arguments; these must be entered after the XEQ (as in XEQ 0).
It is now time to return to reading the rest of this manual, so type the memory resident command
MONThis command exits SK*DOS and returns to the monitor (assuming that your computer has one.)
The file-spec usually looks something like this:
0.A/FILENAME.EXTIn this example,
0 is the drive number. It is separated from the rest of the file-spec by a period, and is usually a number between 0 and 9, inclusive. The drive number (and its period) may not always appear; if it is missing, then SK*DOS uses one of two default values (which may both be the same): it uses the system drive default value when loading commands from a drive, and it uses the work drive default value for most everything else. The drive number may either precede the rest of the file-spec, as above, or may follow it.
A is the directory name. An SK*DOS disk has a main directory, sometimes called a root directory, and may have up to 26 smaller directories called subdirectories. The subdirectories are called A/ through Z/, while the root directory is either called / or is not specified at all. The directory name (and its slash) may not always appear; if it is missing, then SK*DOS uses either the system or work drive default directory.
FILENAME in the above example is a one- to eight-character long word which usually is chosen to identify the contents of the file. It must begin with a letter, and the remaining characters may be either letters, numbers, hyphens, asterisks ("stars"), or the underscore. The file name is the only absolutely required part of the file-spec in every instance.
EXT in the above example is a one- to three-character abbreviation which usually identifies the type of file. It is separated from the name by a period (and may also be followed by another period and the drive number). Like file-names, extensions must begin with a letter, and the remaining characters may be either letters, numbers, hyphens, asterisks ("stars"), or the underscore. Extensions are not always required if they are obvious, since SK*DOS programs default to certain extensions for certain commands.
Each of the four parts of the file-spec needs some further clarifying details:
1. SK*DOS differentiates between physical drive numbers and logical drive numbers. The physical drive number is the wired-in number determined by the hardware controller and the actual disk drive. For example, a given floppy drive may have its DS0 (drive select 0) jumper installed, which gives that drive the physical number of drive 0. This same drive also has a logical drive number, which may be different from the physical number. This assignment is handled by the DRIVE command, which may assign any of the ten logical drive numbers to any physical drive. The file-spec uses logical drive numbers, not physical drive numbers.
2. Floppy disks usually have only a root directory; most people use subdirectories only to split a large hard disk into smaller, more easily manageable sections. Each subdirectory might then be used for a specific type of file. (Incidentally, if you change the variable FNCASE to allow both upper and lower case file specs, you can then have a total of 52 subdirectories on a disk, called A through Z and a through z.)
3. There are several file names which are reserved for SK*DOS's own internal use. These fall into two categories:
a. The names DIR, GET, GETX, MON, RESET, SAVE, TRACE***, and XEQ are used for internal `memory-resident' commands. While these names may also be used for file names, any command files bearing these names will not be called since SK*DOS will use its internal commands instead.
b. The names CONS, PRTR, and NULL (and possibly a few others of your own choosing) apply to I/O devices rather than files. These names can also be used for file names, but in certain cases SK*DOS will interpret them as applying to a device rather than a file.
4. While files can have any valid extension, by convention certain kinds of files tend to have specified extensions. These extensions are listed in Appendix F. Most programs and commands default to specific extensions unless another extension is specified. For example, the EDLIN command is used to edit text files, and always assumes that its text files use a .TXT extension if none is given. The GET command, on the other hand, is always used to load binary files into memory, and so it automatically assumes a .BIN extension if none is given. You may provide an extension when you use these commands, but it is usually not necessary.
Unlike default extensions, which are built into commands and programs, default drive numbers and directories can be specified by the user by using the SYSTEM and WORK commands. When SK*DOS is first booted, it defaults everything to the root directory of drive 0. You may keep these defaults, but specify different drive numbers or directory names just when you need them, or you may set up a default drive or directory different from the root directory of drive 0, which will then automatically be chosen for most commands - unless you specify otherwise.
Here is a simple example. Suppose you wish to use the LIST.COM command to display a file called FILE.TXT. Let's assume that LIST is in directory C of drive 0, while FILE.TXT is in directory X of drive 3. One way to do this would be to give the command
SK*DOS: 0.C/LIST.COM 3.X/FILE.TXTBut since SK*DOS automatically defaults to .COM extensions for commands, while LIST defaults to .TXT extensions for text files, the command could have been shortened to
SK*DOS: 0.C/LIST 3.X/FILEIf you expect to use a lot of commands from drive 0 directory C, and perhaps a lot of text files from drive 3 directory X, then you could specify those as default values with
SK*DOS: SYSTEM 0.C/ SK*DOS: WORK 3.X/These two commands would tell SK*DOS to use 0 and C/ for loading its system commands, and to use 3 and X/ as the work drive and directory. Once you do this, then the command to list the file would simply be
SK*DOS: LIST FILESubdirectories are generally used to organize the contents of a hard disk. For example, you might put all assembler source files into the A/ subdirectory, put your Basic programs into B/, put C programs into C/, and so on.
Although it wastes space, it is possible to put separate copies of a file into more than one directory. For example, to place a copy of the above 3.X/FILE.TXT into subdirectory Y, you could simply use the COPY command as follows:
SK*DOS: COPY 3.X/FILE.TXT 3.Y/FILE.TXTTo move a file from one directory into another, you use the RENAME command. Thus the command
SK*DOS: RENAME 3.X/FILE.TXT 3.Y/FILE.TXTleaves only one file on the disk, but moves it from X/ to Y/. This is different from the COPY command, which would leave two copies of the same file on the disk, one in X/ and another in Y/. Note that in this case, changing one copy does not change the other.
1. The SK*DOS program itself.
2. A set of disk-resident commands such as CAT and LIST. The disk-resident commands are described later; this section deals with the SK*DOS program itself.
The SK*DOS program in turn consists of three parts:
1. The File Control System (or FCS for short) which maintains the disk directory and in general is responsible for managing the disk contents. The FCS has various routines which may be called by other programs; some of these routines actually handle the disk and its files, while other routines may handle peripheral functions (such as printing out strings, or converting numbers to and from decimal). These routines are used by the FCS, but they are documented in this manual and may also be used by application programs.
2. The Command Processor System (or CPS for short) which acts as an interface between a user and the FCS. When SK*DOS is first loaded and executed, the CPS prints the SK*DOS: prompt and awaits further instructions. These instructions may or may not involve the FCS.
3. The Basic Input/Output System (BIOS) which adapts SK*DOS to run on a particular hardware system. The BIOS contains the software which interfaces with the keyboard, display, printer, and disk drives.
The FCS and CPS parts of SK*DOS are the same on all systems; the BIOS portion must be tailored for each different computer. In some cases, the BIOS is provided either by Star-K or by a hardware manufacturer; in other cases, you may have purchased a more generic version of SK*DOS and will have to provide your own BIOS if your hardware is different from those systems currently supported.
Depending on the version, SK*DOS (with all its hardware-dependent routines) occupies approximately 24K to 32K of RAM. The exact location of SK*DOS varies from system to system, but in most systems it begins at $1000. Once booted, however, it is not necessary to know exactly where SK*DOS is located in your memory, since SK*DOS is called by application programs through `exception vectors'. Hence this manual describes the FCS and CPS portions of SK*DOS from the point of view of a user or applications programmer. BIOS information, needed only by systems programmers implementing SK*DOS on a new system, is provided in a separate (optional) Configuration Manual.
Commands typed into the command processor (and other input which may be entered in user programs) are stored in a line buffer (called LINBUF). If you type a control-A character while inputting a command or other input into the line buffer, SK*DOS will add the remaining contents of the line buffer to the input you have just typed and display it on your terminal. This is useful for repeating a command in its entirety, or repeating just parts of it. For example, suppose you have just completed the command
COPY 0.NAME1.TXT 1.NAME2.OLDand then, upon completion of that command, you type a control-A. SK*DOS will display the entire previous line again and position the cursor after the D in OLD. If you then press the RETURN key, you will perform the command again. Alternatively, you may backspace and change any part of the command. For example, if you backspaced to the beginning of NAME2 and typed in a new name such as NAME3, then the line would read
COPY 0.NAME1.TXT 1.NAME3At this point you could either press RETURN and execute the line as is, or again press control-A; the latter would complete the line by adding the .OLD at the end.
Whenever you enter a command line, SK*DOS will first check whether you have typed the name of a memory-resident command. If so, the command is immediately executed.
If no memory-resident command exists by that name, SK*DOS will try to find the command on the disk and execute it. Disk resident commands are program files which have a .COM extension.
If no such .COM file is found, SK*DOS makes one more try - to find a Batch File. A batch file is a text file (having a .BAT extension) which itself contains one or more other memory- or disk-resident commands which should also be executed.
For example, suppose a batch file called TWODIR.BAT has the following two text lines:
DIR 0 DIR 1Entering the command TWODIR would then display the disk directories of both drives 0 and 1.
If a command is given which is none of the above then SK*DOS will print a `file not found' error message.
Note that a .BAT file should never have the same name as a .COM file, since SK*DOS will find and execute the .COM file first, and never execute the .BAT file at all.
Memory-resident commands include the following; more detailed descriptions are provided in Appendix G:
DIR -- Display the contents (directory) of a disk
GET -- Load a binary file from disk into memory
GETX -- Load a binary file from disk into memory
MON -- Exit SK*DOS and return to a ROM monitor
RESET -- Exit SK*DOS and return to a ROM monitor
SAVE -- Save contents of memory to a binary disk file
TRACE*** -- Allow command tracing; see TRACENAB in Appendix G.
XEQ -- Execute the last file loaded from disk
There are some differences between GET and GETX, and between MON and RESET:
GET and GETX both load a binary file from disk into memory, but GET checks the loading addresses and will return an error message if the file would load outside the normal user-accessible memory (as defined by two variables called OFFSET and MEMEND.) GETX does not check such loading addresses.
MON and RESET both exit SK*DOS and return to a ROM monitor, but in some systems MON will re-enter the monitor without doing a complete system initialization; whereas RESET will completely initialize the system. This distinction is important because RESET will initialize all exception vectors to the ROM monitor's values, whereas MON may not. (In some systems, RESET may not be operational.)
Although DIR will display the contents of a disk, you may prefer to use some of the other disk-resident commands such as CAT, ACAT, SCAT, or TCAT, which provide more information than DIR.
Although, strictly speaking, Control-A is not a command, this may be a convenient place to discuss it. Pressing a control-A while typing any command will repeat the remaining portion of any previous command, display it on the screen, and ready it for execution. The control-A is a powerful feature, but it can also be misused if entered past the end of the last previous command.
Disk resident commands are supplied as binary, machine language files with .COM extensions. They are executed simply by typing their names. For example, the CAT.COM command may be executed just by typing CAT after the SK*DOS: prompt. This is equivalent to typing the sequence
SK*DOS: GET CAT.COM SK*DOS: XEQsince any unknown word (other than one recognized by SK*DOS as a memory resident command) is interpreted as calling a disk command and executing it. A .COM extension is assumed, and the program is automatically executed as soon as it is loaded.
Since SK*DOS automatically searches the disk for commands, it is possible for users to write their own Disk Resident Commands. Arguments to be used by the command can be entered on the same line as the command name, and then processed by the command with the aid of SK*DOS routines such as NEXTCH (get the next character from the Line Buffer). The listing of the LIST command, later in this manual, will show how additional commands can be written.
The disk-resident commands supplied with SK*DOS are described in Appendix G.
When reading a file, the FCS looks up the file location in the system portion of the disk (track number 0 on the disk) and then goes to read it. When writing a file to the disk, the FCS uses the system track to assign space to the file; when the file is written, the FCS updates the system track so that the file can later be found.
Fortunately, though this process is rather complex in any disk system, the user need not be concerned with how it is done, or where on the disk a given file is located. The SK*DOS FCS does all this automatically; the user need only give the FCS a file name and a command as to what to do. This is done by setting up a File Control Block or FCB for each file that is to be opened; a given program may use as many FCBs as desired. The FCB contains the file-spec, assorted flags and variables which are used by the FCS to keep track of the file, and also the data read from, or about to be written to, a single sector on the disk.
For example, to access the disk through the FCS to read text from a disk file, the sequence of operations would be something like this:
1. Set up a File Control Block with a DS instruction.
2. Point the A4 register to the FCB, and call the SK*DOS FCS system to input a file name.
3. Call SK*DOS again to assign a default extension, if needed.
4. Call SK*DOS a third time to open the file.
5. Call SK*DOS to read a byte from the file and process it.
6. Repeat step 5 as long as needed, then
7. Call SK*DOS to close the file.
All of these operations use the FCB as a buffer, both to hold the contents of an entire sector of data read from or written to the disk, as well as to keep track of the file name and location, and other pertinent data.
The FCB is discussed in the next Chapter.
The FCB is an area of memory 352 or 608 bytes long which must start on an even address. (Although only 352-byte FCBs are used at this time, users should set aside 608 bytes for each FCB so as to be compatible with future versions of SK*DOS.)
SK*DOS maintains several such FCBs for its internal use. One of these is called the User FCB, or USRFCB. It is available for use by other programs. Although it is used by SK*DOS, this is done in a way which does not prevent its use by those user programs which also require a FCB.
The FCB consists of 352 (or 608) bytes. Of these, the first 96 bytes are used for storage of various file parameters, while the remaining 256 (or 512) bytes hold the data for one sector of disk data. During a disk read operation, these bytes hold the contents of the last sector previously read; during a write operation, these bytes generally hold the contents of the next sector to be written.
Not all of the first 96 bytes are used; the following descriptions cover those bytes that are used in SK*DOS.
1 = open for sequential reading
2 = open for sequential writing
3 = open for updating, but no changes have been made to current sector
83 = open for updating, and changes have been made to current sector (this is hexadecimal 83)
Bits 0-3 - reserved for future use (leave at 0)
Bit 4 - will not be listed by CAT utility
Bit 5 - Reading not permitted
Bit 6 - Deletion not permitted
Bit 7 - Writing not permitted
This chapter documents these routines and shows how they are used. All of these routines are accessed through the `exception vectors' of the 68xxx processor.
68xxx processors have a number of `traps' which trap undefined or illegal operations, and cause a return to a supervisor or operating system via a set of exception vectors in low memory. A full description of this system is beyond the scope of this manual, and we suggest you get the Motorola literature for your processor, or one of the many textbooks on 68xxx programming, for more information.
One of the undefined or illegal 68K operations which causes a trap is any machine language instruction beginning with $A; Motorola literature refers to these as "Line 1010" instructions. Whenever any such instruction is encountered, the 68K CPU does a trap, via one of its exception vectors, to SK*DOS. Thus SK*DOS uses these instructions within user programs to call functions within SK*DOS.
Within SK*DOS, the second byte of each such instruction is used to select a particular function to be performed. For example, the instruction $A001 is used to read a byte from a file, $A002 writes a byte, and so on. A user program calls such a function with the instruction
DC $A0xxwhere the xx is simply replaced by the number of the function desired.
To avoid the necessity of remembering the numeric code for each particular function, the SK*DOS disk includes a file called SKEQUATE.TXT which provides a series of EQUates which define the exact numeric code for each function. Hence only the function name given in the following descriptions need be remembered. This file may be included as a library file in any user programs you write with the instruction
LIB SKEQUATEOnce so defined, the names in the SKEQUATE file can be used in the DC line, as in
DC FREADEach of the functions listed in this chapter always preserves registers D0 through D4, and A0 through A4, and generally never preserves registers D5 through D7 and A5 through A6. Arguments passed to SK*DOS are generally passed in D4 or A4, as applicable, and arguments passed back to the user program are generally in D5 or A5, as appropriate. In addition, all of the following functions always return with A6 pointing to the SK*DOS user- accessible variable area (see Chapter 11 and VPOINT for a fuller explanation.)
The functions listed in this chapter are divided into two groups:
A. Functions which do not involve reading or writing to the disk
B. Functions which do involve writing or reading disks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
D5: WWMMDDYY | | | +- year in hexadecimal | | +----- day in hex | +--------- month in hex +------------- day of week (00=none, 01=Sunday, 02=Monday, ...)
D6: 00HHMMSS | | | +- seconds in hex | | +----- minutes in hex | +--------- hours (24-hr time) +------------- always zeroIf no clock/calendar IC is available, then the day of week byte of D5, and all of D6, are zero, and only the date (month/day/year) is returned (obtained from the date typed in by the user upon booting).
NOTE: Each of the following Group B. functions requires that the calling program must point A4 to the FCB to be operated on, and then do a call to the appropriate function specified below. If the operation is finished without error, then FCS returns with the Z bit set. If an error is detected, then the Z bit is cleared and the error code is in byte 1 of the FCB. If no error is detected, this byte is not necessarily 0, and should not be tested unless the Z bit indicates an error. The normal call to these functions is thus
LEA ______,A4 Point to the File Control Block
DC <function> Call FCS to perform operation
BNE ERROR Go process error if detected
Note that it is required that A4 point to the beginning of the FCB when FCS is called. Since the contents of A0 through A4, and D0 through D4, are preserved upon return from the FCS, A4 will still be pointing to the FCB upon return.
If the FCS is called with an unimplemented operation code, the FCS will print out an error message and return to SK*DOS.
The following descriptions include typical error codes that may be generated on specific operations. In addition, most of the operations may also result in disk read or write errors due to hardware problems.
In all cases, if no error occurs, then the FCS returns with the Z bit of the condition code register set. If an error does occur, then the Z bit is cleared and byte 1 of the FCB (as well as location ERRTYP) contains the code for the error that occurred. Error codes are listed in Appendix E.
a. Use FRRECD to position to a particular sector of the file.
b. Use FRBACK to backup to the preceding sector.
c. Use FRGET to read a particular byte from the currently selected sector.
d. Use FRPUT to write a particular byte to the currently selected sector.
e. Use FREAD to sequentially read the sector, starting with the first. You may read as many bytes as there are in the file. (If executed after opening the file for update, FREAD will start reading at the beginning of the file.)
f. Use FRRECD to extend the file.
g. Use FCLOSE to close the file.
h. The only way to write past the end of a sector into the next sector is to use FRRECD to position to the next sector.
(See Chapter 13 for further information on random files.)
1. User variables which are often needed by application programs running under SK*DOS. These are described in this Chapter.
2. System variables which are generally needed only by systems programmers implementing SK*DOS on a new system, or modifying major operating parameters. A few of these are described in Chapter 13, but most such variables are described in the optional Configuration Manual, not in this Users' Manual.
The precise location of User variables may change between various versions of SK*DOS. Any call to an SK*DOS function, however, returns in A6 a pointer to the beginning of this variable area. In particular, the call to VPOINT (see Chapter 10) specifically exists to return the address of this variable area in A6. Each of the locations described below can be referenced using indexed addressing with reference to A6. This may be done either by referring to the numeric offset given in the descriptions below, or by using the SKEQUATE file on your SK*DOS disk as a library file in your assembly language programs as follows:
LIB SKEQUATE include as libraryYou may then refer to variables by their symbolic name. For example, the following two lines are equivalent:
LEA 0(A6),A4 Uses absolute offset LEA USRFCB(A6),A4 Uses offset defined in SKEQUATENOTE: In the following, the abbreviation DN stands for Device Number, and is used for those variables whose addresses are different for different devices. For example, PLINES is listed as 3322+80*DN(A6). For device number 0, the address is 3322(A6); for device 1 it is 3322+80*1(A6), or 3402(A6), and so on.
SK*DOS: LIST TEXTcommand, when the LIST program begins, the line buffer pointer LPOINT points to the first letter of the word TEXT. The LIST program can access the name with several subroutines, such as GETNAM. This is a convenient way of getting and passing arguments to programs directly from the command line.
Since user programs may change OFFSET as they run (to load another program into memory above themselves, for example), SK*DOS resets OFFSET to the value stored in OFFINI each time it does a warm start (it actually sets OFFSET to the next 256-byte boundary above OFFINI to make OFFSET a more convenient number.) OFFSET is thus only a temporary value, valid for the duration of any given program; OFFINI is more permanent.
SK*DOS: LIST TEXTThe program reads the file name from the line buffer, opens the file, and reads and prints one character at a time. The following listing shows how this is done.
* LIST UTILITY FOR SK*DOS / 68K * COPYRIGHT (C) 1986 BY PETER A. STARK * * EQUATES TO SK*DOS * * THE FOLLOWING WOULD NORMALLY BE IN SKEQUATE.TXT 00000001 FCBERR EQU 1 ERROR BYTE 0000A024 DEFEXT EQU $A024 DEFAULT EXTENSION 0000A008 FCLOSE EQU $A008 CLOSE A FILE 0000A005 FOPENR EQU $A005 OPEN FILE FOR READ 0000A001 FREAD EQU $A001 READ NEXT BYTE 0000A023 GETNAM EQU $A023 GET FILE NAME 0000A034 PCRLF EQU $A034 PRINT CR/LF 0000A037 PERROR EQU $A037 PRINT ERROR MSG 0000A033 PUTCH EQU $A033 OUTPUT NEXT CHAR 0000A01E WARMST EQU $A01E RETURN TO SK*DOS * 000000 ORG $0000 * 000000 6002 }000004 LIST BRA.S START GO TO START * 000002 0100 VER DC.W $0100 VERSION NUMBER * * START OF ACTUAL PROGRAM 000004 A034 START DC PCRLF START ON NEW LINE 000006 4201 CLR.B D1 PREV CHAR WAS NONE 000008 204E MOVE.L A6,A0 SAVE POINTER 00000A 284E MOVE.L A6,A4 POINT TO USER FCB 00000C A023 DC GETNAM GET FILE SPEC 00000E 640C }00001C BCC.S NAMEOK IF FILE NAME OK 000010 197C 0015 0001 MOVE.B #21,FCBERR(A4) ELSE IT'S ERR 21 * * ERROR ROUTINE * 000016 A037 ERROR DC PERROR PRINT ERROR CODE 000018 6142 }00005C BSR.S CLOSE CLOSE THE FILE 00001A A01E DC WARMST RETURN TO SK*DOS * * FILE SPEC WAS OK; DEFAULT TO .TXT 00001C 183C 0001 NAMEOK MOVE.B #1,D4 DEFAULT EXT 000020 A024 DC DEFEXT DEFAULT TO .TXT * * NOW ACTUALLY OPEN THE FILE 000022 A005 DC FOPENR OPEN FOR READ 000024 66F0 }000016 BNE.S ERROR IF NOT ZERO * * MAIN LOOP TO READ AND PRINT EACH CHARACTER 000026 2848 MAIN MOVE.L A0,A4 POINT TO SYS FCB 000028 A001 DC FREAD GO READ NEXT CHAR 00002A 670C }000038 BEQ.S CHAROK GO ON IF NO ERROR * * IF THERE WAS AN ERROR, SEE IF END OF FILE 00002C 0C2C 0008 0001 CMP.B #8,FCBERR(A4) END OF FILE? 000032 66E2 }000016 BNE.S ERROR NOT END OF FILE 000034 6126 }00005C BSR.S CLOSE CLOSE ON EOF 000036 A01E DC WARMST RETURN TO SK*DOS * * CONTINUE IF CHARACTER IS OK 000038 0C05 000A CHAROK CMP.B #$0A,D5 IS IT LINE FEED? 00003C 660A }000048 BNE.S PRNTIT NO, PRINT IT 00003E 1C01 MOVE.B D1,D6 YES, GET PREV 000040 1205 MOVE.B D5,D1 SAVE CURRENT 000042 0C06 000D CMP.B #$0D,D6 WAS PREV A CR? 000046 67DE }000026 BEQ.S MAIN YES, SO SWALLOW IT 000048 1205 PRNTIT MOVE.B D5,D1 SAVE CHARACTER 00004A 1805 MOVE.B D5,D4 READY FOR PRINTING 00004C A033 DC PUTCH AND PRINT IT 00004E 0C04 000D CMP.B #$0D,D4 WAS IT RETURN? 000052 66D2 }000026 BNE.S MAIN NO, SO CONTINUE 000054 183C 000A MOVE.B #$0A,D4 000058 A033 DC PUTCH ADD LINE FEED 00005A 60CA }000026 BRA.S MAIN AND ALSO CONTINUE * * CLOSE SUBROUTINE * 00005C 2848 CLOSE MOVE.L A0,A4 POINT TO FCB 00005E A008 DC FCLOSE CLOSE FILE 000060 4E75 RTS RETURN * END LISTThe above example shows a variety of techniques. Note especially how it checks for the end of file. When the read routine detects an error, the error code is fetched from byte 1 of the FCB and examined. If it is an 8 (end of file), then the program simply finishes up. If it is any other error, then it goes to an error routine.
Note also that the program is ORG'ed at $0000. When loaded, however, it will be relocated upward by the current value of OFFSET, so that it resides in memory just above SK*DOS.
* BUILD UTILITY FOR SK*DOS / 68K * COPYRIGHT (C) 1986 BY PETER A. STARK * * EQUATES TO SK*DOS * 00000001 FCBERR EQU 1 ERROR BYTE 000002F6 LPOINT EQU $2F6 LINE BUFR PTR * THE FOLLOWING WOULD NORMALLY BE IN SKEQUATE.TXT 0000A024 DEFEXT EQU $A024 DEFAULT EXTENSION 0000A009 FCSCLS EQU $A009 CLOSE ALL FILES 0000A00F FDELET EQU $A00F DELETE A FILE 0000A006 FOPENW EQU $A006 OPEN FOR WRITE 0000A002 FWRITE EQU $A002 WRITE A BYTE 0000A029 GETCH EQU $A029 GET CHAR 0000A023 GETNAM EQU $A023 GET FILE NAME 0000A02C INLINE EQU $A02C INPUT TEXT LINE 0000A037 PERROR EQU $A037 PRINT ERROR MSG 0000A035 PSTRNG EQU $A035 CR/LF AND STRING 0000A000 VPOINT EQU $A000 PT TO VAR AREA 0000A01E WARMST EQU $A01E WARM START * 000000 ORG $0000 * 000000 6002 }000004 BUILD BRA.S START * 000002 0100 DC.W $0100 VERSION * * ACTUAL START OF PROGRAM 000004 A000 START DC VPOINT GET POINTER 000006 204E MOVE.L A6,A0 SAVE POINTER 000008 284E MOVE.L A6,A4 POINT TO SYS FCB 00000A A023 DC GETNAM GET FILE SPEC 00000C 652E }00003C BCS.S ERROR ON ERROR * * IF NAME WAS OK, DO DEFAULT EXTENSION 00000E 183C 0001 MOVE.B #1,D4 CODE FOR DEFAULT 000012 A024 DC DEFEXT GO DEFAULT IT * * NOW OPEN FILE FOR WRITE 000014 A006 OPEN DC FOPENW OPEN FILE 000016 662A }000042 BNE.S OPENNG IF ERROR * 000018 49FA 005C}000076 NXTLIN LEA PROMPT(PC),A4 PRINT PERIOD 00001C A035 DC PSTRNG PRINT IT 00001E A02C DC INLINE INPUT A LINE 000020 2668 02F6 MOVE.L LPOINT(A0),A3 POINT TO NEXT 000024 181B MOVE.B (A3)+,D4 GET CHAR 000026 0C04 0023 CMP.B #$23,D4 CHECK FOR # 00002A 6712 }00003E BEQ.S QUIT YES, SO GO QUIT 00002C 2848 MOVE.L A0,A4 POINT TO FCB 00002E A002 NEXTL1 DC FWRITE GO OUTPUT CHAR 000030 660A }00003C BNE.S ERROR ON ERROR 000032 0C04 000D CMP.B #$0D,D4 END OF LINE? 000036 67E0 }000018 BEQ.S NXTLIN YES, START LINE 000038 181B MOVE.B (A3)+,D4 GET CHAR 00003A 60F2 }00002E BRA.S NEXTL1 AND REPEAT * * PROCESS ERRORS 00003C A037 ERROR DC PERROR 00003E A009 QUIT DC FCSCLS 000040 A01E DC WARMST * * ERROR HANDLING ON OPENING FILE 000042 0C2C 0003 0001 OPENNG CMP.B #3,FCBERR(A4) ALREADY EXISTS? 000048 66F2 }00003C BNE.S ERROR NO, REAL ERROR * * IF FILE EXISTS, DELETE IT? 00004A 49FA 002C}000078 LEA ASKDEL(PC),A4 00004E A035 DC PSTRNG ASK TO DELETE 000050 A029 DC GETCH GET ANSWER 000052 0205 00DF AND.B #$DF,D5 CVT TO UPPER CASE 000056 0C05 0059 CMP.B #$59,D5 IS IT YES? 00005A 66E2 }00003E BNE.S QUIT QUIT IF NOT 00005C 49FA 0052}0000B0 LEA ASKSUR(PC),A4 000060 A035 DC PSTRNG ASK IF HE'S SURE 000062 A029 DC GETCH GET ANSWER 000064 0205 00DF AND.B #$DF,D5 CVT TO UPPER CASE 000068 0C05 0059 CMP.B #$59,D5 IS IT YES? 00006C 66D0 }00003E BNE.S QUIT QUIT IF NOT * DELETE FILE IF OK WITH USER 00006E 2848 MOVE.L A0,A4 POINT TO FCB 000070 A00F DC FDELET DELETE THE FILE 000072 66C8 }00003C BNE.S ERROR ON ERROR 000074 609E }000014 BRA OPEN OPEN FILE AGAIN * * TEXT STRINGS 000076 2E PROMPT DC.B "." 000077 04 DC.B 4 000078 5448 4154 2046 ASKDEL DC.B "THAT FILE ALREADY EXISTS ... DO YOU WISH TO DELETE IT? " 0000AF 04 DC.B 4 0000B0 4152 4520 594F ASKSUR DC.B "ARE YOU SURE YOU REALLY WANT TO? " 0000D1 04 DC.B 4 * END BUILDAlthough it is not immediately obvious from the above examples, all user-written programs must be written in position - independent code (although see the description of the binary file format in Chapter 13 for possible exceptions.) To write position - independent programs for 68xxx processors, generally follow the following rules:
1. Do not use JMP and JSR instructions - use BRA and BSR instead. In general, there should be no JMPs or JSRs in your programs at all.
2. Refer to variables within your program using PC-relative addressing. For example, the instruction MOVE.B NUMBER(PC),D4 would move the quantity NUMBER into D4, but the (PC) tells the assembler to use PC-relative addressing. Unfortunately, the 68xxx does not allow PC-relative addressing as a destination; that is, the instruction MOVE.B D4,NUMBER(PC) is illegal. Hence this instruction has to be replaced by a two-instruction sequence such as
LEA NUMBER(PC),A5 MOVE.B D4,(A5)While this adds an extra instruction every time you store to a local variable, we suggest that you avoid the alternative shortcut of setting one address register to point to your data area and then doing all stores relative to that register. Although this makes your program a bit shorter and faster, it generates code which causes PICTEST to signal an error even though there is none. (PICTEST, explained later in the manual, is used to check a program to make sure it is position independent.)
NOTE to non - assembly language users: The assembly language
examples in this chapter are intended only as guides for those users who
intend to write their own assembly language programs. If you wish to try
them out, proceed as follows: (1) Type the command EDLIN SAMPLE to edit
a sample file. (2) When EDLIN returns with a # prompt, give it the I command
to start inserting text. (3) Enter the LIST program (the first program
in this chapter). Examine the listing to note that the first line of the
program begins with an asterisk; type in only the material to the right
of that column. For example, begin the first line with * LIST ...; begin
the 7th line with FCBERR EQU ... (4) When you finish typing in the program,
enter a # at the beginning of a new line, and then use the S command to
exit EDLIN. (5) Give the command ASM SAMPLE to assemble the sample from
assembly language to machine language. You will now see that the assembler
added all of the machine language code which you did not type in. (6) Assuming
there are no errors (correct the program if there are), then give the command
SAMPLE SAMPLE. The computer will then use the SAMPLE.COM file generated
by the assembler to print out the SAMPLE.TXT file you typed. SAMPLE.COM
does exactly the same thing as the LIST command supplied with SK*DOS, except
that it does not have the 'help' feature.
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 logical tracks (numbered 0 through 255) per drive and 256 logical sectors (numbered 0 through 255) per track. (For compatibility with 6809 SK*DOS, sector numbering begins with 1 for floppy disks.)
The exact positioning of those sectors and tracks is controlled by the disk drivers and FORMAT routine, not by SK*DOS itself. On floppy disks, the physical placement of these tracks and sectors on the disk would most likely agree with their logical numbering; on hard disks they might physically be placed elsewhere on the disk. That is why the previous paragraph uses the word logical in describing track and sector number - a logical address is where SK*DOS thinks the sector is located; the physical address is the actual location on the disk where the disk drivers place it.
Depending on the system, SK*DOS floppy disks may be either single- or double density, and single- or double-sided. In addition, double-density disks may have either a single- or double-density directory track. As long as disks are used only on a single system, the particular floppy disk format is not important.
Standard SK*DOS / 68K disks will generally be double density throughout, and may be single or double-sided. Disks intended to be interchanged with 6809 SK*DOS systems, however, should be formatted and used in single-density, single-sided format, since 6809 SK*DOS requires that track 0 always be in single density.
Each sector of an SK*DOS disk 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 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 1 and 2 pointer to next sector
Bytes 3 and 4 sector counter
Bytes 5 through 256 252 bytes of data
Some sectors have a slightly different format, and may omit the pointer or sector counter.
All the tracks on a disk can be used for storing data and program files except for track 0. The sectors on this track have special uses as follows:
Sector 1 on track 0 holds the super-boot program. This is a program which is loaded by the boot program in the system ROM monitor, and which in turn loads the rest of SK*DOS into memory. (This sector has 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.) On some systems, the boot procedure may be different, and so this sector may not be needed on those systems.
Sector 2 is often empty. It has been set aside as an extension of sector 1 in case more than 256 bytes are needed for booting.
Sector 3 is the System Information Sector or SIS. It contains the disk name and number, the date when the disk was formatted, the number of tracks and sectors on the disk, and three pieces of information about the free sector chain on the disk: the track and sector numbers of the first sector in the chain, the track and sector numbers of the last sector in the chain, and the total number of sectors in the chain.
Sector 4 is usually empty, although the COPY utility places a copy of the SIS into this sector to verify that the disk is available for writing.
Sector 5 begins the directory, which extends to the last sector of track 0. Each directory entry requires 24 bytes, so there is room for 10 entries in each sector with 16 bytes empty. For example, on a 5-14" single density, 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.)
No special character is used to denote the end of text, although the last line of text will generally end with a CR. Any space remaining in the last sector of a text file is filled with NULL ($00) bytes. When SK*DOS reads a space-compressed file, it does not return any NULL characters to the calling program; hence it will generate an end-of-file error immediately after the last character of the text.
Text files may consist of any characters except for NULL ($00) and TAB ($09).
A typical binary file will generally consist of several segments, each of which begins with an identification byte which describes what the segment consists of. There are ten such identification bytes:
$02 marks the beginning of a relatively short segment containing binary data to be loaded into memory. The $02 is followed by a two-byte load address, a one-byte count which specifies how many bytes are to be loaded, and a number of bytes equal to the count. The count is a number between 1 and 255, and the load address is a number between $0000 and $FFFF. During loading, SK*DOS adds the current value of OFFSET to load addresses specified in the file (unless the - option is used in the command line).
$03 is similar to $02 in that it also marks the beginning of data to be loaded into memory, but it is followed by a four-byte load address and a two-byte count. It is therefore used for memory addresses above $FFFF, or for loading data longer than 255 bytes (although such data is often split into a number of shorter $02 segments.) As with the $02 segment, SK*DOS adds the current value of OFFSET to load addresses specified in the file (unless the - option is used in the command line).
$16 marks the beginning of a two-byte transfer address; that is, the address where the file just loaded should be executed. The current value of OFFSET is added to the address specified in the file.
$17 marks the beginning of a four-byte transfer address, used if the transfer address is above $FFFF. The current value of OFFSET is added to the address specified in the file.
$04, $05, $18, and $19 are similar to $02, $03, $16, and $17, respectively, except that the current value of OFFSET is NOT added to the specified address in the file. Note, however, that load addresses are still checked against OFFSET and MEMEND limits unless the GETX command is used (see the descriptions of GET and GETX later in this manual). These four codes are provided for special applications, and should not normally be used as they may cause the system to crash in future multi-tasking versions of SK*DOS.
$0F and $10 are special codes used for programs which are not written in the normal position independent code (PIC). They are used to allow SK*DOS to modify an address while loading a position - dependent program. The $0F is to be followed by a single word address (relative to OFFSET), and the $10 is to be followed by a single long-word address (relative to OFFSET), which specifies the address of a long-word address which is to be modified by adding the current value of OFFSET to it. For example, suppose the current value of OFFSET is $5000 and the disk file contains the sequence $0F 12 34 or the sequence $10 00 00 12 34. Either of these two segments tells SK*DOS to add $5000 to the contents of address $6234 (which is presumed to have been previously loaded.) This code would normally be used only with assemblers or compilers which generate non - position - independent code.
With one exception, all of the loading and transfer addresses referred to above are merely relative addresses; they are added to the current value of OFFSET (see Chapter 11) when used. For example, if OFFSET currently has a value of $5000, and a file has a loading address of $1000 and a transfer address of $1004, then it will actually be loaded into memory at $6000 and executed starting at $6004. The exception is SK*DOS itself. Since SK*DOS is loaded by the bootstrap program at a time when OFFSET has not yet been defined, it contains absolute loading and transfer addresses rather than relative ones. Hence you can determine the absolute address where your SK*DOS is loaded into memory by examining the SK*DOS.SYS (or SK*DOS.COR) file with the LOCATE command (using the - option so the OFFSET is not added by LOCATE). Since COLDST is located at the very beginning of SK*DOS, this method is also used to find the address of COLDST in your system.
Note that segments do not contain any kind of checksum; it is assumed that any disk errors will be caught by CRC or other error checking in the disk hardware or drivers. As in text files, the last segment of a binary file is generally followed by NULLs so as to fill out the last sector of a file.
As pointed out earlier, the third and fourth bytes of a data sector contain a two-byte (four hex-digit) sector count which numbers the sectors of a file. For example, the first data sector of a file has the number 0001, the second is numbered 0002, and the seventh would be numbered 0007. Don't confuse these numbers with the physical location of the sector on the disk, which is sometimes called the sector's disk address, and which consists of a track number and a sector number. The sector count could be used to make sure we read the sectors of a file in the right order, but in practice is almost never used with sequential files. Though we have used the term sector count above, in the rest of this discussion we will simply call it the sector number. Some people also call it the record number, but this is a bit confusing since the word record can also be applied to a part of a sector.
Although sequential files are most common, we often need random or direct access files. These are files which allow us to read or write data in the middle of a file without necessarily reading or writing all the data before it. For example, at some point we might need to read data located in sector 0007, followed by sector 0104, followed by sector 0025, and so on. This is accomplished by the random file capabilities of SK*DOS.
In order to allow us to rapidly locate a particular sector number in a file, without reading all the sectors before it, SK*DOS provides for a special random file format which contains an extra two sectors. These two sectors, which are always at the very beginning of a random file, contain a file map of the file, which maps out the placement of the file on the disk and helps us to find a specific sector. These two sectors always have a sector number of 0000.
Thus the very first data sector of any file is always sector number 0001, but in a sequential file this is the first actual sector of a file, whereas in a random file it is actually the third sector. (If you try to do a sequential read of a random file, SK*DOS skips the first two sectors and so you will never know they are there.)
Few application programs actually need random files, but if they do, they will take care of generating and manipulating them automatically, through SK*DOS. Nevertheless, here is information on how this is done.
There is only one way to generate a random file:
1. Open a file for writing with FOPENW.
2. After opening, but before writing anything to it, change byte 26 of the FCB to a non-zero number.
3. Now write sequentially to the file using FWRITE.
4. When done, close the file with FCLOSE.
SK*DOS will automatically put a two-sector file map at the beginning of the file, and will update it as data is written to the file. Note that the file map is customized for the particular placement of the file sectors on the disk. If you copy a file from one disk to another, the two file map sectors will be different since the copy will most likely be in a different place on the disk. But you need not concern yourself with this since SK*DOS does this automatically.
Once the file is written, it can be read or updated (the data in it can be modified), or lengthened. But random files are usually made oversize to begin with, so there is room for adding more data at a later time without increasing the file size later.
The file can be read sequentially just like any sequential file. If it is opened (with FOPENR) and then read (with FREAD), it will look like any sequential file, since SK*DOS will automatically skip the file map sectors and read only the data sectors.
Things are a bit more complex - and interesting - if the file is opened for updating. We now have a number of options, which are listed in the description of the Open for Update operation, FOPENU. The most important concept is that, by use of FRRECD, we can locate any sector number (that is, a sector with a desired sector count) in the file in a short time. For example, we can tell SK*DOS to read sector number 0104 of a file. Given a sector number, SK*DOS will look it up in the file map, calculate the exact location of that sector, and then go directly to it.
Once we have located the specific sector, we can specify an exact byte of that sector, and either read it (with FRGET) or write it (with FRPUT). Knowing that there are 252 data bytes in each sector, we could thus locate any particular byte in a file after some fairly simple calculations. For example, to locate byte N in a file, we could use the following two BASIC lines:
Sector number = INT(N/252) + 1 Byte number = N - INT(N/252)*252 + 4N in these equations is assumed to start with 0; the 4 in the second equation is due to the fact that the first data byte in a sector is actually numbered 4. For example, the 253rd byte in a file (which would actually be numbered 252) would be byte number 4 (the first byte) in sector 0002.
When SK*DOS is initially booted, it is loaded with initial values of OFFINI and MEMINI. It first checks memory, starting with OFFINI up through the value of MEMINI, looking for a memory address which fails to store a memory pattern stored into it, or which generates a bus error. Once such an address is found, SK*DOS stores into MEMEND the top address of the last 4K block of memory found to be working. This value of MEMEND then remains there unless changed by user programs; SK*DOS does not itself change it at any time (although programs such as RAMDISK may.)
Following the above, at every warm-start SK*DOS sets OFFSET to the next even 256-byte boundary above the current OFFINI. Since OFFINI at boot-up normally points just above SK*DOS and its drivers, OFFSET is thus initialized to the next 256-byte boundary above SK*DOS.
The system stack pointer is initialized at COLDST, with the user stack pointer initialized at COLDST-$0200. For example, if COLDST is at $1000, then the user stack will go from $0E00 down, while the system (DOS) stack will go from $1000 down. The system stack will never extend down as far as the user stack, so data on the user stack is preserved during DOS calls. Between them, OFFSET (through OFFINI) and MEMEND (through MEMINI) therefore delimit the current user memory.
User programs (and SK*DOS utilities) can subsequently change these values to reserve memory for themselves. A program which wants to reserve permanent memory for itself (such as a RAM disk) can be loaded into memory at OFFSET, and then point OFFINI above itself, or can move itself to the top of memory and then point MEMEND just below itself. Either way, its memory will then be protected permanently, since SK*DOS does not itself move these boundaries.
It is also possible to reserve memory just temporarily; for example, a program might be needed only for a while. Such a program can then be loaded into low memory, and OFFSET adjusted to point to an even address above the program. OFFSET will then remain at this value only until the next warm start, at which time it will go back to the previous value as determined by OFFINI. (Note that OFFSET must always be even.)
1. The program can be placed at the bottom of user memory and then OFFINI set to point above the program's top memory address, or
2. The program can be placed at the top of user memory and then MEMEND set to point just below the program's bottom address, or
3. The program can be placed into some other memory area which SK*DOS does not use and does not know about.
To add the command to SK*DOS's command list, a table of command names and starting addresses must be placed somewhere in memory and identified to SK*DOS. The following is an example of such a table:
CMDTAB DC.B 'DIR' Command name DC.B 0 delimiter to signal end of name DC.L DIR Starting address of command DC.B 'GET' Command name DC.B 0 delimiter to signal end of name DC.L GET Starting address of command . . . . . . DC.B 0 End of table flagFinally, to tell SK*DOS where this table is located, its starting address (i.e., the address of the first DC.B) must be placed as a long word into location COMTAB.
(The astute reader may note that, depending on the length of the command name, the assembler may insert an extra empty byte between the DC.B command name delimiter and the DC.L which holds its address so as to make sure that the DC.L starts on an even address. This is irrelevant to SK*DOS.)
TRACE*** is generally used just before you load in and execute a new program being tested. It sets the trace bit in the user status register, so that SK*DOS enters the new program in the trace mode. The 68xxx CPU will then execute the first instruction and trap to the ROM monitor, generally to display a register dump. You may then use the facilities of the monitor to trace further, insert breakpoints, or do other debugging.
When using TRACE*** with HUMBUG, you must first execute the TRACENAB command, which tells HUMBUG that you will be using TRACE*** and initializes one of its memory locations. In a few instances, that may not work; the easiest solution is then to exit SK*DOS with the MON command, type the HUMBUG command ST 1000 to execute one (harmless) trace instruction, and then return to SK*DOS with the RD command. This sets the HUMBUG registers properly.
Bytes 16-26 Disk name (and extension)
Bytes 27-28 Disk number
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-37 Month, day, and year of disk creation
Byte 38 Number of logical tracks on the disk - 1
Byte 39 Number of logical sectors per track
Within each sector, the first two bytes are a link pointer and the next 14 bytes are empty (filled with zeroes); the remaining 240 bytes are split into ten groups of 24, with each set of 24 bytes being one file entry. These bytes are used as follows:
Bytes 0-10 File name and extension
Byte 11 File attribute
Byte 12 File protection
Bytes 13-14 Track and sector number of first sector
Bytes 15-16 Track and sector number of last sector
Bytes 17-18 File size in sectors
Byte 19 Sequential/random flag
Byte 20 Time of file creation / update
Bytes 21-23 Month, day, year of file creation/update
The first character of the file name (byte 0) is replaced by $FF when a file is deleted, but the remaining bytes are unaltered. Hence the CAT command (using its N option) can display data on deleted files. When the directory is first established, all bytes in the empty directory are written as zeroes; hence when SK*DOS searches the directory for a file, it stops searching when it gets to an entry whose first character is still 00.
Although logically an SK*DOS disk can hold one root directory and 26 (or more) subdirectories, physically all of the files are listed in one master directory, which is stored as a flat file as described above. Within this directory, files are coded as to which subdirectory (A/ through Z/) they belong into by storing the directory code in bit 7 of bytes 0 through 7 of the directory entry.
As with all ASCII text in SK*DOS, file names are stored as 7-bit ASCII characters, with the left-most, eighth or parity bit, normally a 0. Bit 7 of bytes 0-7 (the eight bytes of the file name) would therefore normally be zeroes. Instead, they are now used to hold the directory code letter. For example, here are some sample file-name bytes:
File TEXT in the root directory:
54 45 58 54 00 00 00 00 <<- ASCII for "TEXT"
0 0 0 0
0 0 0 0 <<- parity bits; 00 means
root
File TEXT in directory U/:
54 C5 58 D4 00 80 00 80 <<- ASCII for "TEXT"
0 1 0 1
0 1 0 1 <<- parity bits; $55
means "U"
While at first glance this seems like an awkward way of coding subdirectories,
in practice just a few extra instructions are required to process these
parity bits. SK*DOS routines GETNAM and FNPRNT do the processing automatically
for input and output of file specifications. The advantages, on the other
hand, greatly outweigh the disadvantages: the directory structure is totally
compatible with earlier versions of SK*DOS (or Flex), the same routines
which differentiate between file names also diferentiate between directory
names without any extra programming or time, it becomes easy to move a
file from one directory to another without rewriting it, the directory
remains a manageable size, and for most situations, the entire directory
for an entire disk is still contained in just one track, thereby minimizing
disk access time.
Before continuing, it is important to explain our use of the word device. In SK*DOS, a device is any I/O port other than a disk drive or RAMDISK. This includes character-oriented devices such as the console, terminals, modems, or printers. For the sake of the discussion in this chapter, we will differentiate between disks and devices, even though many people would classify a disk as one type of device. In fact, SK*DOS allows devices and disks to be treated in similar ways. Nevertheless, for our purposes it is easier to separate the concepts.
CATnormally displays a catalog of a disk on the screen. On the other hand, the command
CAT >CATFILEsends the catalog listing to a diskfile called CATFILE.PIP, rather than displaying it on the screen. Furthermore, the command
CAT >PRTRwould send the catalog listing to the printer (if a PRTR printer driver is installed; otherwise it will go to a disk file called PRTR.PIP. You can also force output to a PRTR file, even when a PRTR device is installed, simply by including the extension.)
Input redirection is handled with the << symbol. For example, the command
BUILD FILEis generally used to input text from the keyboard into FILE. The command
BUILD FILE <ANOTHERwould also send text to FILE, but would take the text from another file called ANOTHER.PIP. Alternatively. if there is a device called COM1 on the system, then
BUILD FILE <COM1would take the text from this input device.Think of < and > as being arrows. The > in >FILE points to FILE, so data goes to FILE, whereas <FILE points away from FILE, so data comes from FILE. Note also that file names used in redirection default to .PIP extensions (which stands for `pipe'), though this can easily be changed by specifying a different extension.
When SK*DOS is initially booted, it has just two devices; these are called the `default' devices:
CONS is the console keyboard and screen. It is used for normal input and output.
NULL is a `null device' which is used when you want to do a function but want no output whatsoever. For example, the command
ASM PROG >NULLwould assemble a file but provide absolutely no output - not even assembler error messages.
Even though it starts with just two devices, SK*DOS can have up to eight. The others must, however, be specifically `installed', either by a command from the keyboard, or by commands included in a STARTUP.BAT file, by reading in `device drivers' from a disk. If you wish, you can substitute other devices instead of the default ones as well.
Normal Device Device Driver use number name Terminal 0 CONS Default driver Error device 1 CONS Default driver Printer 2 CONS Default driver 3 CONS Default driver 4 CONS Default driver 5 CONS Default driver 6 CONS Default driver Null device 7 NULL Default driverThis tells us that devices 0 through 6 are currently the CONS console default driver, while device 7 is the NULL default driver. Furthermore, it also tells us that device 0 is normally the terminal (which is used to control SK*DOS), device 1 is the error device (where most error messages go), device 2 is usually the printer (although right now printer output would go to the console instead), and device 7 is usually the null device.
As you can see, at this point CONS has several device numbers. That means that output sent to any one of those numbers would really go to the console.
The assignments shown in the above tables can be changed at any time by using the DEVICE command. DEVICE is most often used to substitute a disk-resident device driver for one of the default drivers.
Device drivers must be `installed' with the DEVICE command. For example, the command
DEVICE PARALLEL AT 2 AS PRTRwould install a driver called PARALLEL.DVR at device 2, and give it the name PRTR. The DEVICE display would then say
Normal Device Device Driver use number name ------ ------ ------ --------- . . . . Printer 2 PRTR PARALLEL.DVR . . . .The DEVICE command can be used to change devices as often as desired; you may also return back to a default driver by using the name DEFAULT instead of a file name, as in
DEVICE DEFAULT AT 2 AS CONSwhich would restore device 2 as the default device CONS. Note that driver names default to .DVR, but DEFAULT has no default extension: DEFAULT refers to the normal default driver, whereas DEFAULT.DVR would be needed in the command line if you had an actual DEFAULT.DVR driver on a disk.
NOTE: The remainder of this chapter will probably only be of interest to advanced programmers.
This is important to remember for several reasons. First, it means that new drivers can only be installed from the keyboard (or from a .BAT file), not from another program, because memory may not be available for the new driver while another program is running. Second, it means that each copy of a driver maintains its own variables such as PLINES (see below) even when it applies to the same hardware device.
Each device number has an 80-byte device descriptor within the table. These bytes contain the following:
Bytes Description
00-03 Logical name, such as CONS or PRTR
04-07 Pointer to first address of the driver, 0000 if in BIOS
08-11 Length of the driver in bytes, 0000 if in BIOS
12-15 Pointer to driver initialization routine
16-19 Pointer to input status check routine in driver
20-23 Pointer to get input character with echo routine
24-27 Pointer to get input character without echo routine
28-31 Pointer to input channel control routine (for ICNTRL)
32-35 Pointer to output status check routine
36-39 Pointer to output character routine
40-43 Pointer to output chan control routine (for OCNTRL)
44 Print lines (PLINES) constant
45 Page width constant (PWIDTH)
46 Null wait constant (NULLWT)
47 Skip lines constant (SLINES)
48 Pause flag (PAUSEB)
49 Line counter counts lines per page
50 Column counter (OCOLUM)
51 Serial device baud rate (BAUDRT)
52 End-of-file character (EOFILC)
53 X-Off character (XOFFC)
54 X-On character (XONC)
55 Reserved for future use
56-59 Pointer to input status check routine (bypass typeahead)
60-63 Pointer to get input char w/o echo routine (bypass typeahead)
64-67 Pointer to routine to flush typeahead buffer
68-79 Reserved for future use
All of the above pointers and numbers are distinct for each device number. The constants from PLINES down can be displayed or changed with the DOSPARAM command.
I/O redirection is done by programs in a totally different way (since programs cannot install new drivers). In general, there are several methods available to programs for accessing different I/O devices. Moreover, programs can access devices through file control blocks, or can access files through device numbers.
1. Normal console I/O functions such as PUTCH, GETCH, PSTRNG, INLINE, OUT5D, HEXIN, and the like, are all steered through DEVIN in the case of input functions, or DEVOUT in the case of output functions. Programs can change DEVIN, or DEVOUT to different device numbers to use different devices for these functions. For example, when a program wants to output to a printer, it can change DEVOUT from 0 to 2 - assuming that a PRTR or similar driver is installed. If not, then output will still go to the console. It is also possible to input from one device but output to another by changing DEVIN and DEVOUT accordingly.
2. An indirect way of changing DEVIN and DEVOUT is through ICNTRL and OCNTRL calls $FFFx (see the next section.)
3. PERROR output is done via DEVERR, and programs can change this byte to steer error output to different devices.
4. All of the above can also be sent to a disk file, or input from a disk file, by opening the appropriate file and placing the FCB address into FIADDR (for input) or FOADDR (for output), and then setting DEVIN or DEVOUT to device number 8. Note that physical I/O devices are only numbered 0 through 7; number 8 applies only to disk files. If the file is not open, or if SK*DOS encounters an error while using the file, it will print an error message, reset the device number to 0, and continue using device 0 for input or output. (You may then use the EOFILC, usually control-Z, to indicate an end of file on input.)
5. Conversely, an FCB can be used to access an I/O device simply by using the four-letter device name when opening the file. Just be careful not to try to input from an output device such as a printer.
The process would go like this: First place a four-letter device name into the name bytes of the FCB. Do not use an extension or SK*DOS will assume you mean a file, although you should call DEFEXT to put in a default extension just in case (DEFEXT will not add an extension if it detects that a device exists with the specified name.) Then call FOPENR or FOPENW to open the file for reading or writing. If the specified driver does not exist, SK*DOS will open the file normally (that's why it is good to have a default extension). If it does exist, then SK*DOS will use the device for subsequent reads or writes instead of a file. Note that the device can only be used sequentially - random file operations will not work and may give undesired results.
SK*DOS accesses devices through an FCB by substituting `fake drive numbers'. Normally, only drive numbers 0 through 9 are valid disk drive numbers; when you open an FCB to a device, SK*DOS uses drive numbers $10 through $17 to refer to devices 0 through 7 respectively. If you know the name of a device but not its number, then it is easiest to open the file with that name. If you already know the number, then it is not even necessary to open the file - just set up an FCB, put in a drive number equal to $10 plus the desired device number, and use FREAD or FWRITE to read or write.
6. Before doing any of the above, a program may check whether a given driver is installed by using FINDEV.
7. GETNAM recognizes device names and substitutes the device number plus $10 when the specified device is installed.
MOVE.B #$10,D4 DC ICNTRLthe value of $10 is passed through SK*DOS to the ICNTRL entry point of the appropriate driver.
The need for ICNTRL and OCNTRL is based on the need for consistency when SK*DOS is implemented on a variety of very different computers. Some of these use conventional terminals, but some (such as the Atari or Amiga 68000 computers) will have built-in video and graphics interfaces. Since each of these provides different output modes and screen display codes, it is important to standardize input and output so that a given program may run on any of these and still provide a common output format.
All 68K SK*DOS calls to GETCH and PUTCH (as well as related calls such as PNSTRN or INLINE) are sent to the specified driver routine through a portion of SK*DOS called IOSEL or I/O Selector. As explained above, the usual device assignments are
0 - console (both keyboard and screen)
1 - error device (usually also the console)
2 - printer
3-6 - user-defined
7 - the `null' device
One function of ICNTRL and OCNTRL is to choose which driver is active at any time. When you call ICNTRL or OCNTRL with the word $FFFx in D4, this selects driver x for input or output, respectively. All following I/O calls via PUTCH, GETCH, etc., (as well as ICNTRL or OCNTRL) are then vectored to that driver until you change the driver assignment with another call to ICNTRL or OCNTRL (or changed DEVIN or DEVOUT). On initial cold start, 68K SK*DOS initializes both input and output to $FFF0 so that the system defaults to using device 0 - the console keyboard and screen. Hence the casual user need not generally be concerned with ICNTRL or OCNTRL.
The second function of ICNTRL and OCNTRL is to pass special arguments to the selected device, or input special key characters from the device. The important requirement is that all SK*DOS users agree on these arguments, so that all drivers and all I/O devices will respond in the same way, regardless of which computer is being used. The following sections describe this feature.
$0000 Return number of current driver (0-7) in D5
0001 Return the name of current driver (such as CONS) in D5
0002 Return a raw 8-bit character from the device
0003 Enable keyboard's function keys
0004 Disable keyboard's function keys
0005 Return a special character from the device
FFFx Switch to driver x
Additional other commands may be defined in the future.
Some device drivers will contain an input translation table and code which allows the driver to convert special key characters or sequences into a single byte which will be returned in D5. For example, suppose a given terminal has a row of ten function keys, which generate a two-byte sequence such as "ESCape followed by $30" for key F0, and so on. Using GETCH, we would get back two separate characters, a $1B for the ESCape, and a $30 for the 0. The problem here is that we have no way of knowing whether this sequence came from function key F0, or whether the user really typed an ESCape and then the digit 0. (The command 0002 of ICNTRL would return the same two characters, but with the parity bits intact, if any.)
ICNTRL command 0005 works a bit differently. After it detects the ESCape code, ICNTRL waits for approximately one-half character time. If it receives the $30 during that time, then it assumes that the combination came from a single F0 function key, and returns a special code which signifies the F0 key. If the $30 is not received during that time, then ICNTRL returns the ESCape first, and then picks up the next character on the next pass. (The next character may also be returned by GETCH). (This description assumes a serial terminal keyboard; computers with an integral keyboard may return a special function key code directly.)
In order to generate the same special F0 code with different terminals or computers, the device driver has a translation table which converts any specific combination of one or more keys into an F0 code which would be common to all systems. The supplied ADM-3A driver shows how this is done.
When ICNTRL command 0005 receives a regular ASCII character, it simply returns it as a single byte in D5, with bits 8-15 of D5 equal to 0. But a special keyboard character is identified by making bits 8-15 non-zero; in other words, special characters are represented by the words $0100 and higher.
The following table shows the key codes for an implementation using a PC-compatible keyboard:
Key No. | Key | Normal | Shift | Control | Num Lock |
1 | ESCAPE | 1B/001B | 1B/001B | 1B/001B | * |
2 | 1 ! | 31/0031 | 21/0021 | --/0431 | * |
3 | 2 @ | 32/0032 | 40/0040 | 00/0100 | * |
4 | 3 # | 33/0033 | 23/0023 | --/0433 | * |
5 | 4 $ | 34/0034 | 24/0024 | --/0434 | * |
6 | 5 % | 35/0035 | 25/0025 | --/0435 | * |
7 | 6 ^ | 36/0036 | 5E/005E | 1E/001E | * |
8 | 7 & | 37/0037 | 26/0026 | --/0437 | * |
9 | 8 * | 38/0038 | 2A/002A | --/0438 | * |
10 | 9 ( | 39/0039 | 28/0028 | --/0439 | * |
11 | 0 ) | 30/0030 | 29/0029 | --/0430 | * |
12 | - _ | 2D/002D | 5F/005F | 1F/001F | * |
13 | = + | 3D/003D | 2B/002B | --/043D | * |
14 | Backspace | 08/0008 | 08/0008 | 7F/007F | * |
15 | TAB | 09/0009 | --/021F | --/031F | * |
16 | q Q | 71/0071 | 51/0051 | 11/0011 | * |
17 | w W | 77/0077 | 57/0057 | 17/0017 | * |
18 | e E | 65/0065 | 45/0045 | 05/0005 | * |
19 | r R | 72/0072 | 52/0052 | 12/0012 | * |
20 | t T | 74/0074 | 54/0054 | 14/0014 | * |
21 | y Y | 79/0079 | 59/0059 | 19/0019 | * |
22 | u U | 75/0075 | 55/0055 | 15/0015 | * |
23 | i I | 69/0069 | 49/0049 | 09/0009 | * |
24 | o O | 6F/006F | 4F/004F | 0F/000F | * |
25 | p P | 70/0070 | 50/0050 | 10/0010 | * |
26 | [ { | 7B/007B | 5B/005B | 1B/001B | * |
27 | ] } | 7D/007D | 5D/005D | 1D/001D | * |
28 | Return | 0D/000D | 0D/000D | 0D/000D | * |
29 | Control | --/---- | --/---- | --/---- | --/---- |
30 | a A | 61/0061 | 41/0041 | 01/0001 | * |
31 | s S | 73/0073 | 53/0053 | 13/0013 | * |
32 | d D | 64/0064 | 44/0044 | 04/0004 | * |
33 | f F | 66/0066 | 46/0046 | 06/0006 | * |
34 | g G | 67/0067 | 47/0047 | 07/0007 | * |
35 | h H | 68/0068 | 48/0048 | 08/0008 | * |
36 | j J | 6A/006A | 4A/004A | 0A/000A | * |
37 | k K | 6B/006B | 4B/004B | 0B/000B | * |
38 | l L | 6C/006C | 4C/004C | 0C/000C | * |
39 | ; : | 3B/003B | 3A/003A | --/043B | * |
40 | ' " | 27/0027 | 22/0022 | --/0427 | * |
41 | ` ~ | 60/0060 | 7E/007E | --/0460 | * |
42 | Left Shift | --/---- | --/---- | --/---- | --/---- |
43 | | | 5C/005C | 7C/007C | 1C/001C | * |
44 | z Z | 7A/007A | 5A/005A | 1A/001A | * |
45 | x X | 78/0078 | 58/0058 | 18/0018 | * |
46 | c C | 63/0063 | 43/0043 | 03/0003 | * |
47 | v V | 76/0076 | 56/0056 | 16/0016 | * |
48 | b B | 62/0062 | 42/0042 | 02/0002 | * |
49 | n N | 6E/006E | 4E/004E | 0E/000E | * |
50 | m M | 6D/006D | 4D/004D | 0D/000D | * |
51 | , < | 2C/002C | 3C/003C | --/042C | * |
52 | . > | 2E/002E | 3E/003E | --/042E | * |
53 | / ? | 2F/002F | 3F/003F | --/042F | * |
54 | Right Shift | --/---- | --/---- | --/---- | --/---- |
55 | Prt Scr * | 2A/011D | 2A/002A | --/031D | 2A/002A |
56 | Alt | --/---- | --/---- | --/---- | --/---- |
57 | Space | 20/0020 | 20/0020 | 20/0020 | * |
58 | Caps Lock | --/---- | --/---- | --/---- | --/---- |
59 | F1 | --/0101 | --/0201 | --/0301 | * |
60 | F2 | --/0102 | --/0202 | --/0302 | * |
61 | F3 | --/0103 | --/0203 | --/0303 | * |
62 | F4 | --/0104 | --/0204 | --/0304 | * |
63 | F5 | --/0105 | --/0205 | --/0305 | * |
64 | F6 | --/0106 | --/0206 | --/0306 | * |
65 | F7 | --/0107 | --/0207 | --/0307 | * |
66 | F8 | --/0108 | --/0208 | --/0308 | * |
67 | F9 | --/0109 | --/0209 | --/0309 | * |
68 | F10 | --/010A | --/020A | --/030A | * |
69 | Num Lock | --/---- | --/---- | --/---- | --/---- |
70 | Scroll Lock | --/011E | --/021E | --/031E | * |
71 | Home | --/0120 | 37/0037 | --/0320 | 37/0037 |
72 | Up Arrow | 0B/0125 | 38/0038 | --/0325 | 38/0038 |
73 | Pg Up | --/0123 | 39/0039 | --/0323 | 39/0039 |
74 | Grey Minus | 2D/002D | 2D/002D | --/032D | 2D/002D |
75 | Left Arrow | 08/0128 | 34/0034 | --/0328 | 34/0034 |
76 | 5 | --/0127 | 35/0035 | --/0327 | 35/0035 |
77 | Right Arrow | 09/0126 | 36/0036 | --/0326 | 36/0036 |
78 | Grey Plus | 2B/002B | 2B/002B | --/033B | 2B/002B |
79 | END | --/0122 | 31/0031 | --/0322 | 31/0031 |
77 | Right Arrow | 09/0126 | 36/0036 | --/0326 | 36/0036 |
80 | Down Arrow | 0A/0127 | 32/0032 | --/0327 | 32/0032 |
81 | Pg Dn | --/0124 | 33/0033 | --/0324 | 33/0033 |
82 | Insert | --/012A | 30/0030 | --/032A | 30/0030 |
83 | Del | --/012B | 2E/002E | --/032B | 2E/002E |
Interfacing to a variety of output devices is again done via a translation table. For example, when a program places the code $0002 into D4 and then calls OCNTRL, the driver uses the translation table to convert the $0001 into whatever character (or sequence of characters) is needed to erase the screen on the current output device.
OCNTRL expects a 16-bit word as an argument and, depending on the exact I/O device, will recognize the following:
$0000 Return number of current driver (0-7) in D5
0001 Return name of current driver (e.g. CONS) in D5
0002 Erase screen
0003 Home cursor
0004 Cursor up
0006 Cursor right
0007 Bell (may also use $07 in data stream)
0008 Cursor left (may also use $08 in data stream)
0009 Horizontal tab
000A Line feed (may use $0A in data) / cursor down
000B Clear current line
000C Erase screen & home cursor (form feed on printer)
000D Carriage return (may also use $0D in data stream)
000E Mask output of character in D5
000F Permit output of character in D5
0010 Return size of text screen in (D5, D6)
0011 Move cursor to position (D5, D6)
0012 Return cursor position in (D5, D6)
0013 Return character under cursor in D5
0014 Clear to end of line
0015 Clear to end of screen
0016 Erase screen
0017 Home cursor
0018 Insert line before current line
0019 Delete current line and close up
0020 Switch to normal print / normal intensity
0021 Switch to condensed print
0022 Switch to expanded print
0023 Switch to double strike / bold / double intensity
0024 Switch to enhanced print mode
0025 Switch to italics
0026 Switch to character spacing in D5
0027 Switch to line spacing in D5
0040 Switch to graphics mode in D5
0041 Switch to text mode
0042 Choose color in D5
0043 Draw to position (D5, D6)
FFFx Switch to driver x
Additional other codes may be defined in the future.
Arguments $0000, 0001, and $FFFx (and 0002 for ICNTRL) are handled internally by SK*DOS; all other arguments are passed directly to the ICNTRL or OCNTRL section of the appropriate driver (except for the default console driver in the BIOS, which simply ignores the control codes.)
SERIAL and PARALLEL are simple drivers for a serial port using a 68681 DUART, and a parallel port using a 68230 PI/T or 6821 PIA, respectively. ADM-3A is an expanded serial driver which shows how ICNTRL and OCNTRL functions are implemented with translation tables.
NOTE: To preserve consistency between different implementations of SK*DOS, users writing device drivers which implement ICNTRL and/or OCNTRL are urged to adhere to the codes listed above. In order to correlate your work with that of others, please contact Star-K Software Systems Corp. before making any extensions to these codes.
If a typeahead buffer is implemented, then either the BIOS or the device driver, or both, may contain a buffer area which holds incoming characters from the keyboard or other device. This buffer, which is typically 64 bytes but may be larger or smaller, acts as a FIFO or first-in, first-out memory. As characters come in, they go into one end of the buffer; as SK*DOS or a user program needs them, they come out the other end of the buffer. Think of the FIFO as a pipe - characters go in one end and come out the other in the exact same order as they went in. In this way, characters that came in while the system is doing other tasks are held until they are needed. This allows you to type ahead of the computer, for example, giving it commands before they are needed.
When typeahead is implemented, normal input (via GETCH, INNOEC, or INLINE, for example) goes through the buffer. Hence normal programs need not even know whether such a buffer exists or not. Occasionally, however, it is useful to be able to bypass the buffer - when immediate response to an ESCape or control-C is needed, for example - or even to be able to empty the buffer (such as when asking "OK to delete - Y or N?", and you wish to make sure that a previously-entered Y does not get used as an answer.) This is where the three typeahead operations come in. Let us discuss them one by one.
When typeahead is implemented, GETCH and INNOEC input the first character in the buffer (i.e., the character that has been in the buffer the longest), whereas INNOE1 inputs the last character (the most recent one). INNOE1 should therefore be used if you are checking for control-C or ESCape, in which case you want to bypass any characters preceding it and act on it immediately. Note that using INNOE1 to get the last character does not remove it from the buffer - it is still there, and still in its correct sequence.
GETCH, INNOEC, and INNOE1 will all wait for a character to be typed if none exists when they are called. Hence we need a way of checking whether there is a character there. STATUS is therefore used to test whether there is any character in the typeahead buffer, whereas STATU1 tests whether a last character exists. Note: when there is a single character in the buffer, it becomes both the first and last character, and STATUS and STATU1 will both return a 'true' when tested. If the character is obtained with GETCH or INNOEC, then both STATUS and STATU1 will go 'false'. But if it is obtained with INNOE1, STATU1 will go 'false' but STATUS will still be 'true', indicating that it is still in the buffer as well.
Finally, FLUSHT is a function which empties the entire buffer so that both STATUS and STATU1 return 'false'. FLUSHT should always be used after INNOE1 returns a break or ESCape character, and may also be used when you want to make sure that some left-over character in the buffer does not provide an undesired answer (as when asking a "Y or N" question.)
The following example shows how these DOS calls are used inside UBASIC to check for a control-C or ESCape. Although checking for ESCape is normally done by SK*DOS internal routines, note that here UBASIC does its own checking. This is needed since it would otherwise remove the ESCape from the buffer before SK*DOS has a chance to check it.
Each time UBASIC outputs a character, or each time it finishes interpreting the current statement, it calls the following BREAK routine:
BREAK DC STATS1 Check 'last' char BEQ.S BREAK1 exit if not there DC INNOE1 else get last char BREAK0 CMP.B #3,D5 is it control-C? BEQ.S CNTRLC Yes, go process it CMP.B ESCAPC(A6),D5 is it ESC? BEQ.S ESCAP Yes, go process it BREAK1 RTS Neither, so exit CNTRLC DC FLUSHT on control-C, flush BRA.L READY and to READY prompt ESCAP DC FLUSHT On ESCape, flush ESCAP1 DC INNOEC Then get next char CMP.B ESCAPC(A6),D5 is it ESCape? BEQ.S BREAK1 Yes, RTS to continue CMP.B #$0D,D5 is it CR? BEQ.L READY Yes, go to READY BRA.S ESCAP1 No, wait for charNote how the typeahead buffer is flushed only after either the control-C or ESCape is identified; any other character is left in the buffer.
Floppy disks come in three sizes: the very old 8" (inch) size, the newer 5- 1/4" size, and the most recent 3-1/2" size.
The data on a disk is written in circular tracks. All 8" disks have 77 tracks, while all 3-1/2" disks have 80 tracks. Ancient 5-1/4" disks had 35 tracks, but modern ones have either 40 or 80 tracks.
In the 5-1/4" case, both kinds occupy slightly more than 3/4" of total width on the disk, but in one case there are 40 wide tracks, in the other there are 80 narrow tracks. By making the tracks narrower and closer together, the 80- track drive puts extra tracks into the position that would normally be between tracks on a 40-track drive. Hence an 80-track drive can read a 40-track disk if it "double-steps"; that is, if it skips over the in-between areas between tracks. The 80-track drive can also write a 40-track disk, but there is a catch - a 40-track drive may not be able to read it! The reason is that the 80-track drive writes a narrower track, which leaves some unrecorded space on each side. Depending on the exact width and alignment of the track, the 40-track drive may read so much of the "garbage" between tracks that it makes too many errors to be reliable. The only way to tell is to try.
Disks also come in three densities: SD or single-density, which is now quite obsolete; DD or double-density, which is most common; and HD or high-density, which has several variations and is the newest. (What is most confusing is that some very old SK*DOS systems use single-density on their outermost track, but DD elsewhere.)
Finally, all modern floppy disks are DS or double-sided; only the very oldest drives are still SS or single-sided. With all of these abbreviations, it is easy to see why we need this short introduction to the topic.
One more complication: the tracks are divided up into smaller sections called sectors. In SK*DOS, each sector has 256 bytes, although other systems use 128, 512, or even more bytes per sector. The number of sectors that can be crammed into a track depends on how fast the disk turns, and on how closely the bits are crammed together in time. Most disks turn at 300 rpm (which is 5 revolutions per second), while 8" and some high density disks turn at 360 rpm (or 6 revolutions per second).
Now on to specifics. Most SK*DOS systems use a version of the Western Digital 17xx or 27xx FDC (floppy disk controller) chip. In particular, both the PT68K- 2 and PT68K-4 use the WD1772 controller, which supports SD or DD disks (but not HD or high density) disks, SS or DS, and either 3-1/2" or 5-1/2" disks. These controllers could also support 8" disks, but are usually not wired up to do so.
This controller can therefore typically handle the following situations;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The 1772 controller can read and write all of the above formats, but cannot format the double-stepping formats labelled with * above.
The PT68K-4, however, also has a Western Digital WD37C65 controller, which can also handle several types of high density drives of the type commonly used on PC clones. It can also easily handle an 8" drive.
The high density drives come in two types. One, commonly called a 1.2 meg HD drive, uses 5-1/4" disks, but rotates at 360 rpm instead of 300 rpm. By using 80 tracks and a data rate the same as 8" disks, it squeezes almost 1.2 megabytes of data on the disk. The second, usually called a 1.44 meg disk, uses 3-1/2" disks, but rotates at 300 rpm and uses the same data rate as the 1.2 meg drive. Because it rotates slower, it has a capacity 20% higher than the 1.2 meg drive.
Both of these high density drives can read and write the lower density
disks as well, but SK*DOS does not implement single density for them. Hence
the WD37C65 controller can handle the following combinations:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Since the 1772 controller can only handle two kinds of drives (which really only differ by the number of tracks), the software and hardware can fairly well differentiate between the different kinds of disks without your having to tell it. The only place where you must specify what kind of disk you want is during formatting, when you must answer several questions as to the number of sides and tracks, and the density.
The 37C65 controller, on the other hand, can handle five different kinds of drives, and twenty different kinds of disks. It can generally figure out what kinds of disks you are using, as long as you tell it what kind of drives you have. You must do this in HUMBUG before you boot by using HUMBUG's IS command to do an Initial Setup.
DRIVE L0=F1tells SK*DOS that logical drive 0 should be physical floppy drive 1.
But there is a problem here - changing a drive select number requires that you move these tiny jumpers on the drive. Some manufacturers (such as IBM and Tandy) don't want their customers messing with these jumpers, and instead do the drive selection in the drive cable. Tandy used to do it by removing pins from the drive connector; IBM does it by twisting part of the cable. In IBM's case, both floppy drives are jumpered as DS1, but the cable makes one into DS0 and the other into DS1. Because IBM-style cables are so cheap, that is the approach we use with the WD37C65 controller.
As long as a computer had only one floppy controller, then the F number was the same as the drive select. Thus floppy drive F0 was DS0 on its controller. But now that we can have two controllers (as the PT68K-4 has both a WD1772 and a WD37C65) we can have two DS0 drives, one on each controller. It is HUMBUG's Initial Setup (IS) command which tells HUMBUG and SK*DOS which physical drive number corresponds to which drive select.
To properly set up a system, therefore, requires that you use the IS command in HUMBUG to tell the system which controller and drive select goes with which F number, and then use the DRIVE command in SK*DOS to tell it which F number goes with which logical drive number.
When you do the IS command, HUMBUG will ask you which controller you want to boot from; that controller will then have F0. Since the 1772 can have up to four drives, selecting it for booting will give it F0 through F3; the 37C65 would then get F4 and F5. On the other hand, if you select the 37C65 for booting, it will get F0 and F1, while the 1772 will get F2 through F5. The system will always boot from F0 when you give the FD command, so there must always be an F0 drive. Other than that, you can skip drives, so there could be an F0 and an F5 perhaps.
Any time you want to know what the relationship is between F numbers and drive select numbers, you can find out by using HUMBUG's IS command, or by using DRIVE or IOSTAT in SK*DOS.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If you connect this adapter directly to the controller, the 8" drive will be drive select 1 on the controller; if you connect it to the twisted end of a normal floppy connector, then it will be drive select 0 on the controller.
Appendices
Click here to continue with the Appendices of this SK*DOS User's Manual.